]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
Sync the latest version from R8.
[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 NetCopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
362
363 Ip = HTONL (DhcpSb->Netmask);
364 NetCopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
365
366 NetZeroMem (&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 NetZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));
379
380 Ip = HTONL (DhcpSb->Para->Router);
381 NetCopyMem (&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 NetFreePool (DhcpSb->LastOffer);
469 DhcpSb->LastOffer = NULL;
470 }
471
472 if (DhcpSb->Selected != NULL) {
473 NetFreePool (DhcpSb->Selected);
474 DhcpSb->Selected = NULL;
475 }
476
477 if (DhcpSb->Para != NULL) {
478 NetFreePool (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 *) NetAllocatePool (NewPacket->Size);
549 if (TempPacket != NULL) {
550 NetCopyMem (TempPacket, NewPacket, NewPacket->Size);
551 NetFreePool (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_DHCP4_HEADER *Head;
643 EFI_STATUS Status;
644
645 Status = EFI_SUCCESS;
646
647 //
648 // First validate the message:
649 // 1. the offer is a unicast
650 // 2. if it is a DHCP message, it must contains a server ID.
651 // Don't return a error for these two case otherwise the session is ended.
652 //
653 Head = &Packet->Dhcp4.Header;
654
655 if (!DHCP_IS_BOOTP (Para) &&
656 ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))) {
657 goto ON_EXIT;
658 }
659
660 //
661 // Call the user's callback. The action according to the return is as:
662 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
663 // 2. EFI_NOT_READY: wait for more offers
664 // 3. EFI_ABORTED: abort the address acquiring.
665 //
666 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
667
668 if (Status == EFI_SUCCESS) {
669 if (DhcpSb->LastOffer != NULL) {
670 NetFreePool (DhcpSb->LastOffer);
671 }
672
673 DhcpSb->LastOffer = Packet;
674
675 return DhcpChooseOffer (DhcpSb);
676
677 } else if (Status == EFI_NOT_READY) {
678 if (DhcpSb->LastOffer != NULL) {
679 NetFreePool (DhcpSb->LastOffer);
680 }
681
682 DhcpSb->LastOffer = Packet;
683
684 } else if (Status == EFI_ABORTED) {
685 //
686 // DhcpInput will end the session upon error return. Remember
687 // only to call DhcpEndSession at the top level call.
688 //
689 goto ON_EXIT;
690 }
691
692 return EFI_SUCCESS;
693
694 ON_EXIT:
695 NetFreePool (Packet);
696 return Status;
697 }
698
699
700 /**
701 Handle packets in DHCP request state.
702
703 @param DhcpSb The DHCP service instance
704 @param Packet The DHCP packet received
705 @param Para The DHCP parameter extracted from the packet. That
706 is, all the option value that we care.
707
708 @retval EFI_SUCCESS The packet is successfully processed.
709 @retval Others Some error occured.
710
711 **/
712 STATIC
713 EFI_STATUS
714 DhcpHandleRequest (
715 IN DHCP_SERVICE *DhcpSb,
716 IN EFI_DHCP4_PACKET *Packet,
717 IN DHCP_PARAMETER *Para
718 )
719 {
720 EFI_DHCP4_HEADER *Head;
721 EFI_DHCP4_HEADER *Selected;
722 EFI_STATUS Status;
723 UINT8 *Message;
724
725 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
726
727 Head = &Packet->Dhcp4.Header;
728 Selected = &DhcpSb->Selected->Dhcp4.Header;
729
730 //
731 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
732 //
733 if (DHCP_IS_BOOTP (Para) ||
734 (Para->ServerId != DhcpSb->Para->ServerId) ||
735 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {
736
737 Status = EFI_SUCCESS;
738 goto ON_EXIT;
739 }
740
741 //
742 // Received a NAK, end the session no matter what the user returns
743 //
744 Status = EFI_DEVICE_ERROR;
745
746 if (Para->DhcpType == DHCP_MSG_NAK) {
747 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
748 goto ON_EXIT;
749 }
750
751 //
752 // Check whether the ACK matches the selected offer
753 //
754 Message = NULL;
755
756 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
757 Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";
758 goto REJECT;
759 }
760
761 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
762
763 if (EFI_ERROR (Status)) {
764 Message = (UINT8 *) "Lease is denied upon received ACK";
765 goto REJECT;
766 }
767
768 //
769 // Record the lease, transit to BOUND state, then notify the user
770 //
771 Status = DhcpLeaseAcquired (DhcpSb);
772
773 if (EFI_ERROR (Status)) {
774 Message = (UINT8 *) "Lease is denied upon entering bound";
775 goto REJECT;
776 }
777
778 DhcpSb->IoStatus = EFI_SUCCESS;
779 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
780
781 NetFreePool (Packet);
782 return EFI_SUCCESS;
783
784 REJECT:
785 DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
786
787 ON_EXIT:
788 NetFreePool (Packet);
789 return Status;
790 }
791
792
793 /**
794 Handle packets in DHCP renew/rebound state.
795
796 @param DhcpSb The DHCP service instance
797 @param Packet The DHCP packet received
798 @param Para The DHCP parameter extracted from the packet. That
799 is, all the option value that we care.
800
801 @retval EFI_SUCCESS The packet is successfully processed.
802 @retval Others Some error occured.
803
804 **/
805 STATIC
806 EFI_STATUS
807 DhcpHandleRenewRebind (
808 IN DHCP_SERVICE *DhcpSb,
809 IN EFI_DHCP4_PACKET *Packet,
810 IN DHCP_PARAMETER *Para
811 )
812 {
813 EFI_DHCP4_HEADER *Head;
814 EFI_DHCP4_HEADER *Selected;
815 EFI_STATUS Status;
816
817 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
818
819 Head = &Packet->Dhcp4.Header;
820 Selected = &DhcpSb->Selected->Dhcp4.Header;
821
822 //
823 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
824 //
825 if (DHCP_IS_BOOTP (Para) ||
826 (Para->ServerId != DhcpSb->Para->ServerId) ||
827 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {
828
829 Status = EFI_SUCCESS;
830 goto ON_EXIT;
831 }
832
833 //
834 // Received a NAK, ignore the user's return then terminate the process
835 //
836 Status = EFI_DEVICE_ERROR;
837
838 if (Para->DhcpType == DHCP_MSG_NAK) {
839 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
840 goto ON_EXIT;
841 }
842
843 //
844 // The lease is different from the selected. Don't send a DECLINE
845 // since it isn't existed in the client's FSM.
846 //
847 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
848 goto ON_EXIT;
849 }
850
851 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
852
853 if (EFI_ERROR (Status)) {
854 goto ON_EXIT;
855 }
856
857 //
858 // Record the lease, start timer for T1 and T2,
859 //
860 DhcpComputeLease (DhcpSb, Para);
861 DhcpSb->LeaseLife = 0;
862 DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
863
864 if (DhcpSb->ExtraRefresh) {
865 DhcpSb->ExtraRefresh = FALSE;
866
867 DhcpSb->IoStatus = EFI_SUCCESS;
868 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
869 }
870
871 ON_EXIT:
872 NetFreePool (Packet);
873 return Status;
874 }
875
876
877 /**
878 Handle packets in DHCP reboot state.
879
880 @param DhcpSb The DHCP service instance
881 @param Packet The DHCP packet received
882 @param Para The DHCP parameter extracted from the packet. That
883 is, all the option value that we care.
884
885 @retval EFI_SUCCESS The packet is successfully processed.
886 @retval Others Some error occured.
887
888 **/
889 STATIC
890 EFI_STATUS
891 DhcpHandleReboot (
892 IN DHCP_SERVICE *DhcpSb,
893 IN EFI_DHCP4_PACKET *Packet,
894 IN DHCP_PARAMETER *Para
895 )
896 {
897 EFI_DHCP4_HEADER *Head;
898 EFI_STATUS Status;
899
900 Head = &Packet->Dhcp4.Header;
901
902 //
903 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
904 //
905 if (DHCP_IS_BOOTP (Para) ||
906 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {
907
908 Status = EFI_SUCCESS;
909 goto ON_EXIT;
910 }
911
912 //
913 // If a NAK is received, transit to INIT and try again.
914 //
915 if (Para->DhcpType == DHCP_MSG_NAK) {
916 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
917
918 DhcpSb->ClientAddr = 0;
919 DhcpSb->DhcpState = Dhcp4Init;
920
921 Status = DhcpInitRequest (DhcpSb);
922 goto ON_EXIT;
923 }
924
925 //
926 // Check whether the ACK matches the selected offer
927 //
928 if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
929 Status = EFI_DEVICE_ERROR;
930 goto ON_EXIT;
931 }
932
933 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
934 if (EFI_ERROR (Status)) {
935 goto ON_EXIT;
936 }
937
938 //
939 // OK, get the parameter from server, record the lease
940 //
941 DhcpSb->Para = NetAllocatePool (sizeof (DHCP_PARAMETER));
942
943 if (DhcpSb->Para == NULL) {
944 Status = EFI_OUT_OF_RESOURCES;
945 goto ON_EXIT;
946 }
947
948 DhcpSb->Selected = Packet;
949 CopyMem (DhcpSb->Para, Para, sizeof (*DhcpSb->Para));
950
951 Status = DhcpLeaseAcquired (DhcpSb);
952
953 if (EFI_ERROR (Status)) {
954 return Status;
955 }
956
957 DhcpSb->IoStatus = EFI_SUCCESS;
958 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
959 return EFI_SUCCESS;
960
961 ON_EXIT:
962 NetFreePool (Packet);
963 return Status;
964 }
965
966
967 /**
968 Handle the received DHCP packets. This function drivers the DHCP
969 state machine.
970
971 @param UdpPacket The UDP packets received.
972 @param Points The local/remote UDP access points
973 @param IoStatus The status of the UDP receive
974 @param Context The opaque parameter to the function.
975
976 @return None
977
978 **/
979 VOID
980 DhcpInput (
981 NET_BUF *UdpPacket,
982 UDP_POINTS *Points,
983 EFI_STATUS IoStatus,
984 VOID *Context
985 )
986 {
987 DHCP_SERVICE *DhcpSb;
988 EFI_DHCP4_HEADER *Head;
989 EFI_DHCP4_PACKET *Packet;
990 DHCP_PARAMETER *Para;
991 EFI_STATUS Status;
992 UINT32 Len;
993
994 Packet = NULL;
995 DhcpSb = (DHCP_SERVICE *) Context;
996
997 //
998 // Don't restart receive if error occurs or DHCP is destoried.
999 //
1000 if (EFI_ERROR (IoStatus)) {
1001 return ;
1002 } else if (DhcpSb->ServiceState == DHCP_DESTORY) {
1003 NetbufFree (UdpPacket);
1004 return ;
1005 }
1006
1007 ASSERT (UdpPacket != NULL);
1008
1009 if (DhcpSb->DhcpState == Dhcp4Stopped) {
1010 goto RESTART;
1011 }
1012
1013 //
1014 // Validate the packet received
1015 //
1016 if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
1017 goto RESTART;
1018 }
1019
1020 //
1021 // Copy the DHCP message to a continuous memory block
1022 //
1023 Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
1024 Packet = (EFI_DHCP4_PACKET *) NetAllocatePool (Len);
1025
1026 if (Packet == NULL) {
1027 goto RESTART;
1028 }
1029
1030 Packet->Size = Len;
1031 Head = &Packet->Dhcp4.Header;
1032 Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
1033
1034 if (Packet->Length != UdpPacket->TotalSize) {
1035 goto RESTART;
1036 }
1037
1038 //
1039 // Is this packet the answer to our packet?
1040 //
1041 if ((Head->OpCode != BOOTP_REPLY) ||
1042 (NTOHL (Head->Xid) != DhcpSb->Xid) ||
1043 !NET_MAC_EQUAL (&DhcpSb->Mac, Head->ClientHwAddr, DhcpSb->HwLen)) {
1044 goto RESTART;
1045 }
1046
1047 //
1048 // Validate the options and retrieve the interested options
1049 //
1050 Para = NULL;
1051 if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
1052 (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
1053 EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {
1054
1055 goto RESTART;
1056 }
1057
1058 //
1059 // Call the handler for each state. The handler should return
1060 // EFI_SUCCESS if the process can go on no matter whether the
1061 // packet is ignored or not. If the return is EFI_ERROR, the
1062 // session will be terminated. Packet's ownership is handled
1063 // over to the handlers. If operation succeeds, the handler
1064 // must notify the user. It isn't necessary to do if EFI_ERROR
1065 // is returned because the DhcpEndSession will notify the user.
1066 //
1067 Status = EFI_SUCCESS;
1068
1069 switch (DhcpSb->DhcpState) {
1070 case Dhcp4Selecting:
1071 Status = DhcpHandleSelect (DhcpSb, Packet, Para);
1072 break;
1073
1074 case Dhcp4Requesting:
1075 Status = DhcpHandleRequest (DhcpSb, Packet, Para);
1076 break;
1077
1078 case Dhcp4InitReboot:
1079 case Dhcp4Init:
1080 case Dhcp4Bound:
1081 //
1082 // Ignore the packet in INITREBOOT, INIT and BOUND states
1083 //
1084 NetFreePool (Packet);
1085 Status = EFI_SUCCESS;
1086 break;
1087
1088 case Dhcp4Renewing:
1089 case Dhcp4Rebinding:
1090 Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
1091 break;
1092
1093 case Dhcp4Rebooting:
1094 Status = DhcpHandleReboot (DhcpSb, Packet, Para);
1095 break;
1096 }
1097
1098 if (Para != NULL) {
1099 NetFreePool (Para);
1100 }
1101
1102 Packet = NULL;
1103
1104 if (EFI_ERROR (Status)) {
1105 NetbufFree (UdpPacket);
1106 DhcpEndSession (DhcpSb, Status);
1107 return ;
1108 }
1109
1110 RESTART:
1111 NetbufFree (UdpPacket);
1112
1113 if (Packet != NULL) {
1114 NetFreePool (Packet);
1115 }
1116
1117 Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
1118
1119 if (EFI_ERROR (Status)) {
1120 DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);
1121 }
1122 }
1123
1124
1125 /**
1126 Release the packet.
1127
1128 @param Arg The packet to release
1129
1130 @return None
1131
1132 **/
1133 VOID
1134 DhcpReleasePacket (
1135 IN VOID *Arg
1136 )
1137 {
1138 NetFreePool (Arg);
1139 }
1140
1141
1142 /**
1143 Release the net buffer when packet is sent.
1144
1145 @param UdpPacket The UDP packets received.
1146 @param Points The local/remote UDP access points
1147 @param IoStatus The status of the UDP receive
1148 @param Context The opaque parameter to the function.
1149
1150 @return None
1151
1152 **/
1153 VOID
1154 DhcpOnPacketSent (
1155 NET_BUF *Packet,
1156 UDP_POINTS *Points,
1157 EFI_STATUS IoStatus,
1158 VOID *Context
1159 )
1160 {
1161 NetbufFree (Packet);
1162 }
1163
1164
1165
1166 /**
1167 Build and transmit a DHCP message according to the current states.
1168 This function implement the Table 5. of RFC 2131. Always transits
1169 the state (as defined in Figure 5. of the same RFC) before sending
1170 a DHCP message. The table is adjusted accordingly.
1171
1172 @param DhcpSb The DHCP service instance
1173 @param Seed The seed packet which the new packet is based on
1174 @param Para The DHCP parameter of the Seed packet
1175 @param Type The message type to send
1176 @param Msg The human readable message to include in the packet
1177 sent.
1178
1179 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1180 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1181 @retval EFI_SUCCESS The message is sent
1182
1183 **/
1184 EFI_STATUS
1185 DhcpSendMessage (
1186 IN DHCP_SERVICE *DhcpSb,
1187 IN EFI_DHCP4_PACKET *Seed,
1188 IN DHCP_PARAMETER *Para,
1189 IN UINT8 Type,
1190 IN UINT8 *Msg
1191 )
1192 {
1193 EFI_DHCP4_CONFIG_DATA *Config;
1194 EFI_DHCP4_PACKET *Packet;
1195 EFI_DHCP4_PACKET *NewPacket;
1196 EFI_DHCP4_HEADER *Head;
1197 EFI_DHCP4_HEADER *SeedHead;
1198 UDP_IO_PORT *UdpIo;
1199 UDP_POINTS EndPoint;
1200 NET_BUF *Wrap;
1201 NET_FRAGMENT Frag;
1202 EFI_STATUS Status;
1203 IP4_ADDR IpAddr;
1204 UINT8 *Buf;
1205 UINT16 MaxMsg;
1206 UINT32 Len;
1207 UINT32 Index;
1208
1209 //
1210 // Allocate a big enough memory block to hold the DHCP packet
1211 //
1212 Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;
1213
1214 if (Msg != NULL) {
1215 Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg);
1216 }
1217
1218 Packet = NetAllocatePool (Len);
1219
1220 if (Packet == NULL) {
1221 return EFI_OUT_OF_RESOURCES;
1222 }
1223
1224 Packet->Size = Len;
1225 Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);
1226
1227 //
1228 // Fill in the DHCP header fields
1229 //
1230 Config = &DhcpSb->ActiveConfig;
1231 SeedHead = NULL;
1232
1233 if (Seed != NULL) {
1234 SeedHead = &Seed->Dhcp4.Header;
1235 }
1236
1237 Head = &Packet->Dhcp4.Header;
1238 NetZeroMem (Head, sizeof (EFI_DHCP4_HEADER));
1239
1240 Head->OpCode = BOOTP_REQUEST;
1241 Head->HwType = DhcpSb->HwType;
1242 Head->HwAddrLen = DhcpSb->HwLen;
1243 Head->Xid = HTONL (DhcpSb->Xid);
1244 Head->Reserved = HTONS (0x8000); //Server, broadcast the message please.
1245
1246 EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);
1247 NetCopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);
1248
1249 //
1250 // Append the DHCP message type
1251 //
1252 Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
1253 Buf = Packet->Dhcp4.Option;
1254 Buf = DhcpAppendOption (Buf, DHCP_TAG_TYPE, 1, &Type);
1255
1256 //
1257 // Append the serverid option if necessary:
1258 // 1. DHCP decline message
1259 // 2. DHCP release message
1260 // 3. DHCP request to confirm one lease.
1261 //
1262 if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||
1263 ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))) {
1264
1265 ASSERT ((Para != NULL) && (Para->ServerId != 0));
1266
1267 IpAddr = HTONL (Para->ServerId);
1268 Buf = DhcpAppendOption (Buf, DHCP_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);
1269 }
1270
1271 //
1272 // Append the requested IP option if necessary:
1273 // 1. DHCP request to use the previously allocated address
1274 // 2. DHCP request to confirm one lease
1275 // 3. DHCP decline to decline one lease
1276 //
1277 IpAddr = 0;
1278
1279 if (Type == DHCP_MSG_REQUEST) {
1280 if (DhcpSb->DhcpState == Dhcp4Rebooting) {
1281 IpAddr = EFI_IP4 (Config->ClientAddress);
1282
1283 } else if (DhcpSb->DhcpState == Dhcp4Requesting) {
1284 ASSERT (SeedHead != NULL);
1285 IpAddr = EFI_IP4 (SeedHead->YourAddr);
1286 }
1287
1288 } else if (Type == DHCP_MSG_DECLINE) {
1289 ASSERT (SeedHead != NULL);
1290 IpAddr = EFI_IP4 (SeedHead->YourAddr);
1291 }
1292
1293 if (IpAddr != 0) {
1294 Buf = DhcpAppendOption (Buf, DHCP_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);
1295 }
1296
1297 //
1298 // Append the Max Message Length option if it isn't a DECLINE
1299 // or RELEASE to direct the server use large messages instead of
1300 // override the BOOTFILE and SERVER fields in the message head.
1301 //
1302 if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {
1303 MaxMsg = HTONS (0xFF00);
1304 Buf = DhcpAppendOption (Buf, DHCP_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);
1305 }
1306
1307 //
1308 // Append the user's message if it isn't NULL
1309 //
1310 if (Msg != NULL) {
1311 Len = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255);
1312 Buf = DhcpAppendOption (Buf, DHCP_TAG_MESSAGE, (UINT16) Len, Msg);
1313 }
1314
1315 //
1316 // Append the user configured options
1317 //
1318 if (DhcpSb->UserOptionLen != 0) {
1319 for (Index = 0; Index < Config->OptionCount; Index++) {
1320 //
1321 // We can't use any option other than the client ID from user
1322 // if it is a DHCP decline or DHCP release .
1323 //
1324 if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&
1325 (Config->OptionList[Index]->OpCode != DHCP_TAG_CLIENT_ID)) {
1326 continue;
1327 }
1328
1329 Buf = DhcpAppendOption (
1330 Buf,
1331 Config->OptionList[Index]->OpCode,
1332 Config->OptionList[Index]->Length,
1333 Config->OptionList[Index]->Data
1334 );
1335 }
1336 }
1337
1338 *(Buf++) = DHCP_TAG_EOP;
1339 Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);
1340
1341 //
1342 // OK, the message is built, call the user to override it.
1343 //
1344 Status = EFI_SUCCESS;
1345 NewPacket = NULL;
1346
1347 if (Type == DHCP_MSG_DISCOVER) {
1348 Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);
1349
1350 } else if (Type == DHCP_MSG_REQUEST) {
1351 Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);
1352
1353 } else if (Type == DHCP_MSG_DECLINE) {
1354 Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);
1355 }
1356
1357 if (EFI_ERROR (Status)) {
1358 NetFreePool (Packet);
1359 return Status;
1360 }
1361
1362 if (NewPacket != NULL) {
1363 NetFreePool (Packet);
1364 Packet = NewPacket;
1365 }
1366
1367 //
1368 // Wrap it into a netbuf then send it.
1369 //
1370 Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;
1371 Frag.Len = Packet->Length;
1372 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet);
1373
1374 if (Wrap == NULL) {
1375 NetFreePool (Packet);
1376 return EFI_OUT_OF_RESOURCES;
1377 }
1378
1379 //
1380 // Save it as the last sent packet for retransmission
1381 //
1382 if (DhcpSb->LastPacket != NULL) {
1383 NetbufFree (DhcpSb->LastPacket);
1384 }
1385
1386 NET_GET_REF (Wrap);
1387 DhcpSb->LastPacket = Wrap;
1388 DhcpSetTransmitTimer (DhcpSb);
1389
1390 //
1391 // Broadcast the message, unless we know the server address.
1392 // Use the lease UdpIo port to send the unicast packet.
1393 //
1394 EndPoint.RemoteAddr = 0xffffffff;
1395 EndPoint.LocalAddr = 0;
1396 EndPoint.RemotePort = DHCP_SERVER_PORT;
1397 EndPoint.LocalPort = DHCP_CLIENT_PORT;
1398 UdpIo = DhcpSb->UdpIo;
1399
1400 if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
1401 EndPoint.RemoteAddr = DhcpSb->ServerAddr;
1402 EndPoint.LocalAddr = DhcpSb->ClientAddr;
1403 UdpIo = DhcpSb->LeaseIoPort;
1404 }
1405
1406 ASSERT (UdpIo != NULL);
1407 Status = UdpIoSendDatagram (UdpIo, Wrap, &EndPoint, 0, DhcpOnPacketSent, DhcpSb);
1408
1409 if (EFI_ERROR (Status)) {
1410 NetbufFree (Wrap);
1411 return EFI_ACCESS_DENIED;
1412 }
1413
1414 return EFI_SUCCESS;
1415 }
1416
1417
1418 /**
1419 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1420 will be retransmitted.
1421
1422 @param DhcpSb The DHCP service instance
1423
1424 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1425 @retval EFI_SUCCESS The packet is retransmitted.
1426
1427 **/
1428 EFI_STATUS
1429 DhcpRetransmit (
1430 IN DHCP_SERVICE *DhcpSb
1431 )
1432 {
1433 UDP_IO_PORT *UdpIo;
1434 UDP_POINTS EndPoint;
1435 EFI_STATUS Status;
1436
1437 ASSERT (DhcpSb->LastPacket != NULL);
1438
1439 //
1440 // Broadcast the message, unless we know the server address.
1441 //
1442 EndPoint.RemotePort = DHCP_SERVER_PORT;
1443 EndPoint.LocalPort = DHCP_CLIENT_PORT;
1444 EndPoint.RemoteAddr = 0xffffffff;
1445 EndPoint.LocalAddr = 0;
1446 UdpIo = DhcpSb->UdpIo;
1447
1448 if (DhcpSb->DhcpState == Dhcp4Renewing) {
1449 EndPoint.RemoteAddr = DhcpSb->ServerAddr;
1450 EndPoint.LocalAddr = DhcpSb->ClientAddr;
1451 UdpIo = DhcpSb->LeaseIoPort;
1452 }
1453
1454 ASSERT (UdpIo != NULL);
1455
1456 NET_GET_REF (DhcpSb->LastPacket);
1457 Status = UdpIoSendDatagram (
1458 UdpIo,
1459 DhcpSb->LastPacket,
1460 &EndPoint,
1461 0,
1462 DhcpOnPacketSent,
1463 DhcpSb
1464 );
1465
1466 if (EFI_ERROR (Status)) {
1467 NET_PUT_REF (DhcpSb->LastPacket);
1468 return EFI_ACCESS_DENIED;
1469 }
1470
1471 return EFI_SUCCESS;
1472 }
1473
1474
1475 /**
1476 Each DHCP service has three timer. Two of them are count down timer.
1477 One for the packet retransmission. The other is to collect the offers.
1478 The third timer increaments the lease life which is compared to T1, T2,
1479 and lease to determine the time to renew and rebind the lease.
1480 DhcpOnTimerTick will be called once every second.
1481
1482 @param Event The timer event
1483 @param Context The context, which is the DHCP service instance.
1484
1485 @return None
1486
1487 **/
1488 VOID
1489 EFIAPI
1490 DhcpOnTimerTick (
1491 IN EFI_EVENT Event,
1492 IN VOID *Context
1493 )
1494 {
1495 DHCP_SERVICE *DhcpSb;
1496 DHCP_PROTOCOL *Instance;
1497 EFI_STATUS Status;
1498
1499 DhcpSb = (DHCP_SERVICE *) Context;
1500 Instance = DhcpSb->ActiveChild;
1501
1502 //
1503 // Check the time to wait offer
1504 //
1505 if ((DhcpSb->WaitOffer > 0) && (--DhcpSb->WaitOffer == 0)) {
1506 //
1507 // OK, offer collection finished, select a offer
1508 //
1509 ASSERT (DhcpSb->DhcpState == Dhcp4Selecting);
1510
1511 if (DhcpSb->LastOffer == NULL) {
1512 goto END_SESSION;
1513 }
1514
1515 if (EFI_ERROR (DhcpChooseOffer (DhcpSb))) {
1516 goto END_SESSION;
1517 }
1518 }
1519
1520 //
1521 // Check the retransmit timer
1522 //
1523 if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
1524
1525 if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
1526 //
1527 // Still has another try
1528 //
1529 DhcpRetransmit (DhcpSb);
1530 DhcpSetTransmitTimer (DhcpSb);
1531
1532 } else {
1533 if (!DHCP_CONNECTED (DhcpSb->DhcpState)) {
1534 goto END_SESSION;
1535 }
1536
1537 //
1538 // Retransmission failed, if the DHCP request is initiated by
1539 // user, adjust the current state according to the lease life.
1540 // Otherwise do nothing to wait the lease to timeout
1541 //
1542 if (DhcpSb->ExtraRefresh) {
1543 Status = EFI_SUCCESS;
1544
1545 if (DhcpSb->LeaseLife < DhcpSb->T1) {
1546 Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
1547
1548 } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
1549 Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
1550
1551 } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
1552 Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
1553
1554 } else {
1555 goto END_SESSION;
1556
1557 }
1558
1559 DhcpSb->IoStatus = EFI_TIMEOUT;
1560 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
1561 }
1562 }
1563 }
1564
1565 //
1566 // If an address has been acquired, check whether need to
1567 // refresh or whether it has expired.
1568 //
1569 if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
1570 DhcpSb->LeaseLife++;
1571
1572 //
1573 // Don't timeout the lease, only count the life if user is
1574 // requesting extra renew/rebind. Adjust the state after that.
1575 //
1576 if (DhcpSb->ExtraRefresh) {
1577 return ;
1578 }
1579
1580 if (DhcpSb->LeaseLife == DhcpSb->Lease) {
1581 //
1582 // Lease expires, end the session
1583 //
1584 goto END_SESSION;
1585
1586 } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
1587 //
1588 // T2 expires, transit to rebinding then send a REQUEST to any server
1589 //
1590 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
1591 goto END_SESSION;
1592 }
1593
1594 Status = DhcpSendMessage (
1595 DhcpSb,
1596 DhcpSb->Selected,
1597 DhcpSb->Para,
1598 DHCP_MSG_REQUEST,
1599 NULL
1600 );
1601
1602 if (EFI_ERROR (Status)) {
1603 goto END_SESSION;
1604 }
1605
1606 } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
1607 //
1608 // T1 expires, transit to renewing, then send a REQUEST to the server
1609 //
1610 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
1611 goto END_SESSION;
1612 }
1613
1614 Status = DhcpSendMessage (
1615 DhcpSb,
1616 DhcpSb->Selected,
1617 DhcpSb->Para,
1618 DHCP_MSG_REQUEST,
1619 NULL
1620 );
1621
1622 if (EFI_ERROR (Status)) {
1623 goto END_SESSION;
1624 }
1625 }
1626 }
1627
1628 //
1629 //
1630 //
1631 if ((Instance != NULL) && (Instance->Token != NULL)) {
1632 Instance->Timeout--;
1633 if (Instance->Timeout == 0) {
1634 PxeDhcpDone (Instance);
1635 }
1636 }
1637
1638 return ;
1639
1640 END_SESSION:
1641 DhcpEndSession (DhcpSb, EFI_TIMEOUT);
1642
1643 return ;
1644 }