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