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