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