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