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