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