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