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