1 /** @file
2   EFI DHCP protocol implementation.
3 
4 Copyright (c) 2006 - 2014, 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   INTN                      Class;
397 
398   DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);
399 
400   if (DhcpSb->Para != NULL) {
401     DhcpSb->Netmask     = DhcpSb->Para->NetMask;
402     DhcpSb->ServerAddr  = DhcpSb->Para->ServerId;
403   }
404 
405   if (DhcpSb->Netmask == 0) {
406     Class           = NetGetIpClass (DhcpSb->ClientAddr);
407     ASSERT (Class < IP4_ADDR_CLASSE);
408     DhcpSb->Netmask = gIp4AllMasks[Class << 3];
409   }
410 
411   if (DhcpSb->LeaseIoPort != NULL) {
412     UdpIoFreeIo (DhcpSb->LeaseIoPort);
413   }
414 
415   //
416   // Create a UDP/IP child to provide ARP service for the Leased IP,
417   // and transmit unicast packet with it as source address. Don't
418   // start receive on this port, the queued packet will be timeout.
419   //
420   DhcpSb->LeaseIoPort = UdpIoCreateIo (
421                           DhcpSb->Controller,
422                           DhcpSb->Image,
423                           DhcpConfigLeaseIoPort,
424                           UDP_IO_UDP4_VERSION,
425                           DhcpSb
426                           );
427 
428   if (DhcpSb->LeaseIoPort == NULL) {
429     return EFI_OUT_OF_RESOURCES;
430   }
431 
432   if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
433     DhcpComputeLease (DhcpSb, DhcpSb->Para);
434   }
435 
436   return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
437 }
438 
439 
440 /**
441   Clean up the DHCP related states, IoStatus isn't reset.
442 
443   @param  DhcpSb                The DHCP instance service.
444 
445 **/
446 VOID
DhcpCleanLease(IN DHCP_SERVICE * DhcpSb)447 DhcpCleanLease (
448   IN DHCP_SERVICE           *DhcpSb
449   )
450 {
451   DhcpSb->DhcpState   = Dhcp4Init;
452   DhcpSb->Xid         = DhcpSb->Xid + 1;
453   DhcpSb->ClientAddr  = 0;
454   DhcpSb->Netmask     = 0;
455   DhcpSb->ServerAddr  = 0;
456 
457   if (DhcpSb->LastOffer != NULL) {
458     FreePool (DhcpSb->LastOffer);
459     DhcpSb->LastOffer = NULL;
460   }
461 
462   if (DhcpSb->Selected != NULL) {
463     FreePool (DhcpSb->Selected);
464     DhcpSb->Selected = NULL;
465   }
466 
467   if (DhcpSb->Para != NULL) {
468     FreePool (DhcpSb->Para);
469     DhcpSb->Para = NULL;
470   }
471 
472   DhcpSb->Lease         = 0;
473   DhcpSb->T1            = 0;
474   DhcpSb->T2            = 0;
475   DhcpSb->ExtraRefresh  = FALSE;
476 
477   if (DhcpSb->LeaseIoPort != NULL) {
478     UdpIoFreeIo (DhcpSb->LeaseIoPort);
479     DhcpSb->LeaseIoPort = NULL;
480   }
481 
482   if (DhcpSb->LastPacket != NULL) {
483     FreePool (DhcpSb->LastPacket);
484     DhcpSb->LastPacket = NULL;
485   }
486 
487   DhcpSb->PacketToLive  = 0;
488   DhcpSb->LastTimeout   = 0;
489   DhcpSb->CurRetry      = 0;
490   DhcpSb->MaxRetries    = 0;
491   DhcpSb->LeaseLife     = 0;
492 
493   //
494   // Clean active config data.
495   //
496   DhcpCleanConfigure (&DhcpSb->ActiveConfig);
497 }
498 
499 
500 /**
501   Select a offer among all the offers collected. If the offer selected is
502   of BOOTP, the lease is recorded and user notified. If the offer is of
503   DHCP, it will request the offer from the server.
504 
505   @param[in]  DhcpSb                The DHCP service instance.
506 
507   @retval EFI_SUCCESS           One of the offer is selected.
508 
509 **/
510 EFI_STATUS
DhcpChooseOffer(IN DHCP_SERVICE * DhcpSb)511 DhcpChooseOffer (
512   IN DHCP_SERVICE           *DhcpSb
513   )
514 {
515   EFI_DHCP4_PACKET          *Selected;
516   EFI_DHCP4_PACKET          *NewPacket;
517   EFI_DHCP4_PACKET          *TempPacket;
518   EFI_STATUS                Status;
519 
520   ASSERT (DhcpSb->LastOffer != NULL);
521 
522   //
523   // User will cache previous offers if he wants to select
524   // from multiple offers. If user provides an invalid packet,
525   // use the last offer, otherwise use the provided packet.
526   //
527   NewPacket = NULL;
528   Status    = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);
529 
530   if (EFI_ERROR (Status)) {
531     return Status;
532   }
533 
534   Selected = DhcpSb->LastOffer;
535 
536   if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {
537     TempPacket = (EFI_DHCP4_PACKET *) AllocatePool (NewPacket->Size);
538     if (TempPacket != NULL) {
539       CopyMem (TempPacket, NewPacket, NewPacket->Size);
540       FreePool (Selected);
541       Selected = TempPacket;
542     }
543   }
544 
545   DhcpSb->Selected  = Selected;
546   DhcpSb->LastOffer = NULL;
547   DhcpSb->Para      = NULL;
548   DhcpValidateOptions (Selected, &DhcpSb->Para);
549 
550   //
551   // A bootp offer has been selected, save the lease status,
552   // enter bound state then notify the user.
553   //
554   if (DHCP_IS_BOOTP (DhcpSb->Para)) {
555     Status = DhcpLeaseAcquired (DhcpSb);
556 
557     if (EFI_ERROR (Status)) {
558       return Status;
559     }
560 
561     DhcpSb->IoStatus = EFI_SUCCESS;
562     DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
563     return EFI_SUCCESS;
564   }
565 
566   //
567   // Send a DHCP requests
568   //
569   Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);
570 
571   if (EFI_ERROR (Status)) {
572     return Status;
573   }
574 
575   return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);
576 }
577 
578 
579 /**
580   Terminate the current address acquire. All the allocated resources
581   are released. Be careful when calling this function. A rule related
582   to this is: only call DhcpEndSession at the highest level, such as
583   DhcpInput, DhcpOnTimerTick...At the other level, just return error.
584 
585   @param[in]  DhcpSb                The DHCP service instance
586   @param[in]  Status                The result of the DHCP process.
587 
588 **/
589 VOID
DhcpEndSession(IN DHCP_SERVICE * DhcpSb,IN EFI_STATUS Status)590 DhcpEndSession (
591   IN DHCP_SERVICE           *DhcpSb,
592   IN EFI_STATUS             Status
593   )
594 {
595   if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
596     DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);
597   } else {
598     DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);
599   }
600 
601   DhcpCleanLease (DhcpSb);
602 
603   DhcpSb->IoStatus = Status;
604   DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
605 }
606 
607 
608 /**
609   Handle packets in DHCP select state.
610 
611   @param[in]  DhcpSb                The DHCP service instance
612   @param[in]  Packet                The DHCP packet received
613   @param[in]  Para                  The DHCP parameter extracted from the packet. That
614                                     is, all the option value that we care.
615 
616   @retval EFI_SUCCESS           The packet is successfully processed.
617   @retval Others                Some error occured.
618 
619 **/
620 EFI_STATUS
DhcpHandleSelect(IN DHCP_SERVICE * DhcpSb,IN EFI_DHCP4_PACKET * Packet,IN DHCP_PARAMETER * Para)621 DhcpHandleSelect (
622   IN DHCP_SERVICE           *DhcpSb,
623   IN EFI_DHCP4_PACKET       *Packet,
624   IN DHCP_PARAMETER         *Para
625   )
626 {
627   EFI_STATUS                Status;
628 
629   Status = EFI_SUCCESS;
630 
631   //
632   // First validate the message:
633   // 1. the offer is a unicast
634   // 2. if it is a DHCP message, it must contains a server ID.
635   // Don't return a error for these two case otherwise the session is ended.
636   //
637   if (!DHCP_IS_BOOTP (Para) &&
638       ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))
639       ) {
640     goto ON_EXIT;
641   }
642 
643   //
644   // Call the user's callback. The action according to the return is as:
645   // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
646   // 2. EFI_NOT_READY: wait for more offers
647   // 3. EFI_ABORTED: abort the address acquiring.
648   //
649   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
650 
651   if (Status == EFI_SUCCESS) {
652     if (DhcpSb->LastOffer != NULL) {
653       FreePool (DhcpSb->LastOffer);
654     }
655 
656     DhcpSb->LastOffer = Packet;
657 
658     return DhcpChooseOffer (DhcpSb);
659 
660   } else if (Status == EFI_NOT_READY) {
661     if (DhcpSb->LastOffer != NULL) {
662       FreePool (DhcpSb->LastOffer);
663     }
664 
665     DhcpSb->LastOffer = Packet;
666 
667   } else if (Status == EFI_ABORTED) {
668     //
669     // DhcpInput will end the session upon error return. Remember
670     // only to call DhcpEndSession at the top level call.
671     //
672     goto ON_EXIT;
673   }
674 
675   return EFI_SUCCESS;
676 
677 ON_EXIT:
678   FreePool (Packet);
679   return Status;
680 }
681 
682 
683 /**
684   Handle packets in DHCP request state.
685 
686   @param[in]  DhcpSb                The DHCP service instance
687   @param[in]  Packet                The DHCP packet received
688   @param[in]  Para                  The DHCP parameter extracted from the packet. That
689                                     is, all the option value that we care.
690 
691   @retval EFI_SUCCESS           The packet is successfully processed.
692   @retval Others                Some error occured.
693 
694 **/
695 EFI_STATUS
DhcpHandleRequest(IN DHCP_SERVICE * DhcpSb,IN EFI_DHCP4_PACKET * Packet,IN DHCP_PARAMETER * Para)696 DhcpHandleRequest (
697   IN DHCP_SERVICE           *DhcpSb,
698   IN EFI_DHCP4_PACKET       *Packet,
699   IN DHCP_PARAMETER         *Para
700   )
701 {
702   EFI_DHCP4_HEADER          *Head;
703   EFI_DHCP4_HEADER          *Selected;
704   EFI_STATUS                Status;
705   UINT8                     *Message;
706 
707   ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
708 
709   Head      = &Packet->Dhcp4.Header;
710   Selected  = &DhcpSb->Selected->Dhcp4.Header;
711 
712   //
713   // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
714   //
715   if (DHCP_IS_BOOTP (Para) ||
716       (Para->ServerId != DhcpSb->Para->ServerId) ||
717       ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
718       ) {
719 
720     Status = EFI_SUCCESS;
721     goto ON_EXIT;
722   }
723 
724   //
725   // Received a NAK, end the session no matter what the user returns
726   //
727   Status = EFI_DEVICE_ERROR;
728 
729   if (Para->DhcpType == DHCP_MSG_NAK) {
730     DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
731     goto ON_EXIT;
732   }
733 
734   //
735   // Check whether the ACK matches the selected offer
736   //
737   Message = NULL;
738 
739   if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
740     Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";
741     goto REJECT;
742   }
743 
744   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
745 
746   if (EFI_ERROR (Status)) {
747     Message = (UINT8 *) "Lease is denied upon received ACK";
748     goto REJECT;
749   }
750 
751   //
752   // Record the lease, transit to BOUND state, then notify the user
753   //
754   Status = DhcpLeaseAcquired (DhcpSb);
755 
756   if (EFI_ERROR (Status)) {
757     Message = (UINT8 *) "Lease is denied upon entering bound";
758     goto REJECT;
759   }
760 
761   DhcpSb->IoStatus = EFI_SUCCESS;
762   DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
763 
764   FreePool (Packet);
765   return EFI_SUCCESS;
766 
767 REJECT:
768   DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
769 
770 ON_EXIT:
771   FreePool (Packet);
772   return Status;
773 }
774 
775 
776 /**
777   Handle packets in DHCP renew/rebound state.
778 
779   @param[in]  DhcpSb                The DHCP service instance
780   @param[in]  Packet                The DHCP packet received
781   @param[in]  Para                  The DHCP parameter extracted from the packet. That
782                                     is, all the option value that we care.
783 
784   @retval EFI_SUCCESS           The packet is successfully processed.
785   @retval Others                Some error occured.
786 
787 **/
788 EFI_STATUS
DhcpHandleRenewRebind(IN DHCP_SERVICE * DhcpSb,IN EFI_DHCP4_PACKET * Packet,IN DHCP_PARAMETER * Para)789 DhcpHandleRenewRebind (
790   IN DHCP_SERVICE           *DhcpSb,
791   IN EFI_DHCP4_PACKET       *Packet,
792   IN DHCP_PARAMETER         *Para
793   )
794 {
795   EFI_DHCP4_HEADER          *Head;
796   EFI_DHCP4_HEADER          *Selected;
797   EFI_STATUS                Status;
798 
799   ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
800 
801   Head      = &Packet->Dhcp4.Header;
802   Selected  = &DhcpSb->Selected->Dhcp4.Header;
803 
804   //
805   // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
806   //
807   if (DHCP_IS_BOOTP (Para) ||
808       (Para->ServerId != DhcpSb->Para->ServerId) ||
809       ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
810       ) {
811 
812     Status = EFI_SUCCESS;
813     goto ON_EXIT;
814   }
815 
816   //
817   // Received a NAK, ignore the user's return then terminate the process
818   //
819   Status = EFI_DEVICE_ERROR;
820 
821   if (Para->DhcpType == DHCP_MSG_NAK) {
822     DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
823     goto ON_EXIT;
824   }
825 
826   //
827   // The lease is different from the selected. Don't send a DECLINE
828   // since it isn't existed in the client's FSM.
829   //
830   if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
831     goto ON_EXIT;
832   }
833 
834   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
835 
836   if (EFI_ERROR (Status)) {
837     goto ON_EXIT;
838   }
839 
840   //
841   // Record the lease, start timer for T1 and T2,
842   //
843   DhcpComputeLease (DhcpSb, Para);
844   DhcpSb->LeaseLife = 0;
845   DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
846 
847   if (DhcpSb->ExtraRefresh != 0) {
848     DhcpSb->ExtraRefresh  = FALSE;
849 
850     DhcpSb->IoStatus      = EFI_SUCCESS;
851     DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
852   }
853 
854 ON_EXIT:
855   FreePool (Packet);
856   return Status;
857 }
858 
859 
860 /**
861   Handle packets in DHCP reboot state.
862 
863   @param[in]  DhcpSb                The DHCP service instance
864   @param[in]  Packet                The DHCP packet received
865   @param[in]  Para                  The DHCP parameter extracted from the packet. That
866                                     is, all the option value that we care.
867 
868   @retval EFI_SUCCESS           The packet is successfully processed.
869   @retval Others                Some error occured.
870 
871 **/
872 EFI_STATUS
DhcpHandleReboot(IN DHCP_SERVICE * DhcpSb,IN EFI_DHCP4_PACKET * Packet,IN DHCP_PARAMETER * Para)873 DhcpHandleReboot (
874   IN DHCP_SERVICE           *DhcpSb,
875   IN EFI_DHCP4_PACKET       *Packet,
876   IN DHCP_PARAMETER         *Para
877   )
878 {
879   EFI_DHCP4_HEADER          *Head;
880   EFI_STATUS                Status;
881 
882   Head = &Packet->Dhcp4.Header;
883 
884   //
885   // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
886   //
887   if (DHCP_IS_BOOTP (Para) ||
888       ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
889       ) {
890 
891     Status = EFI_SUCCESS;
892     goto ON_EXIT;
893   }
894 
895   //
896   // If a NAK is received, transit to INIT and try again.
897   //
898   if (Para->DhcpType == DHCP_MSG_NAK) {
899     DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
900 
901     DhcpSb->ClientAddr  = 0;
902     DhcpSb->DhcpState   = Dhcp4Init;
903 
904     Status              = DhcpInitRequest (DhcpSb);
905     goto ON_EXIT;
906   }
907 
908   //
909   // Check whether the ACK matches the selected offer
910   //
911   if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
912     Status = EFI_DEVICE_ERROR;
913     goto ON_EXIT;
914   }
915 
916   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
917   if (EFI_ERROR (Status)) {
918     goto ON_EXIT;
919   }
920 
921   //
922   // OK, get the parameter from server, record the lease
923   //
924   DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);
925   if (DhcpSb->Para == NULL) {
926     Status = EFI_OUT_OF_RESOURCES;
927     goto ON_EXIT;
928   }
929 
930   DhcpSb->Selected  = Packet;
931   Status            = DhcpLeaseAcquired (DhcpSb);
932   if (EFI_ERROR (Status)) {
933     return Status;
934   }
935 
936   DhcpSb->IoStatus = EFI_SUCCESS;
937   DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
938   return EFI_SUCCESS;
939 
940 ON_EXIT:
941   FreePool (Packet);
942   return Status;
943 }
944 
945 
946 /**
947   Handle the received DHCP packets. This function drives the DHCP
948   state machine.
949 
950   @param  UdpPacket             The UDP packets received.
951   @param  EndPoint              The local/remote UDP access point
952   @param  IoStatus              The status of the UDP receive
953   @param  Context               The opaque parameter to the function.
954 
955 **/
956 VOID
957 EFIAPI
DhcpInput(NET_BUF * UdpPacket,UDP_END_POINT * EndPoint,EFI_STATUS IoStatus,VOID * Context)958 DhcpInput (
959   NET_BUF                   *UdpPacket,
960   UDP_END_POINT             *EndPoint,
961   EFI_STATUS                IoStatus,
962   VOID                      *Context
963   )
964 {
965   DHCP_SERVICE              *DhcpSb;
966   EFI_DHCP4_HEADER          *Head;
967   EFI_DHCP4_PACKET          *Packet;
968   DHCP_PARAMETER            *Para;
969   EFI_STATUS                Status;
970   UINT32                    Len;
971 
972   Packet  = NULL;
973   DhcpSb  = (DHCP_SERVICE *) Context;
974 
975   //
976   // Don't restart receive if error occurs or DHCP is destroyed.
977   //
978   if (EFI_ERROR (IoStatus)) {
979     return ;
980   } else if (DhcpSb->ServiceState == DHCP_DESTROY) {
981     NetbufFree (UdpPacket);
982     return ;
983   }
984 
985   ASSERT (UdpPacket != NULL);
986 
987   if (DhcpSb->DhcpState == Dhcp4Stopped) {
988     goto RESTART;
989   }
990 
991   //
992   // Validate the packet received
993   //
994   if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
995     goto RESTART;
996   }
997 
998   //
999   // Copy the DHCP message to a continuous memory block
1000   //
1001   Len     = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
1002   Packet  = (EFI_DHCP4_PACKET *) AllocatePool (Len);
1003 
1004   if (Packet == NULL) {
1005     goto RESTART;
1006   }
1007 
1008   Packet->Size    = Len;
1009   Head            = &Packet->Dhcp4.Header;
1010   Packet->Length  = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
1011 
1012   if (Packet->Length != UdpPacket->TotalSize) {
1013     goto RESTART;
1014   }
1015 
1016   //
1017   // Is this packet the answer to our packet?
1018   //
1019   if ((Head->OpCode != BOOTP_REPLY) ||
1020       (NTOHL (Head->Xid) != DhcpSb->Xid) ||
1021       (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
1022     goto RESTART;
1023   }
1024 
1025   //
1026   // Validate the options and retrieve the interested options
1027   //
1028   Para = NULL;
1029   if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
1030       (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
1031       EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {
1032 
1033     goto RESTART;
1034   }
1035 
1036   //
1037   // Call the handler for each state. The handler should return
1038   // EFI_SUCCESS if the process can go on no matter whether the
1039   // packet is ignored or not. If the return is EFI_ERROR, the
1040   // session will be terminated. Packet's ownership is handled
1041   // over to the handlers. If operation succeeds, the handler
1042   // must notify the user. It isn't necessary to do if EFI_ERROR
1043   // is returned because the DhcpEndSession will notify the user.
1044   //
1045   Status = EFI_SUCCESS;
1046 
1047   switch (DhcpSb->DhcpState) {
1048   case Dhcp4Selecting:
1049     Status = DhcpHandleSelect (DhcpSb, Packet, Para);
1050     break;
1051 
1052   case Dhcp4Requesting:
1053     Status = DhcpHandleRequest (DhcpSb, Packet, Para);
1054     break;
1055 
1056   case Dhcp4InitReboot:
1057   case Dhcp4Init:
1058   case Dhcp4Bound:
1059     //
1060     // Ignore the packet in INITREBOOT, INIT and BOUND states
1061     //
1062     FreePool (Packet);
1063     Status = EFI_SUCCESS;
1064     break;
1065 
1066   case Dhcp4Renewing:
1067   case Dhcp4Rebinding:
1068     Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
1069     break;
1070 
1071   case Dhcp4Rebooting:
1072     Status = DhcpHandleReboot (DhcpSb, Packet, Para);
1073     break;
1074   }
1075 
1076   if (Para != NULL) {
1077     FreePool (Para);
1078   }
1079 
1080   Packet = NULL;
1081 
1082   if (EFI_ERROR (Status)) {
1083     NetbufFree (UdpPacket);
1084     UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
1085     DhcpEndSession (DhcpSb, Status);
1086     return ;
1087   }
1088 
1089 RESTART:
1090   NetbufFree (UdpPacket);
1091 
1092   if (Packet != NULL) {
1093     FreePool (Packet);
1094   }
1095 
1096   Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
1097 
1098   if (EFI_ERROR (Status)) {
1099     DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);
1100   }
1101 }
1102 
1103 
1104 /**
1105   Release the packet.
1106 
1107   @param[in]  Arg                   The packet to release
1108 
1109 **/
1110 VOID
1111 EFIAPI
DhcpReleasePacket(IN VOID * Arg)1112 DhcpReleasePacket (
1113   IN VOID                   *Arg
1114   )
1115 {
1116   FreePool (Arg);
1117 }
1118 
1119 
1120 /**
1121   Release the net buffer when packet is sent.
1122 
1123   @param  UdpPacket             The UDP packets received.
1124   @param  EndPoint              The local/remote UDP access point
1125   @param  IoStatus              The status of the UDP receive
1126   @param  Context               The opaque parameter to the function.
1127 
1128 **/
1129 VOID
1130 EFIAPI
DhcpOnPacketSent(NET_BUF * Packet,UDP_END_POINT * EndPoint,EFI_STATUS IoStatus,VOID * Context)1131 DhcpOnPacketSent (
1132   NET_BUF                   *Packet,
1133   UDP_END_POINT             *EndPoint,
1134   EFI_STATUS                IoStatus,
1135   VOID                      *Context
1136   )
1137 {
1138   NetbufFree (Packet);
1139 }
1140 
1141 
1142 
1143 /**
1144   Build and transmit a DHCP message according to the current states.
1145   This function implement the Table 5. of RFC 2131. Always transits
1146   the state (as defined in Figure 5. of the same RFC) before sending
1147   a DHCP message. The table is adjusted accordingly.
1148 
1149   @param[in]  DhcpSb                The DHCP service instance
1150   @param[in]  Seed                  The seed packet which the new packet is based on
1151   @param[in]  Para                  The DHCP parameter of the Seed packet
1152   @param[in]  Type                  The message type to send
1153   @param[in]  Msg                   The human readable message to include in the packet
1154                                     sent.
1155 
1156   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources for the packet
1157   @retval EFI_ACCESS_DENIED     Failed to transmit the packet through UDP
1158   @retval EFI_SUCCESS           The message is sent
1159   @retval other                 Other error occurs
1160 
1161 **/
1162 EFI_STATUS
DhcpSendMessage(IN DHCP_SERVICE * DhcpSb,IN EFI_DHCP4_PACKET * Seed,IN DHCP_PARAMETER * Para,IN UINT8 Type,IN UINT8 * Msg)1163 DhcpSendMessage (
1164   IN DHCP_SERVICE           *DhcpSb,
1165   IN EFI_DHCP4_PACKET       *Seed,
1166   IN DHCP_PARAMETER         *Para,
1167   IN UINT8                  Type,
1168   IN UINT8                  *Msg
1169   )
1170 {
1171   EFI_DHCP4_CONFIG_DATA     *Config;
1172   EFI_DHCP4_PACKET          *Packet;
1173   EFI_DHCP4_PACKET          *NewPacket;
1174   EFI_DHCP4_HEADER          *Head;
1175   EFI_DHCP4_HEADER          *SeedHead;
1176   UDP_IO                    *UdpIo;
1177   UDP_END_POINT             EndPoint;
1178   NET_BUF                   *Wrap;
1179   NET_FRAGMENT              Frag;
1180   EFI_STATUS                Status;
1181   IP4_ADDR                  IpAddr;
1182   UINT8                     *Buf;
1183   UINT16                    MaxMsg;
1184   UINT32                    Len;
1185   UINT32                    Index;
1186 
1187   //
1188   // Allocate a big enough memory block to hold the DHCP packet
1189   //
1190   Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;
1191 
1192   if (Msg != NULL) {
1193     Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg);
1194   }
1195 
1196   Packet = AllocatePool (Len);
1197 
1198   if (Packet == NULL) {
1199     return EFI_OUT_OF_RESOURCES;
1200   }
1201 
1202   Packet->Size    = Len;
1203   Packet->Length  = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);
1204 
1205   //
1206   // Fill in the DHCP header fields
1207   //
1208   Config    = &DhcpSb->ActiveConfig;
1209   SeedHead  = NULL;
1210 
1211   if (Seed != NULL) {
1212     SeedHead = &Seed->Dhcp4.Header;
1213   }
1214 
1215   Head = &Packet->Dhcp4.Header;
1216   ZeroMem (Head, sizeof (EFI_DHCP4_HEADER));
1217 
1218   Head->OpCode       = BOOTP_REQUEST;
1219   Head->HwType       = DhcpSb->HwType;
1220   Head->HwAddrLen    = DhcpSb->HwLen;
1221   Head->Xid          = HTONL (DhcpSb->Xid);
1222   Head->Reserved     = HTONS (0x8000);  //Server, broadcast the message please.
1223 
1224   EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);
1225   CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);
1226 
1227   if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) {
1228     Head->Seconds = 0;
1229   } else if ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) {
1230     //
1231     // Use the same value as the original DHCPDISCOVER message.
1232     //
1233     Head->Seconds = DhcpSb->LastPacket->Dhcp4.Header.Seconds;
1234   } else {
1235     SetElapsedTime(&Head->Seconds, DhcpSb->ActiveChild);
1236   }
1237 
1238   //
1239   // Append the DHCP message type
1240   //
1241   Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
1242   Buf                 = Packet->Dhcp4.Option;
1243   Buf                 = DhcpAppendOption (Buf, DHCP_TAG_TYPE, 1, &Type);
1244 
1245   //
1246   // Append the serverid option if necessary:
1247   //   1. DHCP decline message
1248   //   2. DHCP release message
1249   //   3. DHCP request to confirm one lease.
1250   //
1251   if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||
1252       ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))
1253       ) {
1254 
1255     ASSERT ((Para != NULL) && (Para->ServerId != 0));
1256 
1257     IpAddr  = HTONL (Para->ServerId);
1258     Buf     = DhcpAppendOption (Buf, DHCP_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);
1259   }
1260 
1261   //
1262   // Append the requested IP option if necessary:
1263   //   1. DHCP request to use the previously allocated address
1264   //   2. DHCP request to confirm one lease
1265   //   3. DHCP decline to decline one lease
1266   //
1267   IpAddr = 0;
1268 
1269   if (Type == DHCP_MSG_REQUEST) {
1270     if (DhcpSb->DhcpState == Dhcp4Rebooting) {
1271       IpAddr = EFI_IP4 (Config->ClientAddress);
1272 
1273     } else if (DhcpSb->DhcpState == Dhcp4Requesting) {
1274       ASSERT (SeedHead != NULL);
1275       IpAddr = EFI_IP4 (SeedHead->YourAddr);
1276     }
1277 
1278   } else if (Type == DHCP_MSG_DECLINE) {
1279     ASSERT (SeedHead != NULL);
1280     IpAddr = EFI_IP4 (SeedHead->YourAddr);
1281   }
1282 
1283   if (IpAddr != 0) {
1284     Buf = DhcpAppendOption (Buf, DHCP_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);
1285   }
1286 
1287   //
1288   // Append the Max Message Length option if it isn't a DECLINE
1289   // or RELEASE to direct the server use large messages instead of
1290   // override the BOOTFILE and SERVER fields in the message head.
1291   //
1292   if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {
1293     MaxMsg  = HTONS (0xFF00);
1294     Buf     = DhcpAppendOption (Buf, DHCP_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);
1295   }
1296 
1297   //
1298   // Append the user's message if it isn't NULL
1299   //
1300   if (Msg != NULL) {
1301     Len     = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255);
1302     Buf     = DhcpAppendOption (Buf, DHCP_TAG_MESSAGE, (UINT16) Len, Msg);
1303   }
1304 
1305   //
1306   // Append the user configured options
1307   //
1308   if (DhcpSb->UserOptionLen != 0) {
1309     for (Index = 0; Index < Config->OptionCount; Index++) {
1310       //
1311       // We can't use any option other than the client ID from user
1312       // if it is a DHCP decline or DHCP release .
1313       //
1314       if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&
1315           (Config->OptionList[Index]->OpCode != DHCP_TAG_CLIENT_ID)) {
1316         continue;
1317       }
1318 
1319       Buf = DhcpAppendOption (
1320               Buf,
1321               Config->OptionList[Index]->OpCode,
1322               Config->OptionList[Index]->Length,
1323               Config->OptionList[Index]->Data
1324               );
1325     }
1326   }
1327 
1328   *(Buf++) = DHCP_TAG_EOP;
1329   Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);
1330 
1331   //
1332   // OK, the message is built, call the user to override it.
1333   //
1334   Status    = EFI_SUCCESS;
1335   NewPacket = NULL;
1336 
1337   if (Type == DHCP_MSG_DISCOVER) {
1338     Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);
1339 
1340   } else if (Type == DHCP_MSG_REQUEST) {
1341     Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);
1342 
1343   } else if (Type == DHCP_MSG_DECLINE) {
1344     Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);
1345   }
1346 
1347   if (EFI_ERROR (Status)) {
1348     FreePool (Packet);
1349     return Status;
1350   }
1351 
1352   if (NewPacket != NULL) {
1353     FreePool (Packet);
1354     Packet = NewPacket;
1355   }
1356 
1357   //
1358   // Save the Client Address will be sent out
1359   //
1360   CopyMem (
1361     &DhcpSb->ClientAddressSendOut[0],
1362     &Packet->Dhcp4.Header.ClientHwAddr[0],
1363     Packet->Dhcp4.Header.HwAddrLen
1364     );
1365 
1366 
1367   //
1368   // Wrap it into a netbuf then send it.
1369   //
1370   Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;
1371   Frag.Len  = Packet->Length;
1372   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet);
1373 
1374   if (Wrap == NULL) {
1375     FreePool (Packet);
1376     return EFI_OUT_OF_RESOURCES;
1377   }
1378 
1379   //
1380   // Save it as the last sent packet for retransmission
1381   //
1382   if (DhcpSb->LastPacket != NULL) {
1383     FreePool (DhcpSb->LastPacket);
1384   }
1385 
1386   DhcpSb->LastPacket = Packet;
1387   DhcpSetTransmitTimer (DhcpSb);
1388 
1389   //
1390   // Broadcast the message, unless we know the server address.
1391   // Use the lease UdpIo port to send the unicast packet.
1392   //
1393   EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
1394   EndPoint.LocalAddr.Addr[0]  = 0;
1395   EndPoint.RemotePort         = DHCP_SERVER_PORT;
1396   EndPoint.LocalPort          = DHCP_CLIENT_PORT;
1397   UdpIo                       = DhcpSb->UdpIo;
1398 
1399   if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
1400     EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
1401     EndPoint.LocalAddr.Addr[0]  = DhcpSb->ClientAddr;
1402     UdpIo                       = DhcpSb->LeaseIoPort;
1403   }
1404 
1405   ASSERT (UdpIo != NULL);
1406   NET_GET_REF (Wrap);
1407 
1408   Status = UdpIoSendDatagram (
1409              UdpIo,
1410              Wrap,
1411              &EndPoint,
1412              NULL,
1413              DhcpOnPacketSent,
1414              DhcpSb
1415              );
1416 
1417   if (EFI_ERROR (Status)) {
1418     NET_PUT_REF (Wrap);
1419     return EFI_ACCESS_DENIED;
1420   }
1421 
1422   return EFI_SUCCESS;
1423 }
1424 
1425 
1426 /**
1427   Retransmit a saved packet. Only DISCOVER and REQUEST messages
1428   will be retransmitted.
1429 
1430   @param[in]  DhcpSb                The DHCP service instance
1431 
1432   @retval EFI_ACCESS_DENIED     Failed to transmit packet through UDP port
1433   @retval EFI_SUCCESS           The packet is retransmitted.
1434 
1435 **/
1436 EFI_STATUS
DhcpRetransmit(IN DHCP_SERVICE * DhcpSb)1437 DhcpRetransmit (
1438   IN DHCP_SERVICE           *DhcpSb
1439   )
1440 {
1441   UDP_IO                    *UdpIo;
1442   UDP_END_POINT             EndPoint;
1443   NET_BUF                   *Wrap;
1444   NET_FRAGMENT              Frag;
1445   EFI_STATUS                Status;
1446 
1447   ASSERT (DhcpSb->LastPacket != NULL);
1448 
1449   //
1450   // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
1451   //
1452   if (DhcpSb->DhcpState != Dhcp4Requesting) {
1453     SetElapsedTime(&DhcpSb->LastPacket->Dhcp4.Header.Seconds, DhcpSb->ActiveChild);
1454   }
1455 
1456   //
1457   // Wrap it into a netbuf then send it.
1458   //
1459   Frag.Bulk = (UINT8 *) &DhcpSb->LastPacket->Dhcp4.Header;
1460   Frag.Len  = DhcpSb->LastPacket->Length;
1461   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, DhcpSb->LastPacket);
1462 
1463   if (Wrap == NULL) {
1464     return EFI_OUT_OF_RESOURCES;
1465   }
1466 
1467   //
1468   // Broadcast the message, unless we know the server address.
1469   //
1470   EndPoint.RemotePort         = DHCP_SERVER_PORT;
1471   EndPoint.LocalPort          = DHCP_CLIENT_PORT;
1472   EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
1473   EndPoint.LocalAddr.Addr[0]  = 0;
1474   UdpIo                       = DhcpSb->UdpIo;
1475 
1476   if (DhcpSb->DhcpState == Dhcp4Renewing) {
1477     EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
1478     EndPoint.LocalAddr.Addr[0]  = DhcpSb->ClientAddr;
1479     UdpIo                       = DhcpSb->LeaseIoPort;
1480   }
1481 
1482   ASSERT (UdpIo != NULL);
1483 
1484   NET_GET_REF (Wrap);
1485   Status = UdpIoSendDatagram (
1486              UdpIo,
1487              Wrap,
1488              &EndPoint,
1489              NULL,
1490              DhcpOnPacketSent,
1491              DhcpSb
1492              );
1493 
1494   if (EFI_ERROR (Status)) {
1495     NET_PUT_REF (Wrap);
1496     return EFI_ACCESS_DENIED;
1497   }
1498 
1499   return EFI_SUCCESS;
1500 }
1501 
1502 
1503 /**
1504   Each DHCP service has three timer. Two of them are count down timer.
1505   One for the packet retransmission. The other is to collect the offers.
1506   The third timer increaments the lease life which is compared to T1, T2,
1507   and lease to determine the time to renew and rebind the lease.
1508   DhcpOnTimerTick will be called once every second.
1509 
1510   @param[in]  Event                 The timer event
1511   @param[in]  Context               The context, which is the DHCP service instance.
1512 
1513 **/
1514 VOID
1515 EFIAPI
DhcpOnTimerTick(IN EFI_EVENT Event,IN VOID * Context)1516 DhcpOnTimerTick (
1517   IN EFI_EVENT              Event,
1518   IN VOID                   *Context
1519   )
1520 {
1521   LIST_ENTRY                *Entry;
1522   LIST_ENTRY                *Next;
1523   DHCP_SERVICE              *DhcpSb;
1524   DHCP_PROTOCOL             *Instance;
1525   EFI_STATUS                Status;
1526 
1527   DhcpSb   = (DHCP_SERVICE *) Context;
1528   Instance = DhcpSb->ActiveChild;
1529 
1530   //
1531   // 0xffff is the maximum supported value for elapsed time according to RFC.
1532   //
1533   if (Instance != NULL && Instance->ElaspedTime < 0xffff) {
1534     Instance->ElaspedTime++;
1535   }
1536 
1537   //
1538   // Check the retransmit timer
1539   //
1540   if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
1541 
1542     //
1543     // Select offer at each timeout if any offer received.
1544     //
1545     if (DhcpSb->DhcpState == Dhcp4Selecting && DhcpSb->LastOffer != NULL) {
1546 
1547       Status = DhcpChooseOffer (DhcpSb);
1548 
1549       if (EFI_ERROR(Status)) {
1550         if (DhcpSb->LastOffer != NULL) {
1551           FreePool (DhcpSb->LastOffer);
1552           DhcpSb->LastOffer = NULL;
1553         }
1554       } else {
1555         goto ON_EXIT;
1556       }
1557     }
1558 
1559     if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
1560       //
1561       // Still has another try
1562       //
1563       DhcpRetransmit (DhcpSb);
1564       DhcpSetTransmitTimer (DhcpSb);
1565 
1566     } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
1567 
1568       //
1569       // Retransmission failed, if the DHCP request is initiated by
1570       // user, adjust the current state according to the lease life.
1571       // Otherwise do nothing to wait the lease to timeout
1572       //
1573       if (DhcpSb->ExtraRefresh != 0) {
1574         Status = EFI_SUCCESS;
1575 
1576         if (DhcpSb->LeaseLife < DhcpSb->T1) {
1577           Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
1578 
1579         } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
1580           Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
1581 
1582         } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
1583           Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
1584 
1585         } else {
1586           goto END_SESSION;
1587 
1588         }
1589 
1590         DhcpSb->IoStatus = EFI_TIMEOUT;
1591         DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
1592       }
1593     } else {
1594       goto END_SESSION;
1595     }
1596   }
1597 
1598   //
1599   // If an address has been acquired, check whether need to
1600   // refresh or whether it has expired.
1601   //
1602   if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
1603     DhcpSb->LeaseLife++;
1604 
1605     //
1606     // Don't timeout the lease, only count the life if user is
1607     // requesting extra renew/rebind. Adjust the state after that.
1608     //
1609     if (DhcpSb->ExtraRefresh != 0) {
1610       return ;
1611     }
1612 
1613     if (DhcpSb->LeaseLife == DhcpSb->Lease) {
1614       //
1615       // Lease expires, end the session
1616       //
1617       goto END_SESSION;
1618 
1619     } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
1620       //
1621       // T2 expires, transit to rebinding then send a REQUEST to any server
1622       //
1623       if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
1624         goto END_SESSION;
1625       }
1626 
1627       if (Instance != NULL) {
1628         Instance->ElaspedTime= 0;
1629       }
1630 
1631       Status = DhcpSendMessage (
1632                  DhcpSb,
1633                  DhcpSb->Selected,
1634                  DhcpSb->Para,
1635                  DHCP_MSG_REQUEST,
1636                  NULL
1637                  );
1638 
1639       if (EFI_ERROR (Status)) {
1640         goto END_SESSION;
1641       }
1642 
1643     } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
1644       //
1645       // T1 expires, transit to renewing, then send a REQUEST to the server
1646       //
1647       if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
1648         goto END_SESSION;
1649       }
1650 
1651       if (Instance != NULL) {
1652         Instance->ElaspedTime= 0;
1653       }
1654 
1655       Status = DhcpSendMessage (
1656                  DhcpSb,
1657                  DhcpSb->Selected,
1658                  DhcpSb->Para,
1659                  DHCP_MSG_REQUEST,
1660                  NULL
1661                  );
1662 
1663       if (EFI_ERROR (Status)) {
1664         goto END_SESSION;
1665       }
1666     }
1667   }
1668 
1669 ON_EXIT:
1670   //
1671   // Iterate through all the DhcpSb Children.
1672   //
1673   NET_LIST_FOR_EACH_SAFE (Entry, Next, &DhcpSb->Children) {
1674     Instance = NET_LIST_USER_STRUCT (Entry, DHCP_PROTOCOL, Link);
1675 
1676     if ((Instance != NULL) && (Instance->Token != NULL)) {
1677       Instance->Timeout--;
1678       if (Instance->Timeout == 0) {
1679         PxeDhcpDone (Instance);
1680       }
1681     }
1682   }
1683 
1684   return ;
1685 
1686 END_SESSION:
1687   DhcpEndSession (DhcpSb, EFI_TIMEOUT);
1688 
1689   return ;
1690 }
1691