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