1 /** @file
2   This file implement the EFI_DHCP4_PROTOCOL interface.
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 /**
19   Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver.
20 
21   The GetModeData() function returns the current operating mode and cached data
22   packet for the EFI DHCPv4 Protocol driver.
23 
24   @param[in]  This          Pointer to the EFI_DHCP4_PROTOCOL instance.
25   @param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure.
26 
27   @retval EFI_SUCCESS           The mode data was returned.
28   @retval EFI_INVALID_PARAMETER This is NULL.
29 
30 **/
31 EFI_STATUS
32 EFIAPI
33 EfiDhcp4GetModeData (
34   IN  EFI_DHCP4_PROTOCOL    *This,
35   OUT EFI_DHCP4_MODE_DATA   *Dhcp4ModeData
36   );
37 
38 /**
39   Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver.
40 
41   The Configure() function is used to initialize, change, or reset the operational
42   settings of the EFI DHCPv4 Protocol driver for the communication device on which
43   the EFI DHCPv4 Service Binding Protocol is installed. This function can be
44   successfully called only if both of the following are true:
45   * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init,
46     Dhcp4InitReboot, or Dhcp4Bound states.
47   * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI
48     DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4
49     Protocol driver.
50   When this driver is in the Dhcp4Stopped state, it can transfer into one of the
51   following two possible initial states:
52   * Dhcp4Init
53   * Dhcp4InitReboot
54   The driver can transfer into these states by calling Configure() with a non-NULL
55   Dhcp4CfgData. The driver will transfer into the appropriate state based on the
56   supplied client network address in the ClientAddress parameter and DHCP options
57   in the OptionList parameter as described in RFC 2131.
58   When Configure() is called successfully while Dhcp4CfgData is set to NULL, the
59   default configuring data will be reset in the EFI DHCPv4 Protocol driver and
60   the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance
61   wants to make it possible for another instance to configure the EFI DHCPv4 Protocol
62   driver, it must call this function with Dhcp4CfgData set to NULL.
63 
64   @param[in]  This                   Pointer to the EFI_DHCP4_PROTOCOL instance.
65   @param[in]  Dhcp4CfgData           Pointer to the EFI_DHCP4_CONFIG_DATA.
66 
67   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or
68                                 Dhcp4InitReboot state, if the original state of this driver
69                                 was Dhcp4Stopped and the value of Dhcp4CfgData was
70                                 not NULL. Otherwise, the state was left unchanged.
71   @retval EFI_ACCESS_DENIED     This instance of the EFI DHCPv4 Protocol driver was not in the
72                                 Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state;
73                                 Or onother instance of this EFI DHCPv4 Protocol driver is already
74                                 in a valid configured state.
75   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
76   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
77   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
78 
79 **/
80 EFI_STATUS
81 EFIAPI
82 EfiDhcp4Configure (
83   IN EFI_DHCP4_PROTOCOL     *This,
84   IN EFI_DHCP4_CONFIG_DATA  *Dhcp4CfgData       OPTIONAL
85   );
86 
87 /**
88   Starts the DHCP configuration process.
89 
90   The Start() function starts the DHCP configuration process. This function can
91   be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
92   Dhcp4InitReboot state.
93   If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
94   driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
95   Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
96   If the process aborts, either by the user or by some unexpected network error,
97   the state is restored to the Dhcp4Init state. The Start() function can be called
98   again to restart the process.
99   Refer to RFC 2131 for precise state transitions during this process. At the
100   time when each event occurs in this process, the callback function that was set
101   by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
102   opportunity to control the process.
103 
104   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
105   @param[in]  CompletionEvent If not NULL, indicates the event that will be signaled when the
106                               EFI DHCPv4 Protocol driver is transferred into the
107                               Dhcp4Bound state or when the DHCP process is aborted.
108                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
109                               check the completion status. If NULL,
110                               EFI_DHCP4_PROTOCOL.Start() will wait until the driver
111                               is transferred into the Dhcp4Bound state or the process fails.
112 
113   @retval EFI_SUCCESS           The DHCP configuration process has started, or it has completed
114                                 when CompletionEvent is NULL.
115   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
116                                 state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
117   @retval EFI_INVALID_PARAMETER This is NULL.
118   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
119   @retval EFI_TIMEOUT           The DHCP configuration process failed because no response was
120                                 received from the server within the specified timeout value.
121   @retval EFI_ABORTED           The user aborted the DHCP process.
122   @retval EFI_ALREADY_STARTED   Some other EFI DHCPv4 Protocol instance already started the
123                                 DHCP process.
124   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
125 
126 **/
127 EFI_STATUS
128 EFIAPI
129 EfiDhcp4Start (
130   IN EFI_DHCP4_PROTOCOL     *This,
131   IN EFI_EVENT              CompletionEvent   OPTIONAL
132   );
133 
134 /**
135   Extends the lease time by sending a request packet.
136 
137   The RenewRebind() function is used to manually extend the lease time when the
138   EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
139   not expired yet. This function will send a request packet to the previously
140   found server (or to any server when RebindRequest is TRUE) and transfer the
141   state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
142   TRUE). When a response is received, the state is returned to Dhcp4Bound.
143   If no response is received before the try count is exceeded (the RequestTryCount
144   field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
145   was issued by the previous server expires, the driver will return to the Dhcp4Bound
146   state and the previous configuration is restored. The outgoing and incoming packets
147   can be captured by the EFI_DHCP4_CALLBACK function.
148 
149   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
150   @param[in]  RebindRequest   If TRUE, this function broadcasts the request packets and enters
151                               the Dhcp4Rebinding state. Otherwise, it sends a unicast
152                               request packet and enters the Dhcp4Renewing state.
153   @param[in]  CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
154                               completes or some error occurs.
155                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
156                               check the completion status. If NULL,
157                               EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
158                               until the DHCP process finishes.
159 
160   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the
161                                 Dhcp4Renewing state or is back to the Dhcp4Bound state.
162   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
163                                 state. EFI_DHCP4_PROTOCOL.Configure() needs to
164                                 be called.
165   @retval EFI_INVALID_PARAMETER This is NULL.
166   @retval EFI_TIMEOUT           There was no response from the server when the try count was
167                                 exceeded.
168   @retval EFI_ACCESS_DENIED     The driver is not in the Dhcp4Bound state.
169   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
170 
171 **/
172 EFI_STATUS
173 EFIAPI
174 EfiDhcp4RenewRebind (
175   IN EFI_DHCP4_PROTOCOL     *This,
176   IN BOOLEAN                RebindRequest,
177   IN EFI_EVENT              CompletionEvent   OPTIONAL
178   );
179 
180 /**
181   Releases the current address configuration.
182 
183   The Release() function releases the current configured IP address by doing either
184   of the following:
185   * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
186     Dhcp4Bound state
187   * Setting the previously assigned IP address that was provided with the
188     EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
189     Dhcp4InitReboot state
190   After a successful call to this function, the EFI DHCPv4 Protocol driver returns
191   to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
192 
193   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
194 
195   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
196   @retval EFI_INVALID_PARAMETER This is NULL.
197   @retval EFI_ACCESS_DENIED     The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
198   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
199 
200 **/
201 EFI_STATUS
202 EFIAPI
203 EfiDhcp4Release (
204   IN EFI_DHCP4_PROTOCOL     *This
205   );
206 
207 /**
208   Stops the current address configuration.
209 
210   The Stop() function is used to stop the DHCP configuration process. After this
211   function is called successfully, the EFI DHCPv4 Protocol driver is transferred
212   into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
213   before DHCP configuration process can be started again. This function can be
214   called when the EFI DHCPv4 Protocol driver is in any state.
215 
216   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
217 
218   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
219   @retval EFI_INVALID_PARAMETER This is NULL.
220 
221 **/
222 EFI_STATUS
223 EFIAPI
224 EfiDhcp4Stop (
225   IN EFI_DHCP4_PROTOCOL     *This
226   );
227 
228 /**
229   Builds a DHCP packet, given the options to be appended or deleted or replaced.
230 
231   The Build() function is used to assemble a new packet from the original packet
232   by replacing or deleting existing options or appending new options. This function
233   does not change any state of the EFI DHCPv4 Protocol driver and can be used at
234   any time.
235 
236   @param[in]  This        Pointer to the EFI_DHCP4_PROTOCOL instance.
237   @param[in]  SeedPacket  Initial packet to be used as a base for building new packet.
238   @param[in]  DeleteCount Number of opcodes in the DeleteList.
239   @param[in]  DeleteList  List of opcodes to be deleted from the seed packet.
240                           Ignored if DeleteCount is zero.
241   @param[in]  AppendCount Number of entries in the OptionList.
242   @param[in]  AppendList  Pointer to a DHCP option list to be appended to SeedPacket.
243                           If SeedPacket also contains options in this list, they are
244                           replaced by new options (except pad option). Ignored if
245                           AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
246   @param[out] NewPacket   Pointer to storage for the pointer to the new allocated packet.
247                           Use the EFI Boot Service FreePool() on the resulting pointer
248                           when done with the packet.
249 
250   @retval EFI_SUCCESS           The new packet was built.
251   @retval EFI_OUT_OF_RESOURCES  Storage for the new packet could not be allocated.
252   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
253 
254 **/
255 EFI_STATUS
256 EFIAPI
257 EfiDhcp4Build (
258   IN EFI_DHCP4_PROTOCOL       *This,
259   IN EFI_DHCP4_PACKET         *SeedPacket,
260   IN UINT32                   DeleteCount,
261   IN UINT8                    *DeleteList OPTIONAL,
262   IN UINT32                   AppendCount,
263   IN EFI_DHCP4_PACKET_OPTION  *AppendList[] OPTIONAL,
264   OUT EFI_DHCP4_PACKET        **NewPacket
265   );
266 
267 /**
268   Transmits a DHCP formatted packet and optionally waits for responses.
269 
270   The TransmitReceive() function is used to transmit a DHCP packet and optionally
271   wait for the response from servers. This function does not change the state of
272   the EFI DHCPv4 Protocol driver and thus can be used at any time.
273 
274   @param[in]  This    Pointer to the EFI_DHCP4_PROTOCOL instance.
275   @param[in]  Token   Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
276 
277   @retval EFI_SUCCESS           The packet was successfully queued for transmission.
278   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
279   @retval EFI_NOT_READY         The previous call to this function has not finished yet. Try to call
280                                 this function after collection process completes.
281   @retval EFI_NO_MAPPING        The default station address is not available yet.
282   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
283   @retval Others                Some other unexpected error occurred.
284 
285 **/
286 EFI_STATUS
287 EFIAPI
288 EfiDhcp4TransmitReceive (
289   IN EFI_DHCP4_PROTOCOL                *This,
290   IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token
291   );
292 
293 /**
294   Parses the packed DHCP option data.
295 
296   The Parse() function is used to retrieve the option list from a DHCP packet.
297   If *OptionCount isn't zero, and there is enough space for all the DHCP options
298   in the Packet, each element of PacketOptionList is set to point to somewhere in
299   the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
300   the caller should reassemble the parsed DHCP options to get the finial result.
301   If *OptionCount is zero or there isn't enough space for all of them, the number
302   of DHCP options in the Packet is returned in OptionCount.
303 
304   @param  This             Pointer to the EFI_DHCP4_PROTOCOL instance.
305   @param  Packet           Pointer to packet to be parsed.
306   @param  OptionCount      On input, the number of entries in the PacketOptionList.
307                            On output, the number of entries that were written into the
308                            PacketOptionList.
309   @param  PacketOptionList List of packet option entries to be filled in. End option or pad
310                            options are not included.
311 
312   @retval EFI_SUCCESS           The packet was successfully parsed.
313   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
314   @retval EFI_BUFFER_TOO_SMALL  One or more of the following conditions is TRUE:
315                                 1) *OptionCount is smaller than the number of options that
316                                 were found in the Packet.
317                                 2) PacketOptionList is NULL.
318 
319 **/
320 EFI_STATUS
321 EFIAPI
322 EfiDhcp4Parse (
323   IN EFI_DHCP4_PROTOCOL       *This,
324   IN EFI_DHCP4_PACKET         *Packet,
325   IN OUT UINT32               *OptionCount,
326   OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
327   );
328 
329 EFI_DHCP4_PROTOCOL  mDhcp4ProtocolTemplate = {
330   EfiDhcp4GetModeData,
331   EfiDhcp4Configure,
332   EfiDhcp4Start,
333   EfiDhcp4RenewRebind,
334   EfiDhcp4Release,
335   EfiDhcp4Stop,
336   EfiDhcp4Build,
337   EfiDhcp4TransmitReceive,
338   EfiDhcp4Parse
339 };
340 
341 /**
342   Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver.
343 
344   The GetModeData() function returns the current operating mode and cached data
345   packet for the EFI DHCPv4 Protocol driver.
346 
347   @param[in]  This          Pointer to the EFI_DHCP4_PROTOCOL instance.
348   @param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure.
349 
350   @retval EFI_SUCCESS           The mode data was returned.
351   @retval EFI_INVALID_PARAMETER This is NULL.
352 
353 **/
354 EFI_STATUS
355 EFIAPI
EfiDhcp4GetModeData(IN EFI_DHCP4_PROTOCOL * This,OUT EFI_DHCP4_MODE_DATA * Dhcp4ModeData)356 EfiDhcp4GetModeData (
357   IN  EFI_DHCP4_PROTOCOL    *This,
358   OUT EFI_DHCP4_MODE_DATA   *Dhcp4ModeData
359   )
360 {
361   DHCP_PROTOCOL             *Instance;
362   DHCP_SERVICE              *DhcpSb;
363   DHCP_PARAMETER            *Para;
364   EFI_TPL                   OldTpl;
365   IP4_ADDR                  Ip;
366 
367   //
368   // First validate the parameters.
369   //
370   if ((This == NULL) || (Dhcp4ModeData == NULL)) {
371     return EFI_INVALID_PARAMETER;
372   }
373 
374   Instance = DHCP_INSTANCE_FROM_THIS (This);
375 
376   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
377   DhcpSb  = Instance->Service;
378 
379   //
380   // Caller can use GetModeData to retrieve current DHCP states
381   // no matter whether it is the active child or not.
382   //
383   Dhcp4ModeData->State = (EFI_DHCP4_STATE) DhcpSb->DhcpState;
384   CopyMem (&Dhcp4ModeData->ConfigData, &DhcpSb->ActiveConfig, sizeof (Dhcp4ModeData->ConfigData));
385   CopyMem (&Dhcp4ModeData->ClientMacAddress, &DhcpSb->Mac, sizeof (Dhcp4ModeData->ClientMacAddress));
386 
387   Ip = HTONL (DhcpSb->ClientAddr);
388   CopyMem (&Dhcp4ModeData->ClientAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
389 
390   Ip = HTONL (DhcpSb->Netmask);
391   CopyMem (&Dhcp4ModeData->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
392 
393   Ip = HTONL (DhcpSb->ServerAddr);
394   CopyMem (&Dhcp4ModeData->ServerAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
395 
396   Para = DhcpSb->Para;
397 
398   if (Para != NULL) {
399     Ip = HTONL (Para->Router);
400     CopyMem (&Dhcp4ModeData->RouterAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
401     Dhcp4ModeData->LeaseTime = Para->Lease;
402   } else {
403     ZeroMem (&Dhcp4ModeData->RouterAddress, sizeof (EFI_IPv4_ADDRESS));
404     Dhcp4ModeData->LeaseTime = 0xffffffff;
405   }
406 
407   Dhcp4ModeData->ReplyPacket = DhcpSb->Selected;
408 
409   gBS->RestoreTPL (OldTpl);
410   return EFI_SUCCESS;
411 }
412 
413 
414 /**
415   Free the resource related to the configure parameters.
416   DHCP driver will make a copy of the user's configure
417   such as the time out value.
418 
419   @param  Config                 The DHCP configure data
420 
421 **/
422 VOID
DhcpCleanConfigure(IN OUT EFI_DHCP4_CONFIG_DATA * Config)423 DhcpCleanConfigure (
424   IN OUT EFI_DHCP4_CONFIG_DATA  *Config
425   )
426 {
427   UINT32                    Index;
428 
429   if (Config->DiscoverTimeout != NULL) {
430     FreePool (Config->DiscoverTimeout);
431   }
432 
433   if (Config->RequestTimeout != NULL) {
434     FreePool (Config->RequestTimeout);
435   }
436 
437   if (Config->OptionList != NULL) {
438     for (Index = 0; Index < Config->OptionCount; Index++) {
439       if (Config->OptionList[Index] != NULL) {
440         FreePool (Config->OptionList[Index]);
441       }
442     }
443 
444     FreePool (Config->OptionList);
445   }
446 
447   ZeroMem (Config, sizeof (EFI_DHCP4_CONFIG_DATA));
448 }
449 
450 
451 /**
452   Allocate memory for configure parameter such as timeout value for Dst,
453   then copy the configure parameter from Src to Dst.
454 
455   @param[out]  Dst                    The destination DHCP configure data.
456   @param[in]   Src                    The source DHCP configure data.
457 
458   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
459   @retval EFI_SUCCESS            The configure is copied.
460 
461 **/
462 EFI_STATUS
DhcpCopyConfigure(OUT EFI_DHCP4_CONFIG_DATA * Dst,IN EFI_DHCP4_CONFIG_DATA * Src)463 DhcpCopyConfigure (
464   OUT EFI_DHCP4_CONFIG_DATA  *Dst,
465   IN  EFI_DHCP4_CONFIG_DATA  *Src
466   )
467 {
468   EFI_DHCP4_PACKET_OPTION   **DstOptions;
469   EFI_DHCP4_PACKET_OPTION   **SrcOptions;
470   UINTN                     Len;
471   UINT32                    Index;
472 
473   CopyMem (Dst, Src, sizeof (*Dst));
474   Dst->DiscoverTimeout  = NULL;
475   Dst->RequestTimeout   = NULL;
476   Dst->OptionList       = NULL;
477 
478   //
479   // Allocate a memory then copy DiscoverTimeout to it
480   //
481   if (Src->DiscoverTimeout != NULL) {
482     Len                   = Src->DiscoverTryCount * sizeof (UINT32);
483     Dst->DiscoverTimeout  = AllocatePool (Len);
484 
485     if (Dst->DiscoverTimeout == NULL) {
486       return EFI_OUT_OF_RESOURCES;
487     }
488 
489     for (Index = 0; Index < Src->DiscoverTryCount; Index++) {
490       Dst->DiscoverTimeout[Index] = MAX (Src->DiscoverTimeout[Index], 1);
491     }
492   }
493 
494   //
495   // Allocate a memory then copy RequestTimeout to it
496   //
497   if (Src->RequestTimeout != NULL) {
498     Len                 = Src->RequestTryCount * sizeof (UINT32);
499     Dst->RequestTimeout = AllocatePool (Len);
500 
501     if (Dst->RequestTimeout == NULL) {
502       goto ON_ERROR;
503     }
504 
505     for (Index = 0; Index < Src->RequestTryCount; Index++) {
506       Dst->RequestTimeout[Index] = MAX (Src->RequestTimeout[Index], 1);
507     }
508   }
509 
510   //
511   // Allocate an array of dhcp option point, then allocate memory
512   // for each option and copy the source option to it
513   //
514   if (Src->OptionList != NULL) {
515     Len             = Src->OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *);
516     Dst->OptionList = AllocateZeroPool (Len);
517 
518     if (Dst->OptionList == NULL) {
519       goto ON_ERROR;
520     }
521 
522     DstOptions  = Dst->OptionList;
523     SrcOptions  = Src->OptionList;
524 
525     for (Index = 0; Index < Src->OptionCount; Index++) {
526       Len = sizeof (EFI_DHCP4_PACKET_OPTION) + MAX (SrcOptions[Index]->Length - 1, 0);
527 
528       DstOptions[Index] = AllocatePool (Len);
529 
530       if (DstOptions[Index] == NULL) {
531         goto ON_ERROR;
532       }
533 
534       CopyMem (DstOptions[Index], SrcOptions[Index], Len);
535     }
536   }
537 
538   return EFI_SUCCESS;
539 
540 ON_ERROR:
541   DhcpCleanConfigure (Dst);
542   return EFI_OUT_OF_RESOURCES;
543 }
544 
545 
546 /**
547   Give up the control of the DHCP service to let other child
548   resume. Don't change the service's DHCP state and the Client
549   address and option list configure as required by RFC2131.
550 
551   @param  DhcpSb                 The DHCP service instance.
552 
553 **/
554 VOID
DhcpYieldControl(IN DHCP_SERVICE * DhcpSb)555 DhcpYieldControl (
556   IN DHCP_SERVICE           *DhcpSb
557   )
558 {
559   EFI_DHCP4_CONFIG_DATA     *Config;
560 
561   Config    = &DhcpSb->ActiveConfig;
562 
563   DhcpSb->ServiceState  = DHCP_UNCONFIGED;
564   DhcpSb->ActiveChild   = NULL;
565 
566   if (Config->DiscoverTimeout != NULL) {
567     FreePool (Config->DiscoverTimeout);
568 
569     Config->DiscoverTryCount  = 0;
570     Config->DiscoverTimeout   = NULL;
571   }
572 
573   if (Config->RequestTimeout != NULL) {
574     FreePool (Config->RequestTimeout);
575 
576     Config->RequestTryCount = 0;
577     Config->RequestTimeout  = NULL;
578   }
579 
580   Config->Dhcp4Callback   = NULL;
581   Config->CallbackContext = NULL;
582 }
583 
584 
585 /**
586   Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver.
587 
588   The Configure() function is used to initialize, change, or reset the operational
589   settings of the EFI DHCPv4 Protocol driver for the communication device on which
590   the EFI DHCPv4 Service Binding Protocol is installed. This function can be
591   successfully called only if both of the following are true:
592   * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init,
593     Dhcp4InitReboot, or Dhcp4Bound states.
594   * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI
595     DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4
596     Protocol driver.
597   When this driver is in the Dhcp4Stopped state, it can transfer into one of the
598   following two possible initial states:
599   * Dhcp4Init
600   * Dhcp4InitReboot
601   The driver can transfer into these states by calling Configure() with a non-NULL
602   Dhcp4CfgData. The driver will transfer into the appropriate state based on the
603   supplied client network address in the ClientAddress parameter and DHCP options
604   in the OptionList parameter as described in RFC 2131.
605   When Configure() is called successfully while Dhcp4CfgData is set to NULL, the
606   default configuring data will be reset in the EFI DHCPv4 Protocol driver and
607   the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance
608   wants to make it possible for another instance to configure the EFI DHCPv4 Protocol
609   driver, it must call this function with Dhcp4CfgData set to NULL.
610 
611   @param[in]  This                   Pointer to the EFI_DHCP4_PROTOCOL instance.
612   @param[in]  Dhcp4CfgData           Pointer to the EFI_DHCP4_CONFIG_DATA.
613 
614   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or
615                                 Dhcp4InitReboot state, if the original state of this driver
616                                 was Dhcp4Stopped and the value of Dhcp4CfgData was
617                                 not NULL. Otherwise, the state was left unchanged.
618   @retval EFI_ACCESS_DENIED     This instance of the EFI DHCPv4 Protocol driver was not in the
619                                 Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state;
620                                 Or onother instance of this EFI DHCPv4 Protocol driver is already
621                                 in a valid configured state.
622   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
623   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
624   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
625 
626 **/
627 EFI_STATUS
628 EFIAPI
EfiDhcp4Configure(IN EFI_DHCP4_PROTOCOL * This,IN EFI_DHCP4_CONFIG_DATA * Dhcp4CfgData OPTIONAL)629 EfiDhcp4Configure (
630   IN EFI_DHCP4_PROTOCOL     *This,
631   IN EFI_DHCP4_CONFIG_DATA  *Dhcp4CfgData       OPTIONAL
632   )
633 {
634   EFI_DHCP4_CONFIG_DATA     *Config;
635   DHCP_PROTOCOL             *Instance;
636   DHCP_SERVICE              *DhcpSb;
637   EFI_STATUS                Status;
638   EFI_TPL                   OldTpl;
639   UINT32                    Index;
640   IP4_ADDR                  Ip;
641 
642   //
643   // First validate the parameters
644   //
645   if (This == NULL) {
646     return EFI_INVALID_PARAMETER;
647   }
648 
649   if (Dhcp4CfgData != NULL) {
650     if ((Dhcp4CfgData->DiscoverTryCount != 0) && (Dhcp4CfgData->DiscoverTimeout == NULL)) {
651       return EFI_INVALID_PARAMETER;
652     }
653 
654     if ((Dhcp4CfgData->RequestTryCount != 0) && (Dhcp4CfgData->RequestTimeout == NULL)) {
655       return EFI_INVALID_PARAMETER;
656     }
657 
658     if ((Dhcp4CfgData->OptionCount != 0) && (Dhcp4CfgData->OptionList == NULL)) {
659       return EFI_INVALID_PARAMETER;
660     }
661 
662     CopyMem (&Ip, &Dhcp4CfgData->ClientAddress, sizeof (IP4_ADDR));
663     if (IP4_IS_LOCAL_BROADCAST(NTOHL (Ip))) {
664       return EFI_INVALID_PARAMETER;
665     }
666   }
667 
668   Instance = DHCP_INSTANCE_FROM_THIS (This);
669 
670   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
671     return EFI_INVALID_PARAMETER;
672   }
673 
674   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
675 
676   DhcpSb  = Instance->Service;
677   Config  = &DhcpSb->ActiveConfig;
678 
679   Status  = EFI_ACCESS_DENIED;
680 
681   if ((DhcpSb->DhcpState != Dhcp4Stopped) &&
682       (DhcpSb->DhcpState != Dhcp4Init) &&
683       (DhcpSb->DhcpState != Dhcp4InitReboot) &&
684       (DhcpSb->DhcpState != Dhcp4Bound)) {
685 
686     goto ON_EXIT;
687   }
688 
689   if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) {
690     goto ON_EXIT;
691   }
692 
693   if (Dhcp4CfgData != NULL) {
694     Status = EFI_OUT_OF_RESOURCES;
695     DhcpCleanConfigure (Config);
696 
697     if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) {
698       goto ON_EXIT;
699     }
700 
701     DhcpSb->UserOptionLen = 0;
702 
703     for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) {
704       DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2;
705     }
706 
707     DhcpSb->ActiveChild = Instance;
708 
709     if (DhcpSb->DhcpState == Dhcp4Stopped) {
710       DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress);
711 
712       if (DhcpSb->ClientAddr != 0) {
713         DhcpSb->DhcpState = Dhcp4InitReboot;
714       } else {
715         DhcpSb->DhcpState = Dhcp4Init;
716       }
717     }
718 
719     DhcpSb->ServiceState  = DHCP_CONFIGED;
720     Status                = EFI_SUCCESS;
721 
722   } else if (DhcpSb->ActiveChild == Instance) {
723     Status = EFI_SUCCESS;
724     DhcpYieldControl (DhcpSb);
725   }
726 
727 ON_EXIT:
728   gBS->RestoreTPL (OldTpl);
729   return Status;
730 }
731 
732 
733 /**
734   Starts the DHCP configuration process.
735 
736   The Start() function starts the DHCP configuration process. This function can
737   be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
738   Dhcp4InitReboot state.
739   If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
740   driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
741   Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
742   If the process aborts, either by the user or by some unexpected network error,
743   the state is restored to the Dhcp4Init state. The Start() function can be called
744   again to restart the process.
745   Refer to RFC 2131 for precise state transitions during this process. At the
746   time when each event occurs in this process, the callback function that was set
747   by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
748   opportunity to control the process.
749 
750   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
751   @param[in]  CompletionEvent If not NULL, indicates the event that will be signaled when the
752                               EFI DHCPv4 Protocol driver is transferred into the
753                               Dhcp4Bound state or when the DHCP process is aborted.
754                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
755                               check the completion status. If NULL,
756                               EFI_DHCP4_PROTOCOL.Start() will wait until the driver
757                               is transferred into the Dhcp4Bound state or the process fails.
758 
759   @retval EFI_SUCCESS           The DHCP configuration process has started, or it has completed
760                                 when CompletionEvent is NULL.
761   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
762                                 state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
763   @retval EFI_INVALID_PARAMETER This is NULL.
764   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
765   @retval EFI_TIMEOUT           The DHCP configuration process failed because no response was
766                                 received from the server within the specified timeout value.
767   @retval EFI_ABORTED           The user aborted the DHCP process.
768   @retval EFI_ALREADY_STARTED   Some other EFI DHCPv4 Protocol instance already started the
769                                 DHCP process.
770   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
771   @retval EFI_NO_MEDIA          There was a media error.
772 
773 **/
774 EFI_STATUS
775 EFIAPI
EfiDhcp4Start(IN EFI_DHCP4_PROTOCOL * This,IN EFI_EVENT CompletionEvent OPTIONAL)776 EfiDhcp4Start (
777   IN EFI_DHCP4_PROTOCOL     *This,
778   IN EFI_EVENT              CompletionEvent   OPTIONAL
779   )
780 {
781   DHCP_PROTOCOL             *Instance;
782   DHCP_SERVICE              *DhcpSb;
783   EFI_STATUS                Status;
784   EFI_TPL                   OldTpl;
785 
786   //
787   // First validate the parameters
788   //
789   if (This == NULL) {
790     return EFI_INVALID_PARAMETER;
791   }
792 
793   Instance = DHCP_INSTANCE_FROM_THIS (This);
794 
795   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
796     return EFI_INVALID_PARAMETER;
797   }
798 
799   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
800   DhcpSb  = Instance->Service;
801 
802   if (DhcpSb->DhcpState == Dhcp4Stopped) {
803     Status = EFI_NOT_STARTED;
804     goto ON_ERROR;
805   }
806 
807   if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) {
808     Status = EFI_ALREADY_STARTED;
809     goto ON_ERROR;
810   }
811 
812   DhcpSb->IoStatus = EFI_ALREADY_STARTED;
813 
814   if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) {
815     goto ON_ERROR;
816   }
817 
818 
819   Instance->CompletionEvent = CompletionEvent;
820 
821   //
822   // Restore the TPL now, don't call poll function at TPL_CALLBACK.
823   //
824   gBS->RestoreTPL (OldTpl);
825 
826   if (CompletionEvent == NULL) {
827     while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
828       DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
829     }
830 
831     return DhcpSb->IoStatus;
832   }
833 
834   return EFI_SUCCESS;
835 
836 ON_ERROR:
837   gBS->RestoreTPL (OldTpl);
838   return Status;
839 }
840 
841 
842 /**
843   Extends the lease time by sending a request packet.
844 
845   The RenewRebind() function is used to manually extend the lease time when the
846   EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
847   not expired yet. This function will send a request packet to the previously
848   found server (or to any server when RebindRequest is TRUE) and transfer the
849   state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
850   TRUE). When a response is received, the state is returned to Dhcp4Bound.
851   If no response is received before the try count is exceeded (the RequestTryCount
852   field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
853   was issued by the previous server expires, the driver will return to the Dhcp4Bound
854   state and the previous configuration is restored. The outgoing and incoming packets
855   can be captured by the EFI_DHCP4_CALLBACK function.
856 
857   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
858   @param[in]  RebindRequest   If TRUE, this function broadcasts the request packets and enters
859                               the Dhcp4Rebinding state. Otherwise, it sends a unicast
860                               request packet and enters the Dhcp4Renewing state.
861   @param[in]  CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
862                               completes or some error occurs.
863                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
864                               check the completion status. If NULL,
865                               EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
866                               until the DHCP process finishes.
867 
868   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the
869                                 Dhcp4Renewing state or is back to the Dhcp4Bound state.
870   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
871                                 state. EFI_DHCP4_PROTOCOL.Configure() needs to
872                                 be called.
873   @retval EFI_INVALID_PARAMETER This is NULL.
874   @retval EFI_TIMEOUT           There was no response from the server when the try count was
875                                 exceeded.
876   @retval EFI_ACCESS_DENIED     The driver is not in the Dhcp4Bound state.
877   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
878 
879 **/
880 EFI_STATUS
881 EFIAPI
EfiDhcp4RenewRebind(IN EFI_DHCP4_PROTOCOL * This,IN BOOLEAN RebindRequest,IN EFI_EVENT CompletionEvent OPTIONAL)882 EfiDhcp4RenewRebind (
883   IN EFI_DHCP4_PROTOCOL     *This,
884   IN BOOLEAN                RebindRequest,
885   IN EFI_EVENT              CompletionEvent   OPTIONAL
886   )
887 {
888   DHCP_PROTOCOL             *Instance;
889   DHCP_SERVICE              *DhcpSb;
890   EFI_STATUS                Status;
891   EFI_TPL                   OldTpl;
892 
893   //
894   // First validate the parameters
895   //
896   if (This == NULL) {
897     return EFI_INVALID_PARAMETER;
898   }
899 
900   Instance = DHCP_INSTANCE_FROM_THIS (This);
901 
902   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
903     return EFI_INVALID_PARAMETER;
904   }
905 
906   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
907   DhcpSb  = Instance->Service;
908 
909   if (DhcpSb->DhcpState == Dhcp4Stopped) {
910     Status = EFI_NOT_STARTED;
911     goto ON_EXIT;
912   }
913 
914   if (DhcpSb->DhcpState != Dhcp4Bound) {
915     Status = EFI_ACCESS_DENIED;
916     goto ON_EXIT;
917   }
918 
919   if (DHCP_IS_BOOTP (DhcpSb->Para)) {
920     Status = EFI_SUCCESS;
921     goto ON_EXIT;
922   }
923 
924   //
925   // Transit the states then send a extra DHCP request
926   //
927   if (!RebindRequest) {
928     DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
929   } else {
930     DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
931   }
932 
933   //
934   // Clear initial time to make sure that elapsed-time
935   // is set to 0 for first REQUEST in renewal process.
936   //
937   Instance->ElaspedTime = 0;
938 
939   Status = DhcpSendMessage (
940              DhcpSb,
941              DhcpSb->Selected,
942              DhcpSb->Para,
943              DHCP_MSG_REQUEST,
944              (UINT8 *) "Extra renew/rebind by the application"
945              );
946 
947   if (EFI_ERROR (Status)) {
948     DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
949     goto ON_EXIT;
950   }
951 
952   DhcpSb->ExtraRefresh        = TRUE;
953   DhcpSb->IoStatus            = EFI_ALREADY_STARTED;
954   Instance->RenewRebindEvent  = CompletionEvent;
955 
956   gBS->RestoreTPL (OldTpl);
957 
958   if (CompletionEvent == NULL) {
959     while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
960       DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
961 
962     }
963 
964     return DhcpSb->IoStatus;
965   }
966 
967   return EFI_SUCCESS;
968 
969 ON_EXIT:
970   gBS->RestoreTPL (OldTpl);
971   return Status;
972 }
973 
974 
975 /**
976   Releases the current address configuration.
977 
978   The Release() function releases the current configured IP address by doing either
979   of the following:
980   * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
981     Dhcp4Bound state
982   * Setting the previously assigned IP address that was provided with the
983     EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
984     Dhcp4InitReboot state
985   After a successful call to this function, the EFI DHCPv4 Protocol driver returns
986   to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
987 
988   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
989 
990   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
991   @retval EFI_INVALID_PARAMETER This is NULL.
992   @retval EFI_ACCESS_DENIED     The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
993   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
994 
995 **/
996 EFI_STATUS
997 EFIAPI
EfiDhcp4Release(IN EFI_DHCP4_PROTOCOL * This)998 EfiDhcp4Release (
999   IN EFI_DHCP4_PROTOCOL     *This
1000   )
1001 {
1002   DHCP_PROTOCOL             *Instance;
1003   DHCP_SERVICE              *DhcpSb;
1004   EFI_STATUS                Status;
1005   EFI_TPL                   OldTpl;
1006 
1007   //
1008   // First validate the parameters
1009   //
1010   if (This == NULL) {
1011     return EFI_INVALID_PARAMETER;
1012   }
1013 
1014   Instance = DHCP_INSTANCE_FROM_THIS (This);
1015 
1016   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
1017     return EFI_INVALID_PARAMETER;
1018   }
1019 
1020   Status  = EFI_SUCCESS;
1021   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
1022   DhcpSb  = Instance->Service;
1023 
1024   if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) {
1025     Status = EFI_ACCESS_DENIED;
1026     goto ON_EXIT;
1027   }
1028 
1029   if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) {
1030     Status = DhcpSendMessage (
1031                DhcpSb,
1032                DhcpSb->Selected,
1033                DhcpSb->Para,
1034                DHCP_MSG_RELEASE,
1035                NULL
1036                );
1037 
1038     if (EFI_ERROR (Status)) {
1039       Status = EFI_DEVICE_ERROR;
1040       goto ON_EXIT;
1041     }
1042   }
1043 
1044   DhcpCleanLease (DhcpSb);
1045 
1046 ON_EXIT:
1047   gBS->RestoreTPL (OldTpl);
1048   return Status;
1049 }
1050 
1051 
1052 /**
1053   Stops the current address configuration.
1054 
1055   The Stop() function is used to stop the DHCP configuration process. After this
1056   function is called successfully, the EFI DHCPv4 Protocol driver is transferred
1057   into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
1058   before DHCP configuration process can be started again. This function can be
1059   called when the EFI DHCPv4 Protocol driver is in any state.
1060 
1061   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
1062 
1063   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
1064   @retval EFI_INVALID_PARAMETER This is NULL.
1065 
1066 **/
1067 EFI_STATUS
1068 EFIAPI
EfiDhcp4Stop(IN EFI_DHCP4_PROTOCOL * This)1069 EfiDhcp4Stop (
1070   IN EFI_DHCP4_PROTOCOL     *This
1071   )
1072 {
1073   DHCP_PROTOCOL             *Instance;
1074   DHCP_SERVICE              *DhcpSb;
1075   EFI_TPL                   OldTpl;
1076 
1077   //
1078   // First validate the parameters
1079   //
1080   if (This == NULL) {
1081     return EFI_INVALID_PARAMETER;
1082   }
1083 
1084   Instance = DHCP_INSTANCE_FROM_THIS (This);
1085 
1086   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
1087     return EFI_INVALID_PARAMETER;
1088   }
1089 
1090   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
1091   DhcpSb  = Instance->Service;
1092 
1093   DhcpCleanLease (DhcpSb);
1094 
1095   DhcpSb->DhcpState     = Dhcp4Stopped;
1096   DhcpSb->ServiceState  = DHCP_UNCONFIGED;
1097 
1098   gBS->RestoreTPL (OldTpl);
1099   return EFI_SUCCESS;
1100 }
1101 
1102 
1103 /**
1104   Builds a DHCP packet, given the options to be appended or deleted or replaced.
1105 
1106   The Build() function is used to assemble a new packet from the original packet
1107   by replacing or deleting existing options or appending new options. This function
1108   does not change any state of the EFI DHCPv4 Protocol driver and can be used at
1109   any time.
1110 
1111   @param[in]  This        Pointer to the EFI_DHCP4_PROTOCOL instance.
1112   @param[in]  SeedPacket  Initial packet to be used as a base for building new packet.
1113   @param[in]  DeleteCount Number of opcodes in the DeleteList.
1114   @param[in]  DeleteList  List of opcodes to be deleted from the seed packet.
1115                           Ignored if DeleteCount is zero.
1116   @param[in]  AppendCount Number of entries in the OptionList.
1117   @param[in]  AppendList  Pointer to a DHCP option list to be appended to SeedPacket.
1118                           If SeedPacket also contains options in this list, they are
1119                           replaced by new options (except pad option). Ignored if
1120                           AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
1121   @param[out] NewPacket   Pointer to storage for the pointer to the new allocated packet.
1122                           Use the EFI Boot Service FreePool() on the resulting pointer
1123                           when done with the packet.
1124 
1125   @retval EFI_SUCCESS           The new packet was built.
1126   @retval EFI_OUT_OF_RESOURCES  Storage for the new packet could not be allocated.
1127   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
1128 
1129 **/
1130 EFI_STATUS
1131 EFIAPI
EfiDhcp4Build(IN EFI_DHCP4_PROTOCOL * This,IN EFI_DHCP4_PACKET * SeedPacket,IN UINT32 DeleteCount,IN UINT8 * DeleteList OPTIONAL,IN UINT32 AppendCount,IN EFI_DHCP4_PACKET_OPTION * AppendList[]OPTIONAL,OUT EFI_DHCP4_PACKET ** NewPacket)1132 EfiDhcp4Build (
1133   IN EFI_DHCP4_PROTOCOL       *This,
1134   IN EFI_DHCP4_PACKET         *SeedPacket,
1135   IN UINT32                   DeleteCount,
1136   IN UINT8                    *DeleteList OPTIONAL,
1137   IN UINT32                   AppendCount,
1138   IN EFI_DHCP4_PACKET_OPTION  *AppendList[] OPTIONAL,
1139   OUT EFI_DHCP4_PACKET        **NewPacket
1140   )
1141 {
1142   //
1143   // First validate the parameters
1144   //
1145   if ((This == NULL) || (NewPacket == NULL)) {
1146     return EFI_INVALID_PARAMETER;
1147   }
1148 
1149   if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
1150       EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) {
1151 
1152     return EFI_INVALID_PARAMETER;
1153   }
1154 
1155   if (((DeleteCount == 0) && (AppendCount == 0)) ||
1156       ((DeleteCount != 0) && (DeleteList == NULL)) ||
1157       ((AppendCount != 0) && (AppendList == NULL))) {
1158 
1159     return EFI_INVALID_PARAMETER;
1160   }
1161 
1162   return DhcpBuild (
1163            SeedPacket,
1164            DeleteCount,
1165            DeleteList,
1166            AppendCount,
1167            AppendList,
1168            NewPacket
1169            );
1170 }
1171 
1172 /**
1173   Callback by UdpIoCreatePort() when creating UdpIo for this Dhcp4 instance.
1174 
1175   @param[in] UdpIo      The UdpIo being created.
1176   @param[in] Context    Dhcp4 instance.
1177 
1178   @retval EFI_SUCCESS   UdpIo is configured successfully.
1179   @retval other         Other error occurs.
1180 **/
1181 EFI_STATUS
1182 EFIAPI
Dhcp4InstanceConfigUdpIo(IN UDP_IO * UdpIo,IN VOID * Context)1183 Dhcp4InstanceConfigUdpIo (
1184   IN UDP_IO       *UdpIo,
1185   IN VOID         *Context
1186   )
1187 {
1188   DHCP_PROTOCOL                     *Instance;
1189   DHCP_SERVICE                      *DhcpSb;
1190   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
1191   EFI_UDP4_CONFIG_DATA              UdpConfigData;
1192   IP4_ADDR                          ClientAddr;
1193   IP4_ADDR                          Ip;
1194   INTN                              Class;
1195   IP4_ADDR                          SubnetMask;
1196 
1197   Instance = (DHCP_PROTOCOL *) Context;
1198   DhcpSb   = Instance->Service;
1199   Token    = Instance->Token;
1200 
1201   ZeroMem (&UdpConfigData, sizeof (EFI_UDP4_CONFIG_DATA));
1202 
1203   UdpConfigData.AcceptBroadcast    = TRUE;
1204   UdpConfigData.AllowDuplicatePort = TRUE;
1205   UdpConfigData.TimeToLive         = 64;
1206   UdpConfigData.DoNotFragment      = TRUE;
1207 
1208   ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
1209   Ip = HTONL (ClientAddr);
1210   CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
1211 
1212   if (DhcpSb->Netmask == 0) {
1213     //
1214     // The Dhcp4.TransmitReceive() API should be able to used at any time according to
1215     // UEFI spec, while in classless addressing network, the netmask must be explicitly
1216     // provided together with the station address.
1217     // If the DHCP instance haven't be configured with a valid netmask, we could only
1218     // compute it accroding to the classful addressing rule.
1219     //
1220     Class = NetGetIpClass (ClientAddr);
1221     ASSERT (Class < IP4_ADDR_CLASSE);
1222     SubnetMask = gIp4AllMasks[Class << 3];
1223   } else {
1224     SubnetMask = DhcpSb->Netmask;
1225   }
1226 
1227   Ip = HTONL (SubnetMask);
1228   CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
1229 
1230   if ((Token->ListenPointCount == 0) || (Token->ListenPoints[0].ListenPort == 0)) {
1231     UdpConfigData.StationPort = DHCP_CLIENT_PORT;
1232   } else {
1233     UdpConfigData.StationPort = Token->ListenPoints[0].ListenPort;
1234   }
1235 
1236   return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
1237 }
1238 
1239 /**
1240   Create UdpIo for this Dhcp4 instance.
1241 
1242   @param Instance   The Dhcp4 instance.
1243 
1244   @retval EFI_SUCCESS                UdpIo is created successfully.
1245   @retval EFI_OUT_OF_RESOURCES       Fails to create UdpIo because of limited
1246                                      resources or configuration failure.
1247 **/
1248 EFI_STATUS
Dhcp4InstanceCreateUdpIo(IN OUT DHCP_PROTOCOL * Instance)1249 Dhcp4InstanceCreateUdpIo (
1250   IN OUT DHCP_PROTOCOL  *Instance
1251   )
1252 {
1253   DHCP_SERVICE  *DhcpSb;
1254   EFI_STATUS    Status;
1255   VOID          *Udp4;
1256 
1257   ASSERT (Instance->Token != NULL);
1258 
1259   DhcpSb          = Instance->Service;
1260   Instance->UdpIo = UdpIoCreateIo (
1261                       DhcpSb->Controller,
1262                       DhcpSb->Image,
1263                       Dhcp4InstanceConfigUdpIo,
1264                       UDP_IO_UDP4_VERSION,
1265                       Instance
1266                       );
1267   if (Instance->UdpIo == NULL) {
1268     return EFI_OUT_OF_RESOURCES;
1269   } else {
1270     Status = gBS->OpenProtocol (
1271                     Instance->UdpIo->UdpHandle,
1272                     &gEfiUdp4ProtocolGuid,
1273                     (VOID **) &Udp4,
1274                     Instance->Service->Image,
1275                     Instance->Handle,
1276                     EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
1277                     );
1278     if (EFI_ERROR (Status)) {
1279       UdpIoFreeIo (Instance->UdpIo);
1280       Instance->UdpIo = NULL;
1281     }
1282     return Status;
1283   }
1284 }
1285 
1286 /**
1287   Callback of Dhcp packet. Does nothing.
1288 
1289   @param Arg           The context.
1290 
1291 **/
1292 VOID
1293 EFIAPI
DhcpDummyExtFree(IN VOID * Arg)1294 DhcpDummyExtFree (
1295   IN VOID                   *Arg
1296   )
1297 {
1298 }
1299 
1300 /**
1301   Callback of UdpIoRecvDatagram() that handles a Dhcp4 packet.
1302 
1303   Only BOOTP responses will be handled that correspond to the Xid of the request
1304   sent out. The packet will be queued to the response queue.
1305 
1306   @param UdpPacket        The Dhcp4 packet.
1307   @param EndPoint         Udp4 address pair.
1308   @param IoStatus         Status of the input.
1309   @param Context          Extra info for the input.
1310 
1311 **/
1312 VOID
1313 EFIAPI
PxeDhcpInput(NET_BUF * UdpPacket,UDP_END_POINT * EndPoint,EFI_STATUS IoStatus,VOID * Context)1314 PxeDhcpInput (
1315   NET_BUF                   *UdpPacket,
1316   UDP_END_POINT             *EndPoint,
1317   EFI_STATUS                IoStatus,
1318   VOID                      *Context
1319   )
1320 {
1321   DHCP_PROTOCOL                     *Instance;
1322   EFI_DHCP4_HEADER                  *Head;
1323   NET_BUF                           *Wrap;
1324   EFI_DHCP4_PACKET                  *Packet;
1325   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
1326   UINT32                            Len;
1327   EFI_STATUS                        Status;
1328 
1329   Wrap     = NULL;
1330   Instance = (DHCP_PROTOCOL *) Context;
1331   Token    = Instance->Token;
1332 
1333   //
1334   // Don't restart receive if error occurs or DHCP is destroyed.
1335   //
1336   if (EFI_ERROR (IoStatus)) {
1337     return ;
1338   }
1339 
1340   ASSERT (UdpPacket != NULL);
1341 
1342   //
1343   // Validate the packet received
1344   //
1345   if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
1346     goto RESTART;
1347   }
1348 
1349   //
1350   // Copy the DHCP message to a continuous memory block, make the buffer size
1351   // of the EFI_DHCP4_PACKET a multiple of 4-byte.
1352   //
1353   Len  = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4);
1354   Wrap = NetbufAlloc (Len);
1355   if (Wrap == NULL) {
1356     goto RESTART;
1357   }
1358 
1359   Packet         = (EFI_DHCP4_PACKET *) NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL);
1360   ASSERT (Packet != NULL);
1361 
1362   Packet->Size   = Len;
1363   Head           = &Packet->Dhcp4.Header;
1364   Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
1365 
1366   if (Packet->Length != UdpPacket->TotalSize) {
1367     goto RESTART;
1368   }
1369 
1370   //
1371   // Is this packet the answer to our packet?
1372   //
1373   if ((Head->OpCode != BOOTP_REPLY) ||
1374       (Head->Xid != Token->Packet->Dhcp4.Header.Xid) ||
1375       (CompareMem (&Token->Packet->Dhcp4.Header.ClientHwAddr[0], Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
1376     goto RESTART;
1377   }
1378 
1379   //
1380   // Validate the options and retrieve the interested options
1381   //
1382   if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
1383       (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
1384       EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
1385 
1386     goto RESTART;
1387   }
1388 
1389   //
1390   // Keep this packet in the ResponseQueue.
1391   //
1392   NET_GET_REF (Wrap);
1393   NetbufQueAppend (&Instance->ResponseQueue, Wrap);
1394 
1395 RESTART:
1396 
1397   NetbufFree (UdpPacket);
1398 
1399   if (Wrap != NULL) {
1400     NetbufFree (Wrap);
1401   }
1402 
1403   Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
1404   if (EFI_ERROR (Status)) {
1405     PxeDhcpDone (Instance);
1406   }
1407 }
1408 
1409 /**
1410   Complete a Dhcp4 transaction and signal the upper layer.
1411 
1412   @param Instance      Dhcp4 instance.
1413 
1414 **/
1415 VOID
PxeDhcpDone(IN DHCP_PROTOCOL * Instance)1416 PxeDhcpDone (
1417   IN DHCP_PROTOCOL  *Instance
1418   )
1419 {
1420   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
1421 
1422   Token = Instance->Token;
1423 
1424   Token->ResponseCount = Instance->ResponseQueue.BufNum;
1425   if (Token->ResponseCount != 0) {
1426     Token->ResponseList = (EFI_DHCP4_PACKET *) AllocatePool (Instance->ResponseQueue.BufSize);
1427     if (Token->ResponseList == NULL) {
1428       Token->Status = EFI_OUT_OF_RESOURCES;
1429       goto SIGNAL_USER;
1430     }
1431 
1432     //
1433     // Copy the received DHCP responses.
1434     //
1435     NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *) Token->ResponseList);
1436     Token->Status = EFI_SUCCESS;
1437   } else {
1438     Token->ResponseList = NULL;
1439     Token->Status       = EFI_TIMEOUT;
1440   }
1441 
1442 SIGNAL_USER:
1443   //
1444   // Clean up the resources dedicated for this transmit receive transaction.
1445   //
1446   NetbufQueFlush (&Instance->ResponseQueue);
1447   UdpIoCleanIo (Instance->UdpIo);
1448   gBS->CloseProtocol (
1449          Instance->UdpIo->UdpHandle,
1450          &gEfiUdp4ProtocolGuid,
1451          Instance->Service->Image,
1452          Instance->Handle
1453          );
1454   UdpIoFreeIo (Instance->UdpIo);
1455   Instance->UdpIo = NULL;
1456   Instance->Token = NULL;
1457 
1458   if (Token->CompletionEvent != NULL) {
1459     gBS->SignalEvent (Token->CompletionEvent);
1460   }
1461 }
1462 
1463 
1464 /**
1465   Transmits a DHCP formatted packet and optionally waits for responses.
1466 
1467   The TransmitReceive() function is used to transmit a DHCP packet and optionally
1468   wait for the response from servers. This function does not change the state of
1469   the EFI DHCPv4 Protocol driver and thus can be used at any time.
1470 
1471   @param[in]  This    Pointer to the EFI_DHCP4_PROTOCOL instance.
1472   @param[in]  Token   Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
1473 
1474   @retval EFI_SUCCESS           The packet was successfully queued for transmission.
1475   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
1476   @retval EFI_NOT_READY         The previous call to this function has not finished yet. Try to call
1477                                 this function after collection process completes.
1478   @retval EFI_NO_MAPPING        The default station address is not available yet.
1479   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
1480   @retval Others                Some other unexpected error occurred.
1481 
1482 **/
1483 EFI_STATUS
1484 EFIAPI
EfiDhcp4TransmitReceive(IN EFI_DHCP4_PROTOCOL * This,IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN * Token)1485 EfiDhcp4TransmitReceive (
1486   IN EFI_DHCP4_PROTOCOL                *This,
1487   IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token
1488   )
1489 {
1490   DHCP_PROTOCOL  *Instance;
1491   EFI_TPL        OldTpl;
1492   EFI_STATUS     Status;
1493   NET_FRAGMENT   Frag;
1494   NET_BUF        *Wrap;
1495   UDP_END_POINT  EndPoint;
1496   IP4_ADDR       Ip;
1497   DHCP_SERVICE   *DhcpSb;
1498   EFI_IP_ADDRESS Gateway;
1499   IP4_ADDR       ClientAddr;
1500 
1501   if ((This == NULL) || (Token == NULL) || (Token->Packet == NULL)) {
1502     return EFI_INVALID_PARAMETER;
1503   }
1504 
1505   Instance = DHCP_INSTANCE_FROM_THIS (This);
1506   DhcpSb   = Instance->Service;
1507 
1508   if (Instance->Token != NULL) {
1509     //
1510     // The previous call to TransmitReceive is not finished.
1511     //
1512     return EFI_NOT_READY;
1513   }
1514 
1515   if ((Token->Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC)                   ||
1516       (NTOHL (Token->Packet->Dhcp4.Header.Xid) == Instance->Service->Xid) ||
1517       (Token->TimeoutValue == 0)                                          ||
1518       ((Token->ListenPointCount != 0) && (Token->ListenPoints == NULL))   ||
1519       EFI_ERROR (DhcpValidateOptions (Token->Packet, NULL))               ||
1520       EFI_IP4_EQUAL (&Token->RemoteAddress, &mZeroIp4Addr)
1521       ) {
1522     //
1523     // The DHCP packet isn't well-formed, the Transaction ID is already used,
1524     // the timeout value is zero, the ListenPoint is invalid, or the
1525     // RemoteAddress is zero.
1526     //
1527     return EFI_INVALID_PARAMETER;
1528   }
1529 
1530   ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
1531 
1532   if (ClientAddr == 0) {
1533     return EFI_NO_MAPPING;
1534   }
1535 
1536   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
1537 
1538   //
1539   // Save the token and the timeout value.
1540   //
1541   Instance->Token   = Token;
1542   Instance->Timeout = Token->TimeoutValue;
1543 
1544   //
1545   // Create a UDP IO for this transmit receive transaction.
1546   //
1547   Status = Dhcp4InstanceCreateUdpIo (Instance);
1548   if (EFI_ERROR (Status)) {
1549     goto ON_ERROR;
1550   }
1551 
1552   //
1553   // Save the Client Address is sent out
1554   //
1555   CopyMem (
1556     &DhcpSb->ClientAddressSendOut[0],
1557     &Token->Packet->Dhcp4.Header.ClientHwAddr[0],
1558     Token->Packet->Dhcp4.Header.HwAddrLen
1559     );
1560 
1561   //
1562   // Wrap the DHCP packet into a net buffer.
1563   //
1564   Frag.Bulk = (UINT8 *) &Token->Packet->Dhcp4;
1565   Frag.Len  = Token->Packet->Length;
1566   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
1567   if (Wrap == NULL) {
1568     Status = EFI_OUT_OF_RESOURCES;
1569     goto ON_ERROR;
1570   }
1571 
1572   //
1573   // Set the local address and local port to ZERO.
1574   //
1575   ZeroMem (&EndPoint, sizeof (UDP_END_POINT));
1576 
1577   //
1578   // Set the destination address and destination port.
1579   //
1580   CopyMem (&Ip, &Token->RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
1581   EndPoint.RemoteAddr.Addr[0] = NTOHL (Ip);
1582 
1583   if (Token->RemotePort == 0) {
1584     EndPoint.RemotePort = DHCP_SERVER_PORT;
1585   } else {
1586     EndPoint.RemotePort = Token->RemotePort;
1587   }
1588 
1589   //
1590   // Get the gateway.
1591   //
1592   ZeroMem (&Gateway, sizeof (Gateway));
1593   if (!IP4_NET_EQUAL (ClientAddr, EndPoint.RemoteAddr.Addr[0], DhcpSb->Netmask)) {
1594     CopyMem (&Gateway.v4, &Token->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
1595     Gateway.Addr[0] = NTOHL (Gateway.Addr[0]);
1596   }
1597 
1598   //
1599   // Transmit the DHCP packet.
1600   //
1601   Status = UdpIoSendDatagram (Instance->UdpIo, Wrap, &EndPoint, &Gateway, DhcpOnPacketSent, NULL);
1602   if (EFI_ERROR (Status)) {
1603     NetbufFree (Wrap);
1604     goto ON_ERROR;
1605   }
1606 
1607   //
1608   // Start to receive the DHCP response.
1609   //
1610   Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
1611   if (EFI_ERROR (Status)) {
1612     goto ON_ERROR;
1613   }
1614 
1615 ON_ERROR:
1616 
1617   if (EFI_ERROR (Status) && (Instance->UdpIo != NULL)) {
1618     UdpIoCleanIo (Instance->UdpIo);
1619     gBS->CloseProtocol (
1620            Instance->UdpIo->UdpHandle,
1621            &gEfiUdp4ProtocolGuid,
1622            Instance->Service->Image,
1623            Instance->Handle
1624            );
1625     UdpIoFreeIo (Instance->UdpIo);
1626     Instance->UdpIo = NULL;
1627     Instance->Token = NULL;
1628   }
1629 
1630   gBS->RestoreTPL (OldTpl);
1631 
1632   if (!EFI_ERROR (Status) && (Token->CompletionEvent == NULL)) {
1633     //
1634     // Keep polling until timeout if no error happens and the CompletionEvent
1635     // is NULL.
1636     //
1637     while (TRUE) {
1638       OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
1639       //
1640       // Raise TPL to protect the UDPIO in instance, in case that DhcpOnTimerTick
1641       // free it when timeout.
1642       //
1643       if (Instance->Timeout > 0) {
1644         Instance->UdpIo->Protocol.Udp4->Poll (Instance->UdpIo->Protocol.Udp4);
1645         gBS->RestoreTPL (OldTpl);
1646       } else {
1647         gBS->RestoreTPL (OldTpl);
1648         break;
1649       }
1650     }
1651   }
1652 
1653   return Status;
1654 }
1655 
1656 
1657 /**
1658   Callback function for DhcpIterateOptions. This callback sets the
1659   EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point
1660   the individual DHCP option in the packet.
1661 
1662   @param[in]  Tag                    The DHCP option type
1663   @param[in]  Len                    Length of the DHCP option data
1664   @param[in]  Data                   The DHCP option data
1665   @param[in]  Context                The context, to pass several parameters in.
1666 
1667   @retval EFI_SUCCESS            It always returns EFI_SUCCESS
1668 
1669 **/
1670 EFI_STATUS
Dhcp4ParseCheckOption(IN UINT8 Tag,IN UINT8 Len,IN UINT8 * Data,IN VOID * Context)1671 Dhcp4ParseCheckOption (
1672   IN UINT8                  Tag,
1673   IN UINT8                  Len,
1674   IN UINT8                  *Data,
1675   IN VOID                   *Context
1676   )
1677 {
1678   DHCP_PARSE_CONTEXT        *Parse;
1679 
1680   Parse = (DHCP_PARSE_CONTEXT *) Context;
1681   Parse->Index++;
1682 
1683   if (Parse->Index <= Parse->OptionCount) {
1684     //
1685     // Use BASE_CR to get the memory position of EFI_DHCP4_PACKET_OPTION for
1686     // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only
1687     // pass in the point to option data.
1688     //
1689     Parse->Option[Parse->Index - 1] = BASE_CR (Data, EFI_DHCP4_PACKET_OPTION, Data);
1690   }
1691 
1692   return EFI_SUCCESS;
1693 }
1694 
1695 
1696 /**
1697   Parses the packed DHCP option data.
1698 
1699   The Parse() function is used to retrieve the option list from a DHCP packet.
1700   If *OptionCount isn't zero, and there is enough space for all the DHCP options
1701   in the Packet, each element of PacketOptionList is set to point to somewhere in
1702   the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
1703   the caller should reassemble the parsed DHCP options to get the finial result.
1704   If *OptionCount is zero or there isn't enough space for all of them, the number
1705   of DHCP options in the Packet is returned in OptionCount.
1706 
1707   @param  This             Pointer to the EFI_DHCP4_PROTOCOL instance.
1708   @param  Packet           Pointer to packet to be parsed.
1709   @param  OptionCount      On input, the number of entries in the PacketOptionList.
1710                            On output, the number of entries that were written into the
1711                            PacketOptionList.
1712   @param  PacketOptionList List of packet option entries to be filled in. End option or pad
1713                            options are not included.
1714 
1715   @retval EFI_SUCCESS           The packet was successfully parsed.
1716   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
1717   @retval EFI_BUFFER_TOO_SMALL  One or more of the following conditions is TRUE:
1718                                 1) *OptionCount is smaller than the number of options that
1719                                 were found in the Packet.
1720                                 2) PacketOptionList is NULL.
1721 
1722 **/
1723 EFI_STATUS
1724 EFIAPI
EfiDhcp4Parse(IN EFI_DHCP4_PROTOCOL * This,IN EFI_DHCP4_PACKET * Packet,IN OUT UINT32 * OptionCount,OUT EFI_DHCP4_PACKET_OPTION * PacketOptionList[]OPTIONAL)1725 EfiDhcp4Parse (
1726   IN EFI_DHCP4_PROTOCOL       *This,
1727   IN EFI_DHCP4_PACKET         *Packet,
1728   IN OUT UINT32               *OptionCount,
1729   OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
1730   )
1731 {
1732   DHCP_PARSE_CONTEXT        Context;
1733   EFI_STATUS                Status;
1734 
1735   //
1736   // First validate the parameters
1737   //
1738   if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {
1739     return EFI_INVALID_PARAMETER;
1740   }
1741 
1742   if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) ||
1743       (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
1744       EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
1745 
1746     return EFI_INVALID_PARAMETER;
1747   }
1748 
1749   if ((*OptionCount != 0) && (PacketOptionList == NULL)) {
1750     return EFI_BUFFER_TOO_SMALL;
1751   }
1752 
1753   ZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
1754 
1755   Context.Option      = PacketOptionList;
1756   Context.OptionCount = *OptionCount;
1757   Context.Index       = 0;
1758 
1759   Status              = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context);
1760 
1761   if (EFI_ERROR (Status)) {
1762     return Status;
1763   }
1764 
1765   *OptionCount = Context.Index;
1766 
1767   if (Context.Index > Context.OptionCount) {
1768     return EFI_BUFFER_TOO_SMALL;
1769   }
1770 
1771   return EFI_SUCCESS;
1772 }
1773 
1774 /**
1775   Set the elapsed time based on the given instance and the pointer to the
1776   elapsed time option.
1777 
1778   @param[in]      Elapsed       The pointer to the position to append.
1779   @param[in]      Instance      The pointer to the Dhcp4 instance.
1780 **/
1781 VOID
SetElapsedTime(IN UINT16 * Elapsed,IN DHCP_PROTOCOL * Instance)1782 SetElapsedTime (
1783   IN     UINT16                 *Elapsed,
1784   IN     DHCP_PROTOCOL          *Instance
1785   )
1786 {
1787   WriteUnaligned16 (Elapsed, HTONS(Instance->ElaspedTime));
1788 }
1789