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