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