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