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