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