]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
1. Fix a bug in Dhcp4 to prevent unexpected broadcast DHCP packets be queued up in...
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Dhcp4Dxe / Dhcp4Io.c
1 /** @file
2 EFI DHCP protocol implementation.
3
4 Copyright (c) 2006 - 2009, 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 port 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 DhcpConfigLeaseIoPort (
317 IN UDP_IO_PORT *UdpIo,
318 IN VOID *Context
319 )
320 {
321 EFI_UDP4_CONFIG_DATA UdpConfigData;
322 EFI_IPv4_ADDRESS Subnet;
323 EFI_IPv4_ADDRESS Gateway;
324 DHCP_SERVICE *DhcpSb;
325 EFI_STATUS Status;
326 IP4_ADDR Ip;
327
328 DhcpSb = (DHCP_SERVICE *) Context;
329
330 UdpConfigData.AcceptBroadcast = FALSE;
331 UdpConfigData.AcceptPromiscuous = FALSE;
332 UdpConfigData.AcceptAnyPort = FALSE;
333 UdpConfigData.AllowDuplicatePort = TRUE;
334 UdpConfigData.TypeOfService = 0;
335 UdpConfigData.TimeToLive = 64;
336 UdpConfigData.DoNotFragment = FALSE;
337 UdpConfigData.ReceiveTimeout = 1;
338 UdpConfigData.TransmitTimeout = 0;
339
340 UdpConfigData.UseDefaultAddress = FALSE;
341 UdpConfigData.StationPort = DHCP_CLIENT_PORT;
342 UdpConfigData.RemotePort = DHCP_SERVER_PORT;
343
344 Ip = HTONL (DhcpSb->ClientAddr);
345 CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
346
347 Ip = HTONL (DhcpSb->Netmask);
348 CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
349
350 ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
351
352 Status = UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);
353
354 if (EFI_ERROR (Status)) {
355 return Status;
356 }
357
358 //
359 // Add a default route if received from the server.
360 //
361 if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) {
362 ZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));
363
364 Ip = HTONL (DhcpSb->Para->Router);
365 CopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
366
367 UdpIo->Udp->Routes (UdpIo->Udp, FALSE, &Subnet, &Subnet, &Gateway);
368 }
369
370 return EFI_SUCCESS;
371 }
372
373
374 /**
375 Update the lease states when a new lease is acquired. It will not only
376 save the acquired the address and lease time, it will also create a UDP
377 child to provide address resolution for the address.
378
379 @param DhcpSb The DHCP service instance
380
381 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
382 @retval EFI_SUCCESS The lease is recorded.
383
384 **/
385 EFI_STATUS
386 DhcpLeaseAcquired (
387 IN OUT DHCP_SERVICE *DhcpSb
388 )
389 {
390 INTN Class;
391
392 DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);
393
394 if (DhcpSb->Para != NULL) {
395 DhcpSb->Netmask = DhcpSb->Para->NetMask;
396 DhcpSb->ServerAddr = DhcpSb->Para->ServerId;
397 }
398
399 if (DhcpSb->Netmask == 0) {
400 Class = NetGetIpClass (DhcpSb->ClientAddr);
401 DhcpSb->Netmask = gIp4AllMasks[Class << 3];
402 }
403
404 if (DhcpSb->LeaseIoPort != NULL) {
405 UdpIoFreePort (DhcpSb->LeaseIoPort);
406 }
407
408 //
409 // Create a UDP/IP child to provide ARP service for the Leased IP,
410 // and transmit unicast packet with it as source address. Don't
411 // start receive on this port, the queued packet will be timeout.
412 //
413 DhcpSb->LeaseIoPort = UdpIoCreatePort (
414 DhcpSb->Controller,
415 DhcpSb->Image,
416 DhcpConfigLeaseIoPort,
417 DhcpSb
418 );
419
420 if (DhcpSb->LeaseIoPort == NULL) {
421 return EFI_OUT_OF_RESOURCES;
422 }
423
424 if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
425 DhcpComputeLease (DhcpSb, DhcpSb->Para);
426 }
427
428 return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
429 }
430
431
432 /**
433 Clean up the DHCP related states, IoStatus isn't reset.
434
435 @param DhcpSb The DHCP instance service.
436
437 **/
438 VOID
439 DhcpCleanLease (
440 IN DHCP_SERVICE *DhcpSb
441 )
442 {
443 DhcpSb->DhcpState = Dhcp4Init;
444 DhcpSb->Xid = DhcpSb->Xid + 1;
445 DhcpSb->ClientAddr = 0;
446 DhcpSb->Netmask = 0;
447 DhcpSb->ServerAddr = 0;
448
449 if (DhcpSb->LastOffer != NULL) {
450 FreePool (DhcpSb->LastOffer);
451 DhcpSb->LastOffer = NULL;
452 }
453
454 if (DhcpSb->Selected != NULL) {
455 FreePool (DhcpSb->Selected);
456 DhcpSb->Selected = NULL;
457 }
458
459 if (DhcpSb->Para != NULL) {
460 FreePool (DhcpSb->Para);
461 DhcpSb->Para = NULL;
462 }
463
464 DhcpSb->Lease = 0;
465 DhcpSb->T1 = 0;
466 DhcpSb->T2 = 0;
467 DhcpSb->ExtraRefresh = FALSE;
468
469 if (DhcpSb->LeaseIoPort != NULL) {
470 UdpIoFreePort (DhcpSb->LeaseIoPort);
471 DhcpSb->LeaseIoPort = NULL;
472 }
473
474 if (DhcpSb->LastPacket != NULL) {
475 FreePool (DhcpSb->LastPacket);
476 DhcpSb->LastPacket = NULL;
477 }
478
479 DhcpSb->PacketToLive = 0;
480 DhcpSb->LastTimeout = 0;
481 DhcpSb->CurRetry = 0;
482 DhcpSb->MaxRetries = 0;
483 DhcpSb->LeaseLife = 0;
484 }
485
486
487 /**
488 Select a offer among all the offers collected. If the offer selected is
489 of BOOTP, the lease is recorded and user notified. If the offer is of
490 DHCP, it will request the offer from the server.
491
492 @param[in] DhcpSb The DHCP service instance.
493
494 @retval EFI_SUCCESS One of the offer is selected.
495
496 **/
497 EFI_STATUS
498 DhcpChooseOffer (
499 IN DHCP_SERVICE *DhcpSb
500 )
501 {
502 EFI_DHCP4_PACKET *Selected;
503 EFI_DHCP4_PACKET *NewPacket;
504 EFI_DHCP4_PACKET *TempPacket;
505 EFI_STATUS Status;
506
507 ASSERT (DhcpSb->LastOffer != NULL);
508
509 //
510 // User will cache previous offers if he wants to select
511 // from multiple offers. If user provides an invalid packet,
512 // use the last offer, otherwise use the provided packet.
513 //
514 NewPacket = NULL;
515 Status = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);
516
517 if (EFI_ERROR (Status)) {
518 return Status;
519 }
520
521 Selected = DhcpSb->LastOffer;
522
523 if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {
524 TempPacket = (EFI_DHCP4_PACKET *) AllocatePool (NewPacket->Size);
525 if (TempPacket != NULL) {
526 CopyMem (TempPacket, NewPacket, NewPacket->Size);
527 gBS->FreePool (Selected);
528 Selected = TempPacket;
529 }
530 }
531
532 DhcpSb->Selected = Selected;
533 DhcpSb->LastOffer = NULL;
534 DhcpSb->Para = NULL;
535 DhcpValidateOptions (Selected, &DhcpSb->Para);
536
537 //
538 // A bootp offer has been selected, save the lease status,
539 // enter bound state then notify the user.
540 //
541 if (DHCP_IS_BOOTP (DhcpSb->Para)) {
542 Status = DhcpLeaseAcquired (DhcpSb);
543
544 if (EFI_ERROR (Status)) {
545 return Status;
546 }
547
548 DhcpSb->IoStatus = EFI_SUCCESS;
549 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
550 return EFI_SUCCESS;
551 }
552
553 //
554 // Send a DHCP requests
555 //
556 Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);
557
558 if (EFI_ERROR (Status)) {
559 return Status;
560 }
561
562 return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);
563 }
564
565
566 /**
567 Terminate the current address acquire. All the allocated resources
568 are released. Be careful when calling this function. A rule related
569 to this is: only call DhcpEndSession at the highest level, such as
570 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
571
572 @param[in] DhcpSb The DHCP service instance
573 @param[in] Status The result of the DHCP process.
574
575 **/
576 VOID
577 DhcpEndSession (
578 IN DHCP_SERVICE *DhcpSb,
579 IN EFI_STATUS Status
580 )
581 {
582 if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
583 DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);
584 } else {
585 DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);
586 }
587
588 DhcpCleanLease (DhcpSb);
589
590 DhcpSb->IoStatus = Status;
591 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
592 }
593
594
595 /**
596 Handle packets in DHCP select state.
597
598 @param[in] DhcpSb The DHCP service instance
599 @param[in] Packet The DHCP packet received
600 @param[in] Para The DHCP parameter extracted from the packet. That
601 is, all the option value that we care.
602
603 @retval EFI_SUCCESS The packet is successfully processed.
604 @retval Others Some error occured.
605
606 **/
607 EFI_STATUS
608 DhcpHandleSelect (
609 IN DHCP_SERVICE *DhcpSb,
610 IN EFI_DHCP4_PACKET *Packet,
611 IN DHCP_PARAMETER *Para
612 )
613 {
614 EFI_STATUS Status;
615
616 Status = EFI_SUCCESS;
617
618 //
619 // First validate the message:
620 // 1. the offer is a unicast
621 // 2. if it is a DHCP message, it must contains a server ID.
622 // Don't return a error for these two case otherwise the session is ended.
623 //
624 if (!DHCP_IS_BOOTP (Para) &&
625 ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))
626 ) {
627 goto ON_EXIT;
628 }
629
630 //
631 // Call the user's callback. The action according to the return is as:
632 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
633 // 2. EFI_NOT_READY: wait for more offers
634 // 3. EFI_ABORTED: abort the address acquiring.
635 //
636 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
637
638 if (Status == EFI_SUCCESS) {
639 if (DhcpSb->LastOffer != NULL) {
640 FreePool (DhcpSb->LastOffer);
641 }
642
643 DhcpSb->LastOffer = Packet;
644
645 return DhcpChooseOffer (DhcpSb);
646
647 } else if (Status == EFI_NOT_READY) {
648 if (DhcpSb->LastOffer != NULL) {
649 FreePool (DhcpSb->LastOffer);
650 }
651
652 DhcpSb->LastOffer = Packet;
653
654 } else if (Status == EFI_ABORTED) {
655 //
656 // DhcpInput will end the session upon error return. Remember
657 // only to call DhcpEndSession at the top level call.
658 //
659 goto ON_EXIT;
660 }
661
662 return EFI_SUCCESS;
663
664 ON_EXIT:
665 gBS->FreePool (Packet);
666 return Status;
667 }
668
669
670 /**
671 Handle packets in DHCP request state.
672
673 @param[in] DhcpSb The DHCP service instance
674 @param[in] Packet The DHCP packet received
675 @param[in] Para The DHCP parameter extracted from the packet. That
676 is, all the option value that we care.
677
678 @retval EFI_SUCCESS The packet is successfully processed.
679 @retval Others Some error occured.
680
681 **/
682 EFI_STATUS
683 DhcpHandleRequest (
684 IN DHCP_SERVICE *DhcpSb,
685 IN EFI_DHCP4_PACKET *Packet,
686 IN DHCP_PARAMETER *Para
687 )
688 {
689 EFI_DHCP4_HEADER *Head;
690 EFI_DHCP4_HEADER *Selected;
691 EFI_STATUS Status;
692 UINT8 *Message;
693
694 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
695
696 Head = &Packet->Dhcp4.Header;
697 Selected = &DhcpSb->Selected->Dhcp4.Header;
698
699 //
700 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
701 //
702 if (DHCP_IS_BOOTP (Para) ||
703 (Para->ServerId != DhcpSb->Para->ServerId) ||
704 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
705 ) {
706
707 Status = EFI_SUCCESS;
708 goto ON_EXIT;
709 }
710
711 //
712 // Received a NAK, end the session no matter what the user returns
713 //
714 Status = EFI_DEVICE_ERROR;
715
716 if (Para->DhcpType == DHCP_MSG_NAK) {
717 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
718 goto ON_EXIT;
719 }
720
721 //
722 // Check whether the ACK matches the selected offer
723 //
724 Message = NULL;
725
726 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
727 Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";
728 goto REJECT;
729 }
730
731 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
732
733 if (EFI_ERROR (Status)) {
734 Message = (UINT8 *) "Lease is denied upon received ACK";
735 goto REJECT;
736 }
737
738 //
739 // Record the lease, transit to BOUND state, then notify the user
740 //
741 Status = DhcpLeaseAcquired (DhcpSb);
742
743 if (EFI_ERROR (Status)) {
744 Message = (UINT8 *) "Lease is denied upon entering bound";
745 goto REJECT;
746 }
747
748 DhcpSb->IoStatus = EFI_SUCCESS;
749 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
750
751 gBS->FreePool (Packet);
752 return EFI_SUCCESS;
753
754 REJECT:
755 DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
756
757 ON_EXIT:
758 gBS->FreePool (Packet);
759 return Status;
760 }
761
762
763 /**
764 Handle packets in DHCP renew/rebound state.
765
766 @param[in] DhcpSb The DHCP service instance
767 @param[in] Packet The DHCP packet received
768 @param[in] Para The DHCP parameter extracted from the packet. That
769 is, all the option value that we care.
770
771 @retval EFI_SUCCESS The packet is successfully processed.
772 @retval Others Some error occured.
773
774 **/
775 EFI_STATUS
776 DhcpHandleRenewRebind (
777 IN DHCP_SERVICE *DhcpSb,
778 IN EFI_DHCP4_PACKET *Packet,
779 IN DHCP_PARAMETER *Para
780 )
781 {
782 EFI_DHCP4_HEADER *Head;
783 EFI_DHCP4_HEADER *Selected;
784 EFI_STATUS Status;
785
786 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
787
788 Head = &Packet->Dhcp4.Header;
789 Selected = &DhcpSb->Selected->Dhcp4.Header;
790
791 //
792 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
793 //
794 if (DHCP_IS_BOOTP (Para) ||
795 (Para->ServerId != DhcpSb->Para->ServerId) ||
796 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
797 ) {
798
799 Status = EFI_SUCCESS;
800 goto ON_EXIT;
801 }
802
803 //
804 // Received a NAK, ignore the user's return then terminate the process
805 //
806 Status = EFI_DEVICE_ERROR;
807
808 if (Para->DhcpType == DHCP_MSG_NAK) {
809 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
810 goto ON_EXIT;
811 }
812
813 //
814 // The lease is different from the selected. Don't send a DECLINE
815 // since it isn't existed in the client's FSM.
816 //
817 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
818 goto ON_EXIT;
819 }
820
821 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
822
823 if (EFI_ERROR (Status)) {
824 goto ON_EXIT;
825 }
826
827 //
828 // Record the lease, start timer for T1 and T2,
829 //
830 DhcpComputeLease (DhcpSb, Para);
831 DhcpSb->LeaseLife = 0;
832 DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
833
834 if (DhcpSb->ExtraRefresh != 0) {
835 DhcpSb->ExtraRefresh = FALSE;
836
837 DhcpSb->IoStatus = EFI_SUCCESS;
838 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
839 }
840
841 ON_EXIT:
842 gBS->FreePool (Packet);
843 return Status;
844 }
845
846
847 /**
848 Handle packets in DHCP reboot state.
849
850 @param[in] DhcpSb The DHCP service instance
851 @param[in] Packet The DHCP packet received
852 @param[in] Para The DHCP parameter extracted from the packet. That
853 is, all the option value that we care.
854
855 @retval EFI_SUCCESS The packet is successfully processed.
856 @retval Others Some error occured.
857
858 **/
859 EFI_STATUS
860 DhcpHandleReboot (
861 IN DHCP_SERVICE *DhcpSb,
862 IN EFI_DHCP4_PACKET *Packet,
863 IN DHCP_PARAMETER *Para
864 )
865 {
866 EFI_DHCP4_HEADER *Head;
867 EFI_STATUS Status;
868
869 Head = &Packet->Dhcp4.Header;
870
871 //
872 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
873 //
874 if (DHCP_IS_BOOTP (Para) ||
875 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
876 ) {
877
878 Status = EFI_SUCCESS;
879 goto ON_EXIT;
880 }
881
882 //
883 // If a NAK is received, transit to INIT and try again.
884 //
885 if (Para->DhcpType == DHCP_MSG_NAK) {
886 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
887
888 DhcpSb->ClientAddr = 0;
889 DhcpSb->DhcpState = Dhcp4Init;
890
891 Status = DhcpInitRequest (DhcpSb);
892 goto ON_EXIT;
893 }
894
895 //
896 // Check whether the ACK matches the selected offer
897 //
898 if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
899 Status = EFI_DEVICE_ERROR;
900 goto ON_EXIT;
901 }
902
903 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
904 if (EFI_ERROR (Status)) {
905 goto ON_EXIT;
906 }
907
908 //
909 // OK, get the parameter from server, record the lease
910 //
911 DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);
912 if (DhcpSb->Para == NULL) {
913 Status = EFI_OUT_OF_RESOURCES;
914 goto ON_EXIT;
915 }
916
917 DhcpSb->Selected = Packet;
918 Status = DhcpLeaseAcquired (DhcpSb);
919 if (EFI_ERROR (Status)) {
920 return Status;
921 }
922
923 DhcpSb->IoStatus = EFI_SUCCESS;
924 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
925 return EFI_SUCCESS;
926
927 ON_EXIT:
928 gBS->FreePool (Packet);
929 return Status;
930 }
931
932
933 /**
934 Handle the received DHCP packets. This function drives the DHCP
935 state machine.
936
937 @param UdpPacket The UDP packets received.
938 @param Points The local/remote UDP access points
939 @param IoStatus The status of the UDP receive
940 @param Context The opaque parameter to the function.
941
942 **/
943 VOID
944 DhcpInput (
945 NET_BUF *UdpPacket,
946 UDP_POINTS *Points,
947 EFI_STATUS IoStatus,
948 VOID *Context
949 )
950 {
951 DHCP_SERVICE *DhcpSb;
952 EFI_DHCP4_HEADER *Head;
953 EFI_DHCP4_PACKET *Packet;
954 DHCP_PARAMETER *Para;
955 EFI_STATUS Status;
956 UINT32 Len;
957
958 Packet = NULL;
959 DhcpSb = (DHCP_SERVICE *) Context;
960
961 //
962 // Don't restart receive if error occurs or DHCP is destoried.
963 //
964 if (EFI_ERROR (IoStatus)) {
965 return ;
966 } else if (DhcpSb->ServiceState == DHCP_DESTORY) {
967 NetbufFree (UdpPacket);
968 return ;
969 }
970
971 ASSERT (UdpPacket != NULL);
972
973 if (DhcpSb->DhcpState == Dhcp4Stopped) {
974 goto RESTART;
975 }
976
977 //
978 // Validate the packet received
979 //
980 if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
981 goto RESTART;
982 }
983
984 //
985 // Copy the DHCP message to a continuous memory block
986 //
987 Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
988 Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len);
989
990 if (Packet == NULL) {
991 goto RESTART;
992 }
993
994 Packet->Size = Len;
995 Head = &Packet->Dhcp4.Header;
996 Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
997
998 if (Packet->Length != UdpPacket->TotalSize) {
999 goto RESTART;
1000 }
1001
1002 //
1003 // Is this packet the answer to our packet?
1004 //
1005 if ((Head->OpCode != BOOTP_REPLY) ||
1006 (NTOHL (Head->Xid) != DhcpSb->Xid) ||
1007 (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
1008 goto RESTART;
1009 }
1010
1011 //
1012 // Validate the options and retrieve the interested options
1013 //
1014 Para = NULL;
1015 if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
1016 (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
1017 EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {
1018
1019 goto RESTART;
1020 }
1021
1022 //
1023 // Call the handler for each state. The handler should return
1024 // EFI_SUCCESS if the process can go on no matter whether the
1025 // packet is ignored or not. If the return is EFI_ERROR, the
1026 // session will be terminated. Packet's ownership is handled
1027 // over to the handlers. If operation succeeds, the handler
1028 // must notify the user. It isn't necessary to do if EFI_ERROR
1029 // is returned because the DhcpEndSession will notify the user.
1030 //
1031 Status = EFI_SUCCESS;
1032
1033 switch (DhcpSb->DhcpState) {
1034 case Dhcp4Selecting:
1035 Status = DhcpHandleSelect (DhcpSb, Packet, Para);
1036 break;
1037
1038 case Dhcp4Requesting:
1039 Status = DhcpHandleRequest (DhcpSb, Packet, Para);
1040 break;
1041
1042 case Dhcp4InitReboot:
1043 case Dhcp4Init:
1044 case Dhcp4Bound:
1045 //
1046 // Ignore the packet in INITREBOOT, INIT and BOUND states
1047 //
1048 gBS->FreePool (Packet);
1049 Status = EFI_SUCCESS;
1050 break;
1051
1052 case Dhcp4Renewing:
1053 case Dhcp4Rebinding:
1054 Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
1055 break;
1056
1057 case Dhcp4Rebooting:
1058 Status = DhcpHandleReboot (DhcpSb, Packet, Para);
1059 break;
1060 }
1061
1062 if (Para != NULL) {
1063 FreePool (Para);
1064 }
1065
1066 Packet = NULL;
1067
1068 if (EFI_ERROR (Status)) {
1069 NetbufFree (UdpPacket);
1070 UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
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 FreePool (DhcpSb->LastPacket);
1357 }
1358
1359 DhcpSb->LastPacket = Packet;
1360 DhcpSetTransmitTimer (DhcpSb);
1361
1362 //
1363 // Broadcast the message, unless we know the server address.
1364 // Use the lease UdpIo port to send the unicast packet.
1365 //
1366 EndPoint.RemoteAddr = 0xffffffff;
1367 EndPoint.LocalAddr = 0;
1368 EndPoint.RemotePort = DHCP_SERVER_PORT;
1369 EndPoint.LocalPort = DHCP_CLIENT_PORT;
1370 UdpIo = DhcpSb->UdpIo;
1371
1372 if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
1373 EndPoint.RemoteAddr = DhcpSb->ServerAddr;
1374 EndPoint.LocalAddr = DhcpSb->ClientAddr;
1375 UdpIo = DhcpSb->LeaseIoPort;
1376 }
1377
1378 ASSERT (UdpIo != NULL);
1379 NET_GET_REF (Wrap);
1380
1381 Status = UdpIoSendDatagram (
1382 UdpIo,
1383 Wrap,
1384 &EndPoint,
1385 0,
1386 DhcpOnPacketSent,
1387 DhcpSb
1388 );
1389
1390 if (EFI_ERROR (Status)) {
1391 NET_PUT_REF (Wrap);
1392 return EFI_ACCESS_DENIED;
1393 }
1394
1395 return EFI_SUCCESS;
1396 }
1397
1398
1399 /**
1400 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1401 will be retransmitted.
1402
1403 @param[in] DhcpSb The DHCP service instance
1404
1405 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1406 @retval EFI_SUCCESS The packet is retransmitted.
1407
1408 **/
1409 EFI_STATUS
1410 DhcpRetransmit (
1411 IN DHCP_SERVICE *DhcpSb
1412 )
1413 {
1414 UDP_IO_PORT *UdpIo;
1415 UDP_POINTS EndPoint;
1416 NET_BUF *Wrap;
1417 NET_FRAGMENT Frag;
1418 EFI_STATUS Status;
1419
1420 ASSERT (DhcpSb->LastPacket != NULL);
1421
1422 DhcpSb->LastPacket->Dhcp4.Header.Seconds = HTONS (*(UINT16 *)(&DhcpSb->LastTimeout));
1423
1424 //
1425 // Wrap it into a netbuf then send it.
1426 //
1427 Frag.Bulk = (UINT8 *) &DhcpSb->LastPacket->Dhcp4.Header;
1428 Frag.Len = DhcpSb->LastPacket->Length;
1429 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, DhcpSb->LastPacket);
1430
1431 if (Wrap == NULL) {
1432 return EFI_OUT_OF_RESOURCES;
1433 }
1434
1435 //
1436 // Broadcast the message, unless we know the server address.
1437 //
1438 EndPoint.RemotePort = DHCP_SERVER_PORT;
1439 EndPoint.LocalPort = DHCP_CLIENT_PORT;
1440 EndPoint.RemoteAddr = 0xffffffff;
1441 EndPoint.LocalAddr = 0;
1442 UdpIo = DhcpSb->UdpIo;
1443
1444 if (DhcpSb->DhcpState == Dhcp4Renewing) {
1445 EndPoint.RemoteAddr = DhcpSb->ServerAddr;
1446 EndPoint.LocalAddr = DhcpSb->ClientAddr;
1447 UdpIo = DhcpSb->LeaseIoPort;
1448 }
1449
1450 ASSERT (UdpIo != NULL);
1451
1452 NET_GET_REF (Wrap);
1453 Status = UdpIoSendDatagram (
1454 UdpIo,
1455 Wrap,
1456 &EndPoint,
1457 0,
1458 DhcpOnPacketSent,
1459 DhcpSb
1460 );
1461
1462 if (EFI_ERROR (Status)) {
1463 NET_PUT_REF (Wrap);
1464 return EFI_ACCESS_DENIED;
1465 }
1466
1467 return EFI_SUCCESS;
1468 }
1469
1470
1471 /**
1472 Each DHCP service has three timer. Two of them are count down timer.
1473 One for the packet retransmission. The other is to collect the offers.
1474 The third timer increaments the lease life which is compared to T1, T2,
1475 and lease to determine the time to renew and rebind the lease.
1476 DhcpOnTimerTick will be called once every second.
1477
1478 @param[in] Event The timer event
1479 @param[in] Context The context, which is the DHCP service instance.
1480
1481 **/
1482 VOID
1483 EFIAPI
1484 DhcpOnTimerTick (
1485 IN EFI_EVENT Event,
1486 IN VOID *Context
1487 )
1488 {
1489 DHCP_SERVICE *DhcpSb;
1490 DHCP_PROTOCOL *Instance;
1491 EFI_STATUS Status;
1492
1493 DhcpSb = (DHCP_SERVICE *) Context;
1494 Instance = DhcpSb->ActiveChild;
1495
1496 //
1497 // Check the retransmit timer
1498 //
1499 if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
1500
1501 //
1502 // Select offer at each timeout if any offer received.
1503 //
1504 if (DhcpSb->DhcpState == Dhcp4Selecting && DhcpSb->LastOffer != NULL) {
1505
1506 Status = DhcpChooseOffer (DhcpSb);
1507
1508 if (EFI_ERROR(Status)) {
1509 FreePool (DhcpSb->LastOffer);
1510 DhcpSb->LastOffer = NULL;
1511 } else {
1512 goto ON_EXIT;
1513 }
1514 }
1515
1516 if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
1517 //
1518 // Still has another try
1519 //
1520 DhcpRetransmit (DhcpSb);
1521 DhcpSetTransmitTimer (DhcpSb);
1522
1523 } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
1524
1525 //
1526 // Retransmission failed, if the DHCP request is initiated by
1527 // user, adjust the current state according to the lease life.
1528 // Otherwise do nothing to wait the lease to timeout
1529 //
1530 if (DhcpSb->ExtraRefresh != 0) {
1531 Status = EFI_SUCCESS;
1532
1533 if (DhcpSb->LeaseLife < DhcpSb->T1) {
1534 Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
1535
1536 } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
1537 Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
1538
1539 } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
1540 Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
1541
1542 } else {
1543 goto END_SESSION;
1544
1545 }
1546
1547 DhcpSb->IoStatus = EFI_TIMEOUT;
1548 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
1549 }
1550 } else {
1551 goto END_SESSION;
1552 }
1553 }
1554
1555 //
1556 // If an address has been acquired, check whether need to
1557 // refresh or whether it has expired.
1558 //
1559 if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
1560 DhcpSb->LeaseLife++;
1561
1562 //
1563 // Don't timeout the lease, only count the life if user is
1564 // requesting extra renew/rebind. Adjust the state after that.
1565 //
1566 if (DhcpSb->ExtraRefresh != 0) {
1567 return ;
1568 }
1569
1570 if (DhcpSb->LeaseLife == DhcpSb->Lease) {
1571 //
1572 // Lease expires, end the session
1573 //
1574 goto END_SESSION;
1575
1576 } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
1577 //
1578 // T2 expires, transit to rebinding then send a REQUEST to any server
1579 //
1580 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
1581 goto END_SESSION;
1582 }
1583
1584 Status = DhcpSendMessage (
1585 DhcpSb,
1586 DhcpSb->Selected,
1587 DhcpSb->Para,
1588 DHCP_MSG_REQUEST,
1589 NULL
1590 );
1591
1592 if (EFI_ERROR (Status)) {
1593 goto END_SESSION;
1594 }
1595
1596 } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
1597 //
1598 // T1 expires, transit to renewing, then send a REQUEST to the server
1599 //
1600 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
1601 goto END_SESSION;
1602 }
1603
1604 Status = DhcpSendMessage (
1605 DhcpSb,
1606 DhcpSb->Selected,
1607 DhcpSb->Para,
1608 DHCP_MSG_REQUEST,
1609 NULL
1610 );
1611
1612 if (EFI_ERROR (Status)) {
1613 goto END_SESSION;
1614 }
1615 }
1616 }
1617
1618 ON_EXIT:
1619 if ((Instance != NULL) && (Instance->Token != NULL)) {
1620 Instance->Timeout--;
1621 if (Instance->Timeout == 0) {
1622 PxeDhcpDone (Instance);
1623 }
1624 }
1625
1626 return ;
1627
1628 END_SESSION:
1629 DhcpEndSession (DhcpSb, EFI_TIMEOUT);
1630
1631 return ;
1632 }