]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/Dhcp4Dxe/Dhcp4Io.c
NetworkPkg: Apply uncrustify changes
[mirror_edk2.git] / NetworkPkg / Dhcp4Dxe / Dhcp4Io.c
1 /** @file
2 EFI DHCP protocol implementation.
3
4 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "Dhcp4Impl.h"
10
11 UINT32 mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 };
12
13 /**
14 Send an initial DISCOVER or REQUEST message according to the
15 DHCP service's current state.
16
17 @param[in] DhcpSb The DHCP service instance
18
19 @retval EFI_SUCCESS The request has been sent
20 @retval other Some error occurs when sending the request.
21
22 **/
23 EFI_STATUS
24 DhcpInitRequest (
25 IN DHCP_SERVICE *DhcpSb
26 )
27 {
28 EFI_STATUS Status;
29
30 ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot));
31
32 //
33 // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message.
34 //
35 DhcpSb->ActiveChild->ElaspedTime = 0;
36
37 if (DhcpSb->DhcpState == Dhcp4Init) {
38 DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE);
39 Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL);
40
41 if (EFI_ERROR (Status)) {
42 DhcpSb->DhcpState = Dhcp4Init;
43 return Status;
44 }
45 } else {
46 DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE);
47 Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL);
48
49 if (EFI_ERROR (Status)) {
50 DhcpSb->DhcpState = Dhcp4InitReboot;
51 return Status;
52 }
53 }
54
55 return EFI_SUCCESS;
56 }
57
58 /**
59 Call user provided callback function, and return the value the
60 function returns. If the user doesn't provide a callback, a
61 proper return value is selected to let the caller continue the
62 normal process.
63
64 @param[in] DhcpSb The DHCP service instance
65 @param[in] Event The event as defined in the spec
66 @param[in] Packet The current packet trigger the event
67 @param[out] NewPacket The user's return new packet
68
69 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
70 @retval EFI_SUCCESS The user function returns success.
71 @retval EFI_ABORTED The user function ask it to abort.
72
73 **/
74 EFI_STATUS
75 DhcpCallUser (
76 IN DHCP_SERVICE *DhcpSb,
77 IN EFI_DHCP4_EVENT Event,
78 IN EFI_DHCP4_PACKET *Packet OPTIONAL,
79 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
80 )
81 {
82 EFI_DHCP4_CONFIG_DATA *Config;
83 EFI_STATUS Status;
84
85 if (NewPacket != NULL) {
86 *NewPacket = NULL;
87 }
88
89 //
90 // If user doesn't provide the call back function, return the value
91 // that directs the client to continue the normal process.
92 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
93 // the offers and select a offer, EFI_NOT_READY tells the client to
94 // collect more offers.
95 //
96 Config = &DhcpSb->ActiveConfig;
97
98 if (Config->Dhcp4Callback == NULL) {
99 if (Event == Dhcp4RcvdOffer) {
100 return EFI_NOT_READY;
101 }
102
103 return EFI_SUCCESS;
104 }
105
106 Status = Config->Dhcp4Callback (
107 &DhcpSb->ActiveChild->Dhcp4Protocol,
108 Config->CallbackContext,
109 (EFI_DHCP4_STATE)DhcpSb->DhcpState,
110 Event,
111 Packet,
112 NewPacket
113 );
114
115 //
116 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
117 // and EFI_ABORTED. If it returns values other than those, assume
118 // it to be EFI_ABORTED.
119 //
120 if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) {
121 return Status;
122 }
123
124 return EFI_ABORTED;
125 }
126
127 /**
128 Notify the user about the operation result.
129
130 @param DhcpSb DHCP service instance
131 @param Which Which notify function to signal
132
133 **/
134 VOID
135 DhcpNotifyUser (
136 IN DHCP_SERVICE *DhcpSb,
137 IN INTN Which
138 )
139 {
140 DHCP_PROTOCOL *Child;
141
142 if ((Child = DhcpSb->ActiveChild) == NULL) {
143 return;
144 }
145
146 if ((Child->CompletionEvent != NULL) &&
147 ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))
148 )
149 {
150 gBS->SignalEvent (Child->CompletionEvent);
151 Child->CompletionEvent = NULL;
152 }
153
154 if ((Child->RenewRebindEvent != NULL) &&
155 ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))
156 )
157 {
158 gBS->SignalEvent (Child->RenewRebindEvent);
159 Child->RenewRebindEvent = NULL;
160 }
161 }
162
163 /**
164 Set the DHCP state. If CallUser is true, it will try to notify
165 the user before change the state by DhcpNotifyUser. It returns
166 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
167 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
168 the return value of this function.
169
170 @param DhcpSb The DHCP service instance
171 @param State The new DHCP state to change to
172 @param CallUser Whether we need to call user
173
174 @retval EFI_SUCCESS The state is changed
175 @retval EFI_ABORTED The user asks to abort the DHCP process.
176
177 **/
178 EFI_STATUS
179 DhcpSetState (
180 IN OUT DHCP_SERVICE *DhcpSb,
181 IN INTN State,
182 IN BOOLEAN CallUser
183 )
184 {
185 EFI_STATUS Status;
186
187 if (CallUser) {
188 Status = EFI_SUCCESS;
189
190 if (State == Dhcp4Renewing) {
191 Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL);
192 } else if (State == Dhcp4Rebinding) {
193 Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL);
194 } else if (State == Dhcp4Bound) {
195 Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL);
196 }
197
198 if (EFI_ERROR (Status)) {
199 return Status;
200 }
201 }
202
203 //
204 // Update the retransmission timer during the state transition.
205 // This will clear the retry count. This is also why the rule
206 // first transit the state, then send packets.
207 //
208 if (State == Dhcp4Selecting) {
209 DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount;
210 } else {
211 DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount;
212 }
213
214 if (DhcpSb->MaxRetries == 0) {
215 DhcpSb->MaxRetries = 4;
216 }
217
218 DhcpSb->CurRetry = 0;
219 DhcpSb->PacketToLive = 0;
220 DhcpSb->LastTimeout = 0;
221 DhcpSb->DhcpState = State;
222 return EFI_SUCCESS;
223 }
224
225 /**
226 Set the retransmit timer for the packet. It will select from either
227 the discover timeouts/request timeouts or the default timeout values.
228
229 @param DhcpSb The DHCP service instance.
230
231 **/
232 VOID
233 DhcpSetTransmitTimer (
234 IN OUT DHCP_SERVICE *DhcpSb
235 )
236 {
237 UINT32 *Times;
238
239 ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry);
240
241 if (DhcpSb->DhcpState == Dhcp4Selecting) {
242 Times = DhcpSb->ActiveConfig.DiscoverTimeout;
243 } else {
244 Times = DhcpSb->ActiveConfig.RequestTimeout;
245 }
246
247 if (Times == NULL) {
248 Times = mDhcp4DefaultTimeout;
249 }
250
251 DhcpSb->PacketToLive = Times[DhcpSb->CurRetry];
252 DhcpSb->LastTimeout = DhcpSb->PacketToLive;
253
254 return;
255 }
256
257 /**
258 Compute the lease. If the server grants a permanent lease, just
259 process it as a normal timeout value since the lease will last
260 more than 100 years.
261
262 @param DhcpSb The DHCP service instance
263 @param Para The DHCP parameter extracted from the server's
264 response.
265 **/
266 VOID
267 DhcpComputeLease (
268 IN OUT DHCP_SERVICE *DhcpSb,
269 IN DHCP_PARAMETER *Para
270 )
271 {
272 ASSERT (Para != NULL);
273
274 DhcpSb->Lease = Para->Lease;
275 DhcpSb->T2 = Para->T2;
276 DhcpSb->T1 = Para->T1;
277
278 if (DhcpSb->Lease == 0) {
279 DhcpSb->Lease = DHCP_DEFAULT_LEASE;
280 }
281
282 if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) {
283 DhcpSb->T2 = Para->Lease - (Para->Lease >> 3);
284 }
285
286 if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) {
287 DhcpSb->T1 = DhcpSb->Lease >> 1;
288 }
289 }
290
291 /**
292 Configure a UDP IO port to use the acquired lease address.
293 DHCP driver needs this port to unicast packet to the server
294 such as DHCP release.
295
296 @param[in] UdpIo The UDP IO to configure
297 @param[in] Context Dhcp service instance.
298
299 @retval EFI_SUCCESS The UDP IO port is successfully configured.
300 @retval Others It failed to configure the port.
301
302 **/
303 EFI_STATUS
304 EFIAPI
305 DhcpConfigLeaseIoPort (
306 IN UDP_IO *UdpIo,
307 IN VOID *Context
308 )
309 {
310 EFI_UDP4_CONFIG_DATA UdpConfigData;
311 EFI_IPv4_ADDRESS Subnet;
312 EFI_IPv4_ADDRESS Gateway;
313 DHCP_SERVICE *DhcpSb;
314 EFI_STATUS Status;
315 IP4_ADDR Ip;
316
317 DhcpSb = (DHCP_SERVICE *)Context;
318
319 UdpConfigData.AcceptBroadcast = FALSE;
320 UdpConfigData.AcceptPromiscuous = FALSE;
321 UdpConfigData.AcceptAnyPort = FALSE;
322 UdpConfigData.AllowDuplicatePort = TRUE;
323 UdpConfigData.TypeOfService = 0;
324 UdpConfigData.TimeToLive = 64;
325 UdpConfigData.DoNotFragment = FALSE;
326 UdpConfigData.ReceiveTimeout = 1;
327 UdpConfigData.TransmitTimeout = 0;
328
329 UdpConfigData.UseDefaultAddress = FALSE;
330 UdpConfigData.StationPort = DHCP_CLIENT_PORT;
331 UdpConfigData.RemotePort = DHCP_SERVER_PORT;
332
333 Ip = HTONL (DhcpSb->ClientAddr);
334 CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
335
336 Ip = HTONL (DhcpSb->Netmask);
337 CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
338
339 ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
340
341 Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
342
343 if (EFI_ERROR (Status)) {
344 return Status;
345 }
346
347 //
348 // Add a default route if received from the server.
349 //
350 if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) {
351 ZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));
352
353 Ip = HTONL (DhcpSb->Para->Router);
354 CopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
355
356 UdpIo->Protocol.Udp4->Routes (UdpIo->Protocol.Udp4, FALSE, &Subnet, &Subnet, &Gateway);
357 }
358
359 return EFI_SUCCESS;
360 }
361
362 /**
363 Update the lease states when a new lease is acquired. It will not only
364 save the acquired the address and lease time, it will also create a UDP
365 child to provide address resolution for the address.
366
367 @param DhcpSb The DHCP service instance
368
369 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
370 @retval EFI_SUCCESS The lease is recorded.
371
372 **/
373 EFI_STATUS
374 DhcpLeaseAcquired (
375 IN OUT DHCP_SERVICE *DhcpSb
376 )
377 {
378 DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);
379
380 if (DhcpSb->Para != NULL) {
381 DhcpSb->Netmask = DhcpSb->Para->NetMask;
382 DhcpSb->ServerAddr = DhcpSb->Para->ServerId;
383 }
384
385 if (DhcpSb->Netmask == 0) {
386 return EFI_ABORTED;
387 }
388
389 if (DhcpSb->LeaseIoPort != NULL) {
390 UdpIoFreeIo (DhcpSb->LeaseIoPort);
391 }
392
393 //
394 // Create a UDP/IP child to provide ARP service for the Leased IP,
395 // and transmit unicast packet with it as source address. Don't
396 // start receive on this port, the queued packet will be timeout.
397 //
398 DhcpSb->LeaseIoPort = UdpIoCreateIo (
399 DhcpSb->Controller,
400 DhcpSb->Image,
401 DhcpConfigLeaseIoPort,
402 UDP_IO_UDP4_VERSION,
403 DhcpSb
404 );
405
406 if (DhcpSb->LeaseIoPort == NULL) {
407 return EFI_OUT_OF_RESOURCES;
408 }
409
410 if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
411 DhcpComputeLease (DhcpSb, DhcpSb->Para);
412 }
413
414 return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
415 }
416
417 /**
418 Clean up the DHCP related states, IoStatus isn't reset.
419
420 @param DhcpSb The DHCP instance service.
421
422 **/
423 VOID
424 DhcpCleanLease (
425 IN DHCP_SERVICE *DhcpSb
426 )
427 {
428 DhcpSb->DhcpState = Dhcp4Init;
429 DhcpSb->Xid = DhcpSb->Xid + 1;
430 DhcpSb->ClientAddr = 0;
431 DhcpSb->Netmask = 0;
432 DhcpSb->ServerAddr = 0;
433
434 if (DhcpSb->LastOffer != NULL) {
435 FreePool (DhcpSb->LastOffer);
436 DhcpSb->LastOffer = NULL;
437 }
438
439 if (DhcpSb->Selected != NULL) {
440 FreePool (DhcpSb->Selected);
441 DhcpSb->Selected = NULL;
442 }
443
444 if (DhcpSb->Para != NULL) {
445 FreePool (DhcpSb->Para);
446 DhcpSb->Para = NULL;
447 }
448
449 DhcpSb->Lease = 0;
450 DhcpSb->T1 = 0;
451 DhcpSb->T2 = 0;
452 DhcpSb->ExtraRefresh = FALSE;
453
454 if (DhcpSb->LeaseIoPort != NULL) {
455 UdpIoFreeIo (DhcpSb->LeaseIoPort);
456 DhcpSb->LeaseIoPort = NULL;
457 }
458
459 if (DhcpSb->LastPacket != NULL) {
460 FreePool (DhcpSb->LastPacket);
461 DhcpSb->LastPacket = NULL;
462 }
463
464 DhcpSb->PacketToLive = 0;
465 DhcpSb->LastTimeout = 0;
466 DhcpSb->CurRetry = 0;
467 DhcpSb->MaxRetries = 0;
468 DhcpSb->LeaseLife = 0;
469
470 //
471 // Clean active config data.
472 //
473 DhcpCleanConfigure (&DhcpSb->ActiveConfig);
474 }
475
476 /**
477 Select a offer among all the offers collected. If the offer selected is
478 of BOOTP, the lease is recorded and user notified. If the offer is of
479 DHCP, it will request the offer from the server.
480
481 @param[in] DhcpSb The DHCP service instance.
482
483 @retval EFI_SUCCESS One of the offer is selected.
484
485 **/
486 EFI_STATUS
487 DhcpChooseOffer (
488 IN DHCP_SERVICE *DhcpSb
489 )
490 {
491 EFI_DHCP4_PACKET *Selected;
492 EFI_DHCP4_PACKET *NewPacket;
493 EFI_DHCP4_PACKET *TempPacket;
494 EFI_STATUS Status;
495
496 ASSERT (DhcpSb->LastOffer != NULL);
497
498 //
499 // User will cache previous offers if he wants to select
500 // from multiple offers. If user provides an invalid packet,
501 // use the last offer, otherwise use the provided packet.
502 //
503 NewPacket = NULL;
504 Status = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);
505
506 if (EFI_ERROR (Status)) {
507 return Status;
508 }
509
510 Selected = DhcpSb->LastOffer;
511
512 if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {
513 TempPacket = (EFI_DHCP4_PACKET *)AllocatePool (NewPacket->Size);
514 if (TempPacket != NULL) {
515 CopyMem (TempPacket, NewPacket, NewPacket->Size);
516 FreePool (Selected);
517 Selected = TempPacket;
518 }
519 }
520
521 DhcpSb->Selected = Selected;
522 DhcpSb->LastOffer = NULL;
523 DhcpSb->Para = NULL;
524 DhcpValidateOptions (Selected, &DhcpSb->Para);
525
526 //
527 // A bootp offer has been selected, save the lease status,
528 // enter bound state then notify the user.
529 //
530 if (DHCP_IS_BOOTP (DhcpSb->Para)) {
531 Status = DhcpLeaseAcquired (DhcpSb);
532
533 if (EFI_ERROR (Status)) {
534 return Status;
535 }
536
537 DhcpSb->IoStatus = EFI_SUCCESS;
538 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
539 return EFI_SUCCESS;
540 }
541
542 //
543 // Send a DHCP requests
544 //
545 Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);
546
547 if (EFI_ERROR (Status)) {
548 return Status;
549 }
550
551 return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);
552 }
553
554 /**
555 Terminate the current address acquire. All the allocated resources
556 are released. Be careful when calling this function. A rule related
557 to this is: only call DhcpEndSession at the highest level, such as
558 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
559
560 @param[in] DhcpSb The DHCP service instance
561 @param[in] Status The result of the DHCP process.
562
563 **/
564 VOID
565 DhcpEndSession (
566 IN DHCP_SERVICE *DhcpSb,
567 IN EFI_STATUS Status
568 )
569 {
570 if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
571 DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);
572 } else {
573 DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);
574 }
575
576 DhcpCleanLease (DhcpSb);
577
578 DhcpSb->IoStatus = Status;
579 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
580 }
581
582 /**
583 Handle packets in DHCP select state.
584
585 @param[in] DhcpSb The DHCP service instance
586 @param[in] Packet The DHCP packet received
587 @param[in] Para The DHCP parameter extracted from the packet. That
588 is, all the option value that we care.
589
590 @retval EFI_SUCCESS The packet is successfully processed.
591 @retval Others Some error occurred.
592
593 **/
594 EFI_STATUS
595 DhcpHandleSelect (
596 IN DHCP_SERVICE *DhcpSb,
597 IN EFI_DHCP4_PACKET *Packet,
598 IN DHCP_PARAMETER *Para
599 )
600 {
601 EFI_STATUS Status;
602
603 Status = EFI_SUCCESS;
604
605 //
606 // First validate the message:
607 // 1. the offer is a unicast
608 // 2. if it is a DHCP message, it must contains a server ID.
609 // Don't return a error for these two case otherwise the session is ended.
610 //
611 if (!DHCP_IS_BOOTP (Para) &&
612 ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))
613 )
614 {
615 goto ON_EXIT;
616 }
617
618 //
619 // Call the user's callback. The action according to the return is as:
620 // 1. EFI_SUCCESS: stop waiting for more offers, select the offer now
621 // 2. EFI_NOT_READY: wait for more offers
622 // 3. EFI_ABORTED: abort the address acquiring.
623 //
624 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
625
626 if (Status == EFI_SUCCESS) {
627 if (DhcpSb->LastOffer != NULL) {
628 FreePool (DhcpSb->LastOffer);
629 }
630
631 DhcpSb->LastOffer = Packet;
632
633 return DhcpChooseOffer (DhcpSb);
634 } else if (Status == EFI_NOT_READY) {
635 if (DhcpSb->LastOffer != NULL) {
636 FreePool (DhcpSb->LastOffer);
637 }
638
639 DhcpSb->LastOffer = Packet;
640 } else if (Status == EFI_ABORTED) {
641 //
642 // DhcpInput will end the session upon error return. Remember
643 // only to call DhcpEndSession at the top level call.
644 //
645 goto ON_EXIT;
646 }
647
648 return EFI_SUCCESS;
649
650 ON_EXIT:
651 FreePool (Packet);
652 return Status;
653 }
654
655 /**
656 Handle packets in DHCP request state.
657
658 @param[in] DhcpSb The DHCP service instance
659 @param[in] Packet The DHCP packet received
660 @param[in] Para The DHCP parameter extracted from the packet. That
661 is, all the option value that we care.
662
663 @retval EFI_SUCCESS The packet is successfully processed.
664 @retval Others Some error occurred.
665
666 **/
667 EFI_STATUS
668 DhcpHandleRequest (
669 IN DHCP_SERVICE *DhcpSb,
670 IN EFI_DHCP4_PACKET *Packet,
671 IN DHCP_PARAMETER *Para
672 )
673 {
674 EFI_DHCP4_HEADER *Head;
675 EFI_DHCP4_HEADER *Selected;
676 EFI_STATUS Status;
677 UINT8 *Message;
678
679 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
680
681 Head = &Packet->Dhcp4.Header;
682 Selected = &DhcpSb->Selected->Dhcp4.Header;
683
684 //
685 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
686 //
687 if (DHCP_IS_BOOTP (Para) ||
688 (Para->ServerId != DhcpSb->Para->ServerId) ||
689 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
690 )
691 {
692 Status = EFI_SUCCESS;
693 goto ON_EXIT;
694 }
695
696 //
697 // Received a NAK, end the session no matter what the user returns
698 //
699 Status = EFI_DEVICE_ERROR;
700
701 if (Para->DhcpType == DHCP_MSG_NAK) {
702 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
703 goto ON_EXIT;
704 }
705
706 //
707 // Check whether the ACK matches the selected offer
708 //
709 Message = NULL;
710
711 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
712 Message = (UINT8 *)"Lease confirmed isn't the same as that in the offer";
713 goto REJECT;
714 }
715
716 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
717
718 if (EFI_ERROR (Status)) {
719 Message = (UINT8 *)"Lease is denied upon received ACK";
720 goto REJECT;
721 }
722
723 //
724 // Record the lease, transit to BOUND state, then notify the user
725 //
726 Status = DhcpLeaseAcquired (DhcpSb);
727
728 if (EFI_ERROR (Status)) {
729 Message = (UINT8 *)"Lease is denied upon entering bound";
730 goto REJECT;
731 }
732
733 DhcpSb->IoStatus = EFI_SUCCESS;
734 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
735
736 FreePool (Packet);
737 return EFI_SUCCESS;
738
739 REJECT:
740 DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
741
742 ON_EXIT:
743 FreePool (Packet);
744 return Status;
745 }
746
747 /**
748 Handle packets in DHCP renew/rebound state.
749
750 @param[in] DhcpSb The DHCP service instance
751 @param[in] Packet The DHCP packet received
752 @param[in] Para The DHCP parameter extracted from the packet. That
753 is, all the option value that we care.
754
755 @retval EFI_SUCCESS The packet is successfully processed.
756 @retval Others Some error occurred.
757
758 **/
759 EFI_STATUS
760 DhcpHandleRenewRebind (
761 IN DHCP_SERVICE *DhcpSb,
762 IN EFI_DHCP4_PACKET *Packet,
763 IN DHCP_PARAMETER *Para
764 )
765 {
766 EFI_DHCP4_HEADER *Head;
767 EFI_DHCP4_HEADER *Selected;
768 EFI_STATUS Status;
769
770 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
771
772 Head = &Packet->Dhcp4.Header;
773 Selected = &DhcpSb->Selected->Dhcp4.Header;
774
775 //
776 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
777 //
778 if (DHCP_IS_BOOTP (Para) ||
779 (Para->ServerId != DhcpSb->Para->ServerId) ||
780 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
781 )
782 {
783 Status = EFI_SUCCESS;
784 goto ON_EXIT;
785 }
786
787 //
788 // Received a NAK, ignore the user's return then terminate the process
789 //
790 Status = EFI_DEVICE_ERROR;
791
792 if (Para->DhcpType == DHCP_MSG_NAK) {
793 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
794 goto ON_EXIT;
795 }
796
797 //
798 // The lease is different from the selected. Don't send a DECLINE
799 // since it isn't existed in the client's FSM.
800 //
801 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
802 goto ON_EXIT;
803 }
804
805 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
806
807 if (EFI_ERROR (Status)) {
808 goto ON_EXIT;
809 }
810
811 //
812 // Record the lease, start timer for T1 and T2,
813 //
814 DhcpComputeLease (DhcpSb, Para);
815 DhcpSb->LeaseLife = 0;
816 DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
817
818 if (DhcpSb->ExtraRefresh != 0) {
819 DhcpSb->ExtraRefresh = FALSE;
820
821 DhcpSb->IoStatus = EFI_SUCCESS;
822 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
823 }
824
825 ON_EXIT:
826 FreePool (Packet);
827 return Status;
828 }
829
830 /**
831 Handle packets in DHCP reboot state.
832
833 @param[in] DhcpSb The DHCP service instance
834 @param[in] Packet The DHCP packet received
835 @param[in] Para The DHCP parameter extracted from the packet. That
836 is, all the option value that we care.
837
838 @retval EFI_SUCCESS The packet is successfully processed.
839 @retval Others Some error occurred.
840
841 **/
842 EFI_STATUS
843 DhcpHandleReboot (
844 IN DHCP_SERVICE *DhcpSb,
845 IN EFI_DHCP4_PACKET *Packet,
846 IN DHCP_PARAMETER *Para
847 )
848 {
849 EFI_DHCP4_HEADER *Head;
850 EFI_STATUS Status;
851
852 Head = &Packet->Dhcp4.Header;
853
854 //
855 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
856 //
857 if (DHCP_IS_BOOTP (Para) ||
858 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
859 )
860 {
861 Status = EFI_SUCCESS;
862 goto ON_EXIT;
863 }
864
865 //
866 // If a NAK is received, transit to INIT and try again.
867 //
868 if (Para->DhcpType == DHCP_MSG_NAK) {
869 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
870
871 DhcpSb->ClientAddr = 0;
872 DhcpSb->DhcpState = Dhcp4Init;
873
874 Status = DhcpInitRequest (DhcpSb);
875 goto ON_EXIT;
876 }
877
878 //
879 // Check whether the ACK matches the selected offer
880 //
881 if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
882 Status = EFI_DEVICE_ERROR;
883 goto ON_EXIT;
884 }
885
886 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
887 if (EFI_ERROR (Status)) {
888 goto ON_EXIT;
889 }
890
891 //
892 // OK, get the parameter from server, record the lease
893 //
894 DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);
895 if (DhcpSb->Para == NULL) {
896 Status = EFI_OUT_OF_RESOURCES;
897 goto ON_EXIT;
898 }
899
900 DhcpSb->Selected = Packet;
901 Status = DhcpLeaseAcquired (DhcpSb);
902 if (EFI_ERROR (Status)) {
903 return Status;
904 }
905
906 DhcpSb->IoStatus = EFI_SUCCESS;
907 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
908 return EFI_SUCCESS;
909
910 ON_EXIT:
911 FreePool (Packet);
912 return Status;
913 }
914
915 /**
916 Handle the received DHCP packets. This function drives the DHCP
917 state machine.
918
919 @param UdpPacket The UDP packets received.
920 @param EndPoint The local/remote UDP access point
921 @param IoStatus The status of the UDP receive
922 @param Context The opaque parameter to the function.
923
924 **/
925 VOID
926 EFIAPI
927 DhcpInput (
928 NET_BUF *UdpPacket,
929 UDP_END_POINT *EndPoint,
930 EFI_STATUS IoStatus,
931 VOID *Context
932 )
933 {
934 DHCP_SERVICE *DhcpSb;
935 EFI_DHCP4_HEADER *Head;
936 EFI_DHCP4_PACKET *Packet;
937 DHCP_PARAMETER *Para;
938 EFI_STATUS Status;
939 UINT32 Len;
940
941 Packet = NULL;
942 DhcpSb = (DHCP_SERVICE *)Context;
943
944 //
945 // Don't restart receive if error occurs or DHCP is destroyed.
946 //
947 if (EFI_ERROR (IoStatus)) {
948 return;
949 } else if (DhcpSb->ServiceState == DHCP_DESTROY) {
950 NetbufFree (UdpPacket);
951 return;
952 }
953
954 ASSERT (UdpPacket != NULL);
955
956 if (DhcpSb->DhcpState == Dhcp4Stopped) {
957 goto RESTART;
958 }
959
960 //
961 // Validate the packet received
962 //
963 if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
964 goto RESTART;
965 }
966
967 //
968 // Copy the DHCP message to a continuous memory block
969 //
970 Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
971 Packet = (EFI_DHCP4_PACKET *)AllocatePool (Len);
972
973 if (Packet == NULL) {
974 goto RESTART;
975 }
976
977 Packet->Size = Len;
978 Head = &Packet->Dhcp4.Header;
979 Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *)Head);
980
981 if (Packet->Length != UdpPacket->TotalSize) {
982 goto RESTART;
983 }
984
985 //
986 // Is this packet the answer to our packet?
987 //
988 if ((Head->OpCode != BOOTP_REPLY) ||
989 (NTOHL (Head->Xid) != DhcpSb->Xid) ||
990 (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0))
991 {
992 goto RESTART;
993 }
994
995 //
996 // Validate the options and retrieve the interested options
997 //
998 Para = NULL;
999 if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
1000 (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
1001 EFI_ERROR (DhcpValidateOptions (Packet, &Para)))
1002 {
1003 goto RESTART;
1004 }
1005
1006 //
1007 // Call the handler for each state. The handler should return
1008 // EFI_SUCCESS if the process can go on no matter whether the
1009 // packet is ignored or not. If the return is EFI_ERROR, the
1010 // session will be terminated. Packet's ownership is handled
1011 // over to the handlers. If operation succeeds, the handler
1012 // must notify the user. It isn't necessary to do if EFI_ERROR
1013 // is returned because the DhcpEndSession will notify the user.
1014 //
1015 Status = EFI_SUCCESS;
1016
1017 switch (DhcpSb->DhcpState) {
1018 case Dhcp4Selecting:
1019 Status = DhcpHandleSelect (DhcpSb, Packet, Para);
1020 break;
1021
1022 case Dhcp4Requesting:
1023 Status = DhcpHandleRequest (DhcpSb, Packet, Para);
1024 break;
1025
1026 case Dhcp4InitReboot:
1027 case Dhcp4Init:
1028 case Dhcp4Bound:
1029 //
1030 // Ignore the packet in INITREBOOT, INIT and BOUND states
1031 //
1032 FreePool (Packet);
1033 Status = EFI_SUCCESS;
1034 break;
1035
1036 case Dhcp4Renewing:
1037 case Dhcp4Rebinding:
1038 Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
1039 break;
1040
1041 case Dhcp4Rebooting:
1042 Status = DhcpHandleReboot (DhcpSb, Packet, Para);
1043 break;
1044 }
1045
1046 if (Para != NULL) {
1047 FreePool (Para);
1048 }
1049
1050 Packet = NULL;
1051
1052 if (EFI_ERROR (Status)) {
1053 NetbufFree (UdpPacket);
1054 UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
1055 DhcpEndSession (DhcpSb, Status);
1056 return;
1057 }
1058
1059 RESTART:
1060 NetbufFree (UdpPacket);
1061
1062 if (Packet != NULL) {
1063 FreePool (Packet);
1064 }
1065
1066 Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
1067
1068 if (EFI_ERROR (Status)) {
1069 DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);
1070 }
1071 }
1072
1073 /**
1074 Release the net buffer when packet is sent.
1075
1076 @param UdpPacket The UDP packets received.
1077 @param EndPoint The local/remote UDP access point
1078 @param IoStatus The status of the UDP receive
1079 @param Context The opaque parameter to the function.
1080
1081 **/
1082 VOID
1083 EFIAPI
1084 DhcpOnPacketSent (
1085 NET_BUF *Packet,
1086 UDP_END_POINT *EndPoint,
1087 EFI_STATUS IoStatus,
1088 VOID *Context
1089 )
1090 {
1091 NetbufFree (Packet);
1092 }
1093
1094 /**
1095 Build and transmit a DHCP message according to the current states.
1096 This function implement the Table 5. of RFC 2131. Always transits
1097 the state (as defined in Figure 5. of the same RFC) before sending
1098 a DHCP message. The table is adjusted accordingly.
1099
1100 @param[in] DhcpSb The DHCP service instance
1101 @param[in] Seed The seed packet which the new packet is based on
1102 @param[in] Para The DHCP parameter of the Seed packet
1103 @param[in] Type The message type to send
1104 @param[in] Msg The human readable message to include in the packet
1105 sent.
1106
1107 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1108 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1109 @retval EFI_SUCCESS The message is sent
1110 @retval other Other error occurs
1111
1112 **/
1113 EFI_STATUS
1114 DhcpSendMessage (
1115 IN DHCP_SERVICE *DhcpSb,
1116 IN EFI_DHCP4_PACKET *Seed,
1117 IN DHCP_PARAMETER *Para,
1118 IN UINT8 Type,
1119 IN UINT8 *Msg
1120 )
1121 {
1122 EFI_DHCP4_CONFIG_DATA *Config;
1123 EFI_DHCP4_PACKET *Packet;
1124 EFI_DHCP4_PACKET *NewPacket;
1125 EFI_DHCP4_HEADER *Head;
1126 EFI_DHCP4_HEADER *SeedHead;
1127 UDP_IO *UdpIo;
1128 UDP_END_POINT EndPoint;
1129 NET_BUF *Wrap;
1130 NET_FRAGMENT Frag;
1131 EFI_STATUS Status;
1132 IP4_ADDR IpAddr;
1133 UINT8 *Buf;
1134 UINT16 MaxMsg;
1135 UINT32 Len;
1136 UINT32 Index;
1137
1138 //
1139 // Allocate a big enough memory block to hold the DHCP packet
1140 //
1141 Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;
1142
1143 if (Msg != NULL) {
1144 Len += (UINT32)AsciiStrLen ((CHAR8 *)Msg);
1145 }
1146
1147 Packet = AllocatePool (Len);
1148
1149 if (Packet == NULL) {
1150 return EFI_OUT_OF_RESOURCES;
1151 }
1152
1153 Packet->Size = Len;
1154 Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);
1155
1156 //
1157 // Fill in the DHCP header fields
1158 //
1159 Config = &DhcpSb->ActiveConfig;
1160 SeedHead = NULL;
1161
1162 if (Seed != NULL) {
1163 SeedHead = &Seed->Dhcp4.Header;
1164 }
1165
1166 Head = &Packet->Dhcp4.Header;
1167 ZeroMem (Head, sizeof (EFI_DHCP4_HEADER));
1168
1169 Head->OpCode = BOOTP_REQUEST;
1170 Head->HwType = DhcpSb->HwType;
1171 Head->HwAddrLen = DhcpSb->HwLen;
1172 Head->Xid = HTONL (DhcpSb->Xid);
1173 Head->Reserved = HTONS (0x8000); // Server, broadcast the message please.
1174
1175 EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);
1176 CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);
1177
1178 if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) {
1179 Head->Seconds = 0;
1180 } else if ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) {
1181 //
1182 // Use the same value as the original DHCPDISCOVER message.
1183 //
1184 Head->Seconds = DhcpSb->LastPacket->Dhcp4.Header.Seconds;
1185 } else {
1186 SetElapsedTime (&Head->Seconds, DhcpSb->ActiveChild);
1187 }
1188
1189 //
1190 // Append the DHCP message type
1191 //
1192 Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
1193 Buf = Packet->Dhcp4.Option;
1194 Buf = DhcpAppendOption (Buf, DHCP4_TAG_MSG_TYPE, 1, &Type);
1195
1196 //
1197 // Append the serverid option if necessary:
1198 // 1. DHCP decline message
1199 // 2. DHCP release message
1200 // 3. DHCP request to confirm one lease.
1201 //
1202 if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||
1203 ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))
1204 )
1205 {
1206 ASSERT ((Para != NULL) && (Para->ServerId != 0));
1207
1208 IpAddr = HTONL (Para->ServerId);
1209 Buf = DhcpAppendOption (Buf, DHCP4_TAG_SERVER_ID, 4, (UINT8 *)&IpAddr);
1210 }
1211
1212 //
1213 // Append the requested IP option if necessary:
1214 // 1. DHCP request to use the previously allocated address
1215 // 2. DHCP request to confirm one lease
1216 // 3. DHCP decline to decline one lease
1217 //
1218 IpAddr = 0;
1219
1220 if (Type == DHCP_MSG_REQUEST) {
1221 if (DhcpSb->DhcpState == Dhcp4Rebooting) {
1222 IpAddr = EFI_IP4 (Config->ClientAddress);
1223 } else if (DhcpSb->DhcpState == Dhcp4Requesting) {
1224 ASSERT (SeedHead != NULL);
1225 IpAddr = EFI_IP4 (SeedHead->YourAddr);
1226 }
1227 } else if (Type == DHCP_MSG_DECLINE) {
1228 ASSERT (SeedHead != NULL);
1229 IpAddr = EFI_IP4 (SeedHead->YourAddr);
1230 }
1231
1232 if (IpAddr != 0) {
1233 Buf = DhcpAppendOption (Buf, DHCP4_TAG_REQUEST_IP, 4, (UINT8 *)&IpAddr);
1234 }
1235
1236 //
1237 // Append the Max Message Length option if it isn't a DECLINE
1238 // or RELEASE to direct the server use large messages instead of
1239 // override the BOOTFILE and SERVER fields in the message head.
1240 //
1241 if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {
1242 MaxMsg = HTONS (0xFF00);
1243 Buf = DhcpAppendOption (Buf, DHCP4_TAG_MAXMSG, 2, (UINT8 *)&MaxMsg);
1244 }
1245
1246 //
1247 // Append the user's message if it isn't NULL
1248 //
1249 if (Msg != NULL) {
1250 Len = MIN ((UINT32)AsciiStrLen ((CHAR8 *)Msg), 255);
1251 Buf = DhcpAppendOption (Buf, DHCP4_TAG_MESSAGE, (UINT16)Len, Msg);
1252 }
1253
1254 //
1255 // Append the user configured options
1256 //
1257 if (DhcpSb->UserOptionLen != 0) {
1258 for (Index = 0; Index < Config->OptionCount; Index++) {
1259 //
1260 // We can't use any option other than the client ID from user
1261 // if it is a DHCP decline or DHCP release .
1262 //
1263 if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&
1264 (Config->OptionList[Index]->OpCode != DHCP4_TAG_CLIENT_ID))
1265 {
1266 continue;
1267 }
1268
1269 Buf = DhcpAppendOption (
1270 Buf,
1271 Config->OptionList[Index]->OpCode,
1272 Config->OptionList[Index]->Length,
1273 Config->OptionList[Index]->Data
1274 );
1275 }
1276 }
1277
1278 *(Buf++) = DHCP4_TAG_EOP;
1279 Packet->Length += (UINT32)(Buf - Packet->Dhcp4.Option);
1280
1281 //
1282 // OK, the message is built, call the user to override it.
1283 //
1284 Status = EFI_SUCCESS;
1285 NewPacket = NULL;
1286
1287 if (Type == DHCP_MSG_DISCOVER) {
1288 Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);
1289 } else if (Type == DHCP_MSG_REQUEST) {
1290 Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);
1291 } else if (Type == DHCP_MSG_DECLINE) {
1292 Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);
1293 }
1294
1295 if (EFI_ERROR (Status)) {
1296 FreePool (Packet);
1297 return Status;
1298 }
1299
1300 if (NewPacket != NULL) {
1301 FreePool (Packet);
1302 Packet = NewPacket;
1303 }
1304
1305 //
1306 // Save the Client Address will be sent out
1307 //
1308 CopyMem (
1309 &DhcpSb->ClientAddressSendOut[0],
1310 &Packet->Dhcp4.Header.ClientHwAddr[0],
1311 Packet->Dhcp4.Header.HwAddrLen
1312 );
1313
1314 //
1315 // Wrap it into a netbuf then send it.
1316 //
1317 Frag.Bulk = (UINT8 *)&Packet->Dhcp4.Header;
1318 Frag.Len = Packet->Length;
1319 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
1320
1321 if (Wrap == NULL) {
1322 FreePool (Packet);
1323 return EFI_OUT_OF_RESOURCES;
1324 }
1325
1326 //
1327 // Save it as the last sent packet for retransmission
1328 //
1329 if (DhcpSb->LastPacket != NULL) {
1330 FreePool (DhcpSb->LastPacket);
1331 }
1332
1333 DhcpSb->LastPacket = Packet;
1334 DhcpSetTransmitTimer (DhcpSb);
1335
1336 //
1337 // Broadcast the message, unless we know the server address.
1338 // Use the lease UdpIo port to send the unicast packet.
1339 //
1340 EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
1341 EndPoint.LocalAddr.Addr[0] = 0;
1342 EndPoint.RemotePort = DHCP_SERVER_PORT;
1343 EndPoint.LocalPort = DHCP_CLIENT_PORT;
1344 UdpIo = DhcpSb->UdpIo;
1345
1346 if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
1347 EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
1348 EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;
1349 UdpIo = DhcpSb->LeaseIoPort;
1350 }
1351
1352 ASSERT (UdpIo != NULL);
1353
1354 Status = UdpIoSendDatagram (
1355 UdpIo,
1356 Wrap,
1357 &EndPoint,
1358 NULL,
1359 DhcpOnPacketSent,
1360 DhcpSb
1361 );
1362
1363 if (EFI_ERROR (Status)) {
1364 NetbufFree (Wrap);
1365 return EFI_ACCESS_DENIED;
1366 }
1367
1368 return EFI_SUCCESS;
1369 }
1370
1371 /**
1372 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1373 will be retransmitted.
1374
1375 @param[in] DhcpSb The DHCP service instance
1376
1377 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1378 @retval EFI_SUCCESS The packet is retransmitted.
1379
1380 **/
1381 EFI_STATUS
1382 DhcpRetransmit (
1383 IN DHCP_SERVICE *DhcpSb
1384 )
1385 {
1386 UDP_IO *UdpIo;
1387 UDP_END_POINT EndPoint;
1388 NET_BUF *Wrap;
1389 NET_FRAGMENT Frag;
1390 EFI_STATUS Status;
1391
1392 ASSERT (DhcpSb->LastPacket != NULL);
1393
1394 //
1395 // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
1396 //
1397 if (DhcpSb->DhcpState != Dhcp4Requesting) {
1398 SetElapsedTime (&DhcpSb->LastPacket->Dhcp4.Header.Seconds, DhcpSb->ActiveChild);
1399 }
1400
1401 //
1402 // Wrap it into a netbuf then send it.
1403 //
1404 Frag.Bulk = (UINT8 *)&DhcpSb->LastPacket->Dhcp4.Header;
1405 Frag.Len = DhcpSb->LastPacket->Length;
1406 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
1407
1408 if (Wrap == NULL) {
1409 return EFI_OUT_OF_RESOURCES;
1410 }
1411
1412 //
1413 // Broadcast the message, unless we know the server address.
1414 //
1415 EndPoint.RemotePort = DHCP_SERVER_PORT;
1416 EndPoint.LocalPort = DHCP_CLIENT_PORT;
1417 EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
1418 EndPoint.LocalAddr.Addr[0] = 0;
1419 UdpIo = DhcpSb->UdpIo;
1420
1421 if (DhcpSb->DhcpState == Dhcp4Renewing) {
1422 EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
1423 EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;
1424 UdpIo = DhcpSb->LeaseIoPort;
1425 }
1426
1427 ASSERT (UdpIo != NULL);
1428
1429 Status = UdpIoSendDatagram (
1430 UdpIo,
1431 Wrap,
1432 &EndPoint,
1433 NULL,
1434 DhcpOnPacketSent,
1435 DhcpSb
1436 );
1437
1438 if (EFI_ERROR (Status)) {
1439 NetbufFree (Wrap);
1440 return EFI_ACCESS_DENIED;
1441 }
1442
1443 return EFI_SUCCESS;
1444 }
1445
1446 /**
1447 Each DHCP service has three timer. Two of them are count down timer.
1448 One for the packet retransmission. The other is to collect the offers.
1449 The third timer increments the lease life which is compared to T1, T2,
1450 and lease to determine the time to renew and rebind the lease.
1451 DhcpOnTimerTick will be called once every second.
1452
1453 @param[in] Event The timer event
1454 @param[in] Context The context, which is the DHCP service instance.
1455
1456 **/
1457 VOID
1458 EFIAPI
1459 DhcpOnTimerTick (
1460 IN EFI_EVENT Event,
1461 IN VOID *Context
1462 )
1463 {
1464 LIST_ENTRY *Entry;
1465 LIST_ENTRY *Next;
1466 DHCP_SERVICE *DhcpSb;
1467 DHCP_PROTOCOL *Instance;
1468 EFI_STATUS Status;
1469
1470 DhcpSb = (DHCP_SERVICE *)Context;
1471 Instance = DhcpSb->ActiveChild;
1472
1473 //
1474 // 0xffff is the maximum supported value for elapsed time according to RFC.
1475 //
1476 if ((Instance != NULL) && (Instance->ElaspedTime < 0xffff)) {
1477 Instance->ElaspedTime++;
1478 }
1479
1480 //
1481 // Check the retransmit timer
1482 //
1483 if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
1484 //
1485 // Select offer at each timeout if any offer received.
1486 //
1487 if ((DhcpSb->DhcpState == Dhcp4Selecting) && (DhcpSb->LastOffer != NULL)) {
1488 Status = DhcpChooseOffer (DhcpSb);
1489
1490 if (EFI_ERROR (Status)) {
1491 if (DhcpSb->LastOffer != NULL) {
1492 FreePool (DhcpSb->LastOffer);
1493 DhcpSb->LastOffer = NULL;
1494 }
1495 } else {
1496 goto ON_EXIT;
1497 }
1498 }
1499
1500 if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
1501 //
1502 // Still has another try
1503 //
1504 DhcpRetransmit (DhcpSb);
1505 DhcpSetTransmitTimer (DhcpSb);
1506 } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
1507 //
1508 // Retransmission failed, if the DHCP request is initiated by
1509 // user, adjust the current state according to the lease life.
1510 // Otherwise do nothing to wait the lease to timeout
1511 //
1512 if (DhcpSb->ExtraRefresh != 0) {
1513 Status = EFI_SUCCESS;
1514
1515 if (DhcpSb->LeaseLife < DhcpSb->T1) {
1516 Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
1517 } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
1518 Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
1519 } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
1520 Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
1521 } else {
1522 goto END_SESSION;
1523 }
1524
1525 DhcpSb->IoStatus = EFI_TIMEOUT;
1526 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
1527 }
1528 } else {
1529 goto END_SESSION;
1530 }
1531 }
1532
1533 //
1534 // If an address has been acquired, check whether need to
1535 // refresh or whether it has expired.
1536 //
1537 if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
1538 DhcpSb->LeaseLife++;
1539
1540 //
1541 // Don't timeout the lease, only count the life if user is
1542 // requesting extra renew/rebind. Adjust the state after that.
1543 //
1544 if (DhcpSb->ExtraRefresh != 0) {
1545 return;
1546 }
1547
1548 if (DhcpSb->LeaseLife == DhcpSb->Lease) {
1549 //
1550 // Lease expires, end the session
1551 //
1552 goto END_SESSION;
1553 } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
1554 //
1555 // T2 expires, transit to rebinding then send a REQUEST to any server
1556 //
1557 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
1558 goto END_SESSION;
1559 }
1560
1561 if (Instance != NULL) {
1562 Instance->ElaspedTime = 0;
1563 }
1564
1565 Status = DhcpSendMessage (
1566 DhcpSb,
1567 DhcpSb->Selected,
1568 DhcpSb->Para,
1569 DHCP_MSG_REQUEST,
1570 NULL
1571 );
1572
1573 if (EFI_ERROR (Status)) {
1574 goto END_SESSION;
1575 }
1576 } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
1577 //
1578 // T1 expires, transit to renewing, then send a REQUEST to the server
1579 //
1580 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
1581 goto END_SESSION;
1582 }
1583
1584 if (Instance != NULL) {
1585 Instance->ElaspedTime = 0;
1586 }
1587
1588 Status = DhcpSendMessage (
1589 DhcpSb,
1590 DhcpSb->Selected,
1591 DhcpSb->Para,
1592 DHCP_MSG_REQUEST,
1593 NULL
1594 );
1595
1596 if (EFI_ERROR (Status)) {
1597 goto END_SESSION;
1598 }
1599 }
1600 }
1601
1602 ON_EXIT:
1603 //
1604 // Iterate through all the DhcpSb Children.
1605 //
1606 NET_LIST_FOR_EACH_SAFE (Entry, Next, &DhcpSb->Children) {
1607 Instance = NET_LIST_USER_STRUCT (Entry, DHCP_PROTOCOL, Link);
1608 Instance->Timeout--;
1609 if ((Instance->Timeout == 0) && (Instance->Token != NULL)) {
1610 PxeDhcpDone (Instance);
1611 }
1612 }
1613
1614 return;
1615
1616 END_SESSION:
1617 DhcpEndSession (DhcpSb, EFI_TIMEOUT);
1618
1619 return;
1620 }