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