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