1 /** @file
2   This file implement the EFI_DHCP4_PROTOCOL interface.
3 
4 Copyright (c) 2006 - 2015, 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 
664     if ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) {
665 
666       return EFI_INVALID_PARAMETER;
667     }
668   }
669 
670   Instance = DHCP_INSTANCE_FROM_THIS (This);
671 
672   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
673     return EFI_INVALID_PARAMETER;
674   }
675 
676   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
677 
678   DhcpSb  = Instance->Service;
679   Config  = &DhcpSb->ActiveConfig;
680 
681   Status  = EFI_ACCESS_DENIED;
682 
683   if ((DhcpSb->DhcpState != Dhcp4Stopped) &&
684       (DhcpSb->DhcpState != Dhcp4Init) &&
685       (DhcpSb->DhcpState != Dhcp4InitReboot) &&
686       (DhcpSb->DhcpState != Dhcp4Bound)) {
687 
688     goto ON_EXIT;
689   }
690 
691   if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) {
692     goto ON_EXIT;
693   }
694 
695   if (Dhcp4CfgData != NULL) {
696     Status = EFI_OUT_OF_RESOURCES;
697     DhcpCleanConfigure (Config);
698 
699     if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) {
700       goto ON_EXIT;
701     }
702 
703     DhcpSb->UserOptionLen = 0;
704 
705     for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) {
706       DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2;
707     }
708 
709     DhcpSb->ActiveChild = Instance;
710 
711     if (DhcpSb->DhcpState == Dhcp4Stopped) {
712       DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress);
713 
714       if (DhcpSb->ClientAddr != 0) {
715         DhcpSb->DhcpState = Dhcp4InitReboot;
716       } else {
717         DhcpSb->DhcpState = Dhcp4Init;
718       }
719     }
720 
721     DhcpSb->ServiceState  = DHCP_CONFIGED;
722     Status                = EFI_SUCCESS;
723 
724   } else if (DhcpSb->ActiveChild == Instance) {
725     Status = EFI_SUCCESS;
726     DhcpYieldControl (DhcpSb);
727   }
728 
729 ON_EXIT:
730   gBS->RestoreTPL (OldTpl);
731   return Status;
732 }
733 
734 
735 /**
736   Starts the DHCP configuration process.
737 
738   The Start() function starts the DHCP configuration process. This function can
739   be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or
740   Dhcp4InitReboot state.
741   If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol
742   driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the
743   Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL.
744   If the process aborts, either by the user or by some unexpected network error,
745   the state is restored to the Dhcp4Init state. The Start() function can be called
746   again to restart the process.
747   Refer to RFC 2131 for precise state transitions during this process. At the
748   time when each event occurs in this process, the callback function that was set
749   by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this
750   opportunity to control the process.
751 
752   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
753   @param[in]  CompletionEvent If not NULL, indicates the event that will be signaled when the
754                               EFI DHCPv4 Protocol driver is transferred into the
755                               Dhcp4Bound state or when the DHCP process is aborted.
756                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
757                               check the completion status. If NULL,
758                               EFI_DHCP4_PROTOCOL.Start() will wait until the driver
759                               is transferred into the Dhcp4Bound state or the process fails.
760 
761   @retval EFI_SUCCESS           The DHCP configuration process has started, or it has completed
762                                 when CompletionEvent is NULL.
763   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
764                                 state. EFI_DHCP4_PROTOCOL. Configure() needs to be called.
765   @retval EFI_INVALID_PARAMETER This is NULL.
766   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
767   @retval EFI_TIMEOUT           The DHCP configuration process failed because no response was
768                                 received from the server within the specified timeout value.
769   @retval EFI_ABORTED           The user aborted the DHCP process.
770   @retval EFI_ALREADY_STARTED   Some other EFI DHCPv4 Protocol instance already started the
771                                 DHCP process.
772   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
773   @retval EFI_NO_MEDIA          There was a media error.
774 
775 **/
776 EFI_STATUS
777 EFIAPI
EfiDhcp4Start(IN EFI_DHCP4_PROTOCOL * This,IN EFI_EVENT CompletionEvent OPTIONAL)778 EfiDhcp4Start (
779   IN EFI_DHCP4_PROTOCOL     *This,
780   IN EFI_EVENT              CompletionEvent   OPTIONAL
781   )
782 {
783   DHCP_PROTOCOL             *Instance;
784   DHCP_SERVICE              *DhcpSb;
785   EFI_STATUS                Status;
786   EFI_TPL                   OldTpl;
787 
788   //
789   // First validate the parameters
790   //
791   if (This == NULL) {
792     return EFI_INVALID_PARAMETER;
793   }
794 
795   Instance = DHCP_INSTANCE_FROM_THIS (This);
796 
797   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
798     return EFI_INVALID_PARAMETER;
799   }
800 
801   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
802   DhcpSb  = Instance->Service;
803 
804   if (DhcpSb->DhcpState == Dhcp4Stopped) {
805     Status = EFI_NOT_STARTED;
806     goto ON_ERROR;
807   }
808 
809   if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) {
810     Status = EFI_ALREADY_STARTED;
811     goto ON_ERROR;
812   }
813 
814   DhcpSb->IoStatus = EFI_ALREADY_STARTED;
815 
816   if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) {
817     goto ON_ERROR;
818   }
819 
820 
821   Instance->CompletionEvent = CompletionEvent;
822 
823   //
824   // Restore the TPL now, don't call poll function at TPL_CALLBACK.
825   //
826   gBS->RestoreTPL (OldTpl);
827 
828   if (CompletionEvent == NULL) {
829     while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
830       DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
831     }
832 
833     return DhcpSb->IoStatus;
834   }
835 
836   return EFI_SUCCESS;
837 
838 ON_ERROR:
839   gBS->RestoreTPL (OldTpl);
840   return Status;
841 }
842 
843 
844 /**
845   Extends the lease time by sending a request packet.
846 
847   The RenewRebind() function is used to manually extend the lease time when the
848   EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has
849   not expired yet. This function will send a request packet to the previously
850   found server (or to any server when RebindRequest is TRUE) and transfer the
851   state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is
852   TRUE). When a response is received, the state is returned to Dhcp4Bound.
853   If no response is received before the try count is exceeded (the RequestTryCount
854   field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that
855   was issued by the previous server expires, the driver will return to the Dhcp4Bound
856   state and the previous configuration is restored. The outgoing and incoming packets
857   can be captured by the EFI_DHCP4_CALLBACK function.
858 
859   @param[in]  This            Pointer to the EFI_DHCP4_PROTOCOL instance.
860   @param[in]  RebindRequest   If TRUE, this function broadcasts the request packets and enters
861                               the Dhcp4Rebinding state. Otherwise, it sends a unicast
862                               request packet and enters the Dhcp4Renewing state.
863   @param[in]  CompletionEvent If not NULL, this event is signaled when the renew/rebind phase
864                               completes or some error occurs.
865                               EFI_DHCP4_PROTOCOL.GetModeData() can be called to
866                               check the completion status. If NULL,
867                               EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait
868                               until the DHCP process finishes.
869 
870   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the
871                                 Dhcp4Renewing state or is back to the Dhcp4Bound state.
872   @retval EFI_NOT_STARTED       The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped
873                                 state. EFI_DHCP4_PROTOCOL.Configure() needs to
874                                 be called.
875   @retval EFI_INVALID_PARAMETER This is NULL.
876   @retval EFI_TIMEOUT           There was no response from the server when the try count was
877                                 exceeded.
878   @retval EFI_ACCESS_DENIED     The driver is not in the Dhcp4Bound state.
879   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
880 
881 **/
882 EFI_STATUS
883 EFIAPI
EfiDhcp4RenewRebind(IN EFI_DHCP4_PROTOCOL * This,IN BOOLEAN RebindRequest,IN EFI_EVENT CompletionEvent OPTIONAL)884 EfiDhcp4RenewRebind (
885   IN EFI_DHCP4_PROTOCOL     *This,
886   IN BOOLEAN                RebindRequest,
887   IN EFI_EVENT              CompletionEvent   OPTIONAL
888   )
889 {
890   DHCP_PROTOCOL             *Instance;
891   DHCP_SERVICE              *DhcpSb;
892   EFI_STATUS                Status;
893   EFI_TPL                   OldTpl;
894 
895   //
896   // First validate the parameters
897   //
898   if (This == NULL) {
899     return EFI_INVALID_PARAMETER;
900   }
901 
902   Instance = DHCP_INSTANCE_FROM_THIS (This);
903 
904   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
905     return EFI_INVALID_PARAMETER;
906   }
907 
908   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
909   DhcpSb  = Instance->Service;
910 
911   if (DhcpSb->DhcpState == Dhcp4Stopped) {
912     Status = EFI_NOT_STARTED;
913     goto ON_EXIT;
914   }
915 
916   if (DhcpSb->DhcpState != Dhcp4Bound) {
917     Status = EFI_ACCESS_DENIED;
918     goto ON_EXIT;
919   }
920 
921   if (DHCP_IS_BOOTP (DhcpSb->Para)) {
922     Status = EFI_SUCCESS;
923     goto ON_EXIT;
924   }
925 
926   //
927   // Transit the states then send a extra DHCP request
928   //
929   if (!RebindRequest) {
930     DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
931   } else {
932     DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
933   }
934 
935   //
936   // Clear initial time to make sure that elapsed-time
937   // is set to 0 for first REQUEST in renewal process.
938   //
939   Instance->ElaspedTime = 0;
940 
941   Status = DhcpSendMessage (
942              DhcpSb,
943              DhcpSb->Selected,
944              DhcpSb->Para,
945              DHCP_MSG_REQUEST,
946              (UINT8 *) "Extra renew/rebind by the application"
947              );
948 
949   if (EFI_ERROR (Status)) {
950     DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
951     goto ON_EXIT;
952   }
953 
954   DhcpSb->ExtraRefresh        = TRUE;
955   DhcpSb->IoStatus            = EFI_ALREADY_STARTED;
956   Instance->RenewRebindEvent  = CompletionEvent;
957 
958   gBS->RestoreTPL (OldTpl);
959 
960   if (CompletionEvent == NULL) {
961     while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
962       DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4);
963 
964     }
965 
966     return DhcpSb->IoStatus;
967   }
968 
969   return EFI_SUCCESS;
970 
971 ON_EXIT:
972   gBS->RestoreTPL (OldTpl);
973   return Status;
974 }
975 
976 
977 /**
978   Releases the current address configuration.
979 
980   The Release() function releases the current configured IP address by doing either
981   of the following:
982   * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the
983     Dhcp4Bound state
984   * Setting the previously assigned IP address that was provided with the
985     EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in
986     Dhcp4InitReboot state
987   After a successful call to this function, the EFI DHCPv4 Protocol driver returns
988   to the Dhcp4Init state and any subsequent incoming packets will be discarded silently.
989 
990   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
991 
992   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase.
993   @retval EFI_INVALID_PARAMETER This is NULL.
994   @retval EFI_ACCESS_DENIED     The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state.
995   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
996 
997 **/
998 EFI_STATUS
999 EFIAPI
EfiDhcp4Release(IN EFI_DHCP4_PROTOCOL * This)1000 EfiDhcp4Release (
1001   IN EFI_DHCP4_PROTOCOL     *This
1002   )
1003 {
1004   DHCP_PROTOCOL             *Instance;
1005   DHCP_SERVICE              *DhcpSb;
1006   EFI_STATUS                Status;
1007   EFI_TPL                   OldTpl;
1008 
1009   //
1010   // First validate the parameters
1011   //
1012   if (This == NULL) {
1013     return EFI_INVALID_PARAMETER;
1014   }
1015 
1016   Instance = DHCP_INSTANCE_FROM_THIS (This);
1017 
1018   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
1019     return EFI_INVALID_PARAMETER;
1020   }
1021 
1022   Status  = EFI_SUCCESS;
1023   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
1024   DhcpSb  = Instance->Service;
1025 
1026   if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) {
1027     Status = EFI_ACCESS_DENIED;
1028     goto ON_EXIT;
1029   }
1030 
1031   if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) {
1032     Status = DhcpSendMessage (
1033                DhcpSb,
1034                DhcpSb->Selected,
1035                DhcpSb->Para,
1036                DHCP_MSG_RELEASE,
1037                NULL
1038                );
1039 
1040     if (EFI_ERROR (Status)) {
1041       Status = EFI_DEVICE_ERROR;
1042       goto ON_EXIT;
1043     }
1044   }
1045 
1046   DhcpCleanLease (DhcpSb);
1047 
1048 ON_EXIT:
1049   gBS->RestoreTPL (OldTpl);
1050   return Status;
1051 }
1052 
1053 
1054 /**
1055   Stops the current address configuration.
1056 
1057   The Stop() function is used to stop the DHCP configuration process. After this
1058   function is called successfully, the EFI DHCPv4 Protocol driver is transferred
1059   into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called
1060   before DHCP configuration process can be started again. This function can be
1061   called when the EFI DHCPv4 Protocol driver is in any state.
1062 
1063   @param[in]  This                  Pointer to the EFI_DHCP4_PROTOCOL instance.
1064 
1065   @retval EFI_SUCCESS           The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase.
1066   @retval EFI_INVALID_PARAMETER This is NULL.
1067 
1068 **/
1069 EFI_STATUS
1070 EFIAPI
EfiDhcp4Stop(IN EFI_DHCP4_PROTOCOL * This)1071 EfiDhcp4Stop (
1072   IN EFI_DHCP4_PROTOCOL     *This
1073   )
1074 {
1075   DHCP_PROTOCOL             *Instance;
1076   DHCP_SERVICE              *DhcpSb;
1077   EFI_TPL                   OldTpl;
1078 
1079   //
1080   // First validate the parameters
1081   //
1082   if (This == NULL) {
1083     return EFI_INVALID_PARAMETER;
1084   }
1085 
1086   Instance = DHCP_INSTANCE_FROM_THIS (This);
1087 
1088   if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
1089     return EFI_INVALID_PARAMETER;
1090   }
1091 
1092   OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);
1093   DhcpSb  = Instance->Service;
1094 
1095   DhcpCleanLease (DhcpSb);
1096 
1097   DhcpSb->DhcpState     = Dhcp4Stopped;
1098   DhcpSb->ServiceState  = DHCP_UNCONFIGED;
1099 
1100   gBS->RestoreTPL (OldTpl);
1101   return EFI_SUCCESS;
1102 }
1103 
1104 
1105 /**
1106   Builds a DHCP packet, given the options to be appended or deleted or replaced.
1107 
1108   The Build() function is used to assemble a new packet from the original packet
1109   by replacing or deleting existing options or appending new options. This function
1110   does not change any state of the EFI DHCPv4 Protocol driver and can be used at
1111   any time.
1112 
1113   @param[in]  This        Pointer to the EFI_DHCP4_PROTOCOL instance.
1114   @param[in]  SeedPacket  Initial packet to be used as a base for building new packet.
1115   @param[in]  DeleteCount Number of opcodes in the DeleteList.
1116   @param[in]  DeleteList  List of opcodes to be deleted from the seed packet.
1117                           Ignored if DeleteCount is zero.
1118   @param[in]  AppendCount Number of entries in the OptionList.
1119   @param[in]  AppendList  Pointer to a DHCP option list to be appended to SeedPacket.
1120                           If SeedPacket also contains options in this list, they are
1121                           replaced by new options (except pad option). Ignored if
1122                           AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION
1123   @param[out] NewPacket   Pointer to storage for the pointer to the new allocated packet.
1124                           Use the EFI Boot Service FreePool() on the resulting pointer
1125                           when done with the packet.
1126 
1127   @retval EFI_SUCCESS           The new packet was built.
1128   @retval EFI_OUT_OF_RESOURCES  Storage for the new packet could not be allocated.
1129   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
1130 
1131 **/
1132 EFI_STATUS
1133 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)1134 EfiDhcp4Build (
1135   IN EFI_DHCP4_PROTOCOL       *This,
1136   IN EFI_DHCP4_PACKET         *SeedPacket,
1137   IN UINT32                   DeleteCount,
1138   IN UINT8                    *DeleteList OPTIONAL,
1139   IN UINT32                   AppendCount,
1140   IN EFI_DHCP4_PACKET_OPTION  *AppendList[] OPTIONAL,
1141   OUT EFI_DHCP4_PACKET        **NewPacket
1142   )
1143 {
1144   //
1145   // First validate the parameters
1146   //
1147   if ((This == NULL) || (NewPacket == NULL)) {
1148     return EFI_INVALID_PARAMETER;
1149   }
1150 
1151   if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
1152       EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) {
1153 
1154     return EFI_INVALID_PARAMETER;
1155   }
1156 
1157   if (((DeleteCount == 0) && (AppendCount == 0)) ||
1158       ((DeleteCount != 0) && (DeleteList == NULL)) ||
1159       ((AppendCount != 0) && (AppendList == NULL))) {
1160 
1161     return EFI_INVALID_PARAMETER;
1162   }
1163 
1164   return DhcpBuild (
1165            SeedPacket,
1166            DeleteCount,
1167            DeleteList,
1168            AppendCount,
1169            AppendList,
1170            NewPacket
1171            );
1172 }
1173 
1174 /**
1175   Callback by UdpIoCreatePort() when creating UdpIo for this Dhcp4 instance.
1176 
1177   @param[in] UdpIo      The UdpIo being created.
1178   @param[in] Context    Dhcp4 instance.
1179 
1180   @retval EFI_SUCCESS   UdpIo is configured successfully.
1181   @retval other         Other error occurs.
1182 **/
1183 EFI_STATUS
1184 EFIAPI
Dhcp4InstanceConfigUdpIo(IN UDP_IO * UdpIo,IN VOID * Context)1185 Dhcp4InstanceConfigUdpIo (
1186   IN UDP_IO       *UdpIo,
1187   IN VOID         *Context
1188   )
1189 {
1190   DHCP_PROTOCOL                     *Instance;
1191   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
1192   EFI_UDP4_CONFIG_DATA              UdpConfigData;
1193   IP4_ADDR                          ClientAddr;
1194   IP4_ADDR                          Ip;
1195   INTN                              Class;
1196   IP4_ADDR                          SubnetMask;
1197 
1198   Instance = (DHCP_PROTOCOL *) Context;
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   Class = NetGetIpClass (ClientAddr);
1213   ASSERT (Class < IP4_ADDR_CLASSE);
1214   SubnetMask = gIp4AllMasks[Class << 3];
1215   Ip = HTONL (SubnetMask);
1216   CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
1217 
1218   if ((Token->ListenPointCount == 0) || (Token->ListenPoints[0].ListenPort == 0)) {
1219     UdpConfigData.StationPort = DHCP_CLIENT_PORT;
1220   } else {
1221     UdpConfigData.StationPort = Token->ListenPoints[0].ListenPort;
1222   }
1223 
1224   return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
1225 }
1226 
1227 /**
1228   Create UdpIo for this Dhcp4 instance.
1229 
1230   @param Instance   The Dhcp4 instance.
1231 
1232   @retval EFI_SUCCESS                UdpIo is created successfully.
1233   @retval EFI_OUT_OF_RESOURCES       Fails to create UdpIo because of limited
1234                                      resources or configuration failure.
1235 **/
1236 EFI_STATUS
Dhcp4InstanceCreateUdpIo(IN OUT DHCP_PROTOCOL * Instance)1237 Dhcp4InstanceCreateUdpIo (
1238   IN OUT DHCP_PROTOCOL  *Instance
1239   )
1240 {
1241   DHCP_SERVICE  *DhcpSb;
1242   EFI_STATUS    Status;
1243   VOID          *Udp4;
1244 
1245   ASSERT (Instance->Token != NULL);
1246 
1247   DhcpSb          = Instance->Service;
1248   Instance->UdpIo = UdpIoCreateIo (
1249                       DhcpSb->Controller,
1250                       DhcpSb->Image,
1251                       Dhcp4InstanceConfigUdpIo,
1252                       UDP_IO_UDP4_VERSION,
1253                       Instance
1254                       );
1255   if (Instance->UdpIo == NULL) {
1256     return EFI_OUT_OF_RESOURCES;
1257   } else {
1258     Status = gBS->OpenProtocol (
1259                     Instance->UdpIo->UdpHandle,
1260                     &gEfiUdp4ProtocolGuid,
1261                     (VOID **) &Udp4,
1262                     Instance->Service->Image,
1263                     Instance->Handle,
1264                     EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
1265                     );
1266     if (EFI_ERROR (Status)) {
1267       UdpIoFreeIo (Instance->UdpIo);
1268       Instance->UdpIo = NULL;
1269     }
1270     return Status;
1271   }
1272 }
1273 
1274 /**
1275   Callback of Dhcp packet. Does nothing.
1276 
1277   @param Arg           The context.
1278 
1279 **/
1280 VOID
1281 EFIAPI
DhcpDummyExtFree(IN VOID * Arg)1282 DhcpDummyExtFree (
1283   IN VOID                   *Arg
1284   )
1285 {
1286 }
1287 
1288 /**
1289   Callback of UdpIoRecvDatagram() that handles a Dhcp4 packet.
1290 
1291   Only BOOTP responses will be handled that correspond to the Xid of the request
1292   sent out. The packet will be queued to the response queue.
1293 
1294   @param UdpPacket        The Dhcp4 packet.
1295   @param EndPoint         Udp4 address pair.
1296   @param IoStatus         Status of the input.
1297   @param Context          Extra info for the input.
1298 
1299 **/
1300 VOID
1301 EFIAPI
PxeDhcpInput(NET_BUF * UdpPacket,UDP_END_POINT * EndPoint,EFI_STATUS IoStatus,VOID * Context)1302 PxeDhcpInput (
1303   NET_BUF                   *UdpPacket,
1304   UDP_END_POINT             *EndPoint,
1305   EFI_STATUS                IoStatus,
1306   VOID                      *Context
1307   )
1308 {
1309   DHCP_PROTOCOL                     *Instance;
1310   EFI_DHCP4_HEADER                  *Head;
1311   NET_BUF                           *Wrap;
1312   EFI_DHCP4_PACKET                  *Packet;
1313   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
1314   UINT32                            Len;
1315   EFI_STATUS                        Status;
1316 
1317   Wrap     = NULL;
1318   Instance = (DHCP_PROTOCOL *) Context;
1319   Token    = Instance->Token;
1320 
1321   //
1322   // Don't restart receive if error occurs or DHCP is destroyed.
1323   //
1324   if (EFI_ERROR (IoStatus)) {
1325     return ;
1326   }
1327 
1328   ASSERT (UdpPacket != NULL);
1329 
1330   //
1331   // Validate the packet received
1332   //
1333   if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
1334     goto RESTART;
1335   }
1336 
1337   //
1338   // Copy the DHCP message to a continuous memory block, make the buffer size
1339   // of the EFI_DHCP4_PACKET a multiple of 4-byte.
1340   //
1341   Len  = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4);
1342   Wrap = NetbufAlloc (Len);
1343   if (Wrap == NULL) {
1344     goto RESTART;
1345   }
1346 
1347   Packet         = (EFI_DHCP4_PACKET *) NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL);
1348   ASSERT (Packet != NULL);
1349 
1350   Packet->Size   = Len;
1351   Head           = &Packet->Dhcp4.Header;
1352   Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
1353 
1354   if (Packet->Length != UdpPacket->TotalSize) {
1355     goto RESTART;
1356   }
1357 
1358   //
1359   // Is this packet the answer to our packet?
1360   //
1361   if ((Head->OpCode != BOOTP_REPLY) ||
1362       (Head->Xid != Token->Packet->Dhcp4.Header.Xid) ||
1363       (CompareMem (&Token->Packet->Dhcp4.Header.ClientHwAddr[0], Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
1364     goto RESTART;
1365   }
1366 
1367   //
1368   // Validate the options and retrieve the interested options
1369   //
1370   if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
1371       (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
1372       EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
1373 
1374     goto RESTART;
1375   }
1376 
1377   //
1378   // Keep this packet in the ResponseQueue.
1379   //
1380   NET_GET_REF (Wrap);
1381   NetbufQueAppend (&Instance->ResponseQueue, Wrap);
1382 
1383 RESTART:
1384 
1385   NetbufFree (UdpPacket);
1386 
1387   if (Wrap != NULL) {
1388     NetbufFree (Wrap);
1389   }
1390 
1391   Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
1392   if (EFI_ERROR (Status)) {
1393     PxeDhcpDone (Instance);
1394   }
1395 }
1396 
1397 /**
1398   Complete a Dhcp4 transaction and signal the upper layer.
1399 
1400   @param Instance      Dhcp4 instance.
1401 
1402 **/
1403 VOID
PxeDhcpDone(IN DHCP_PROTOCOL * Instance)1404 PxeDhcpDone (
1405   IN DHCP_PROTOCOL  *Instance
1406   )
1407 {
1408   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token;
1409 
1410   Token = Instance->Token;
1411 
1412   Token->ResponseCount = Instance->ResponseQueue.BufNum;
1413   if (Token->ResponseCount != 0) {
1414     Token->ResponseList = (EFI_DHCP4_PACKET *) AllocatePool (Instance->ResponseQueue.BufSize);
1415     if (Token->ResponseList == NULL) {
1416       Token->Status = EFI_OUT_OF_RESOURCES;
1417       goto SIGNAL_USER;
1418     }
1419 
1420     //
1421     // Copy the received DHCP responses.
1422     //
1423     NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *) Token->ResponseList);
1424     Token->Status = EFI_SUCCESS;
1425   } else {
1426     Token->ResponseList = NULL;
1427     Token->Status       = EFI_TIMEOUT;
1428   }
1429 
1430 SIGNAL_USER:
1431   //
1432   // Clean up the resources dedicated for this transmit receive transaction.
1433   //
1434   NetbufQueFlush (&Instance->ResponseQueue);
1435   UdpIoCleanIo (Instance->UdpIo);
1436   gBS->CloseProtocol (
1437          Instance->UdpIo->UdpHandle,
1438          &gEfiUdp4ProtocolGuid,
1439          Instance->Service->Image,
1440          Instance->Handle
1441          );
1442   UdpIoFreeIo (Instance->UdpIo);
1443   Instance->UdpIo = NULL;
1444   Instance->Token = NULL;
1445 
1446   if (Token->CompletionEvent != NULL) {
1447     gBS->SignalEvent (Token->CompletionEvent);
1448   }
1449 }
1450 
1451 
1452 /**
1453   Transmits a DHCP formatted packet and optionally waits for responses.
1454 
1455   The TransmitReceive() function is used to transmit a DHCP packet and optionally
1456   wait for the response from servers. This function does not change the state of
1457   the EFI DHCPv4 Protocol driver and thus can be used at any time.
1458 
1459   @param[in]  This    Pointer to the EFI_DHCP4_PROTOCOL instance.
1460   @param[in]  Token   Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
1461 
1462   @retval EFI_SUCCESS           The packet was successfully queued for transmission.
1463   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
1464   @retval EFI_NOT_READY         The previous call to this function has not finished yet. Try to call
1465                                 this function after collection process completes.
1466   @retval EFI_NO_MAPPING        The default station address is not available yet.
1467   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
1468   @retval Others                Some other unexpected error occurred.
1469 
1470 **/
1471 EFI_STATUS
1472 EFIAPI
EfiDhcp4TransmitReceive(IN EFI_DHCP4_PROTOCOL * This,IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN * Token)1473 EfiDhcp4TransmitReceive (
1474   IN EFI_DHCP4_PROTOCOL                *This,
1475   IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN  *Token
1476   )
1477 {
1478   DHCP_PROTOCOL  *Instance;
1479   EFI_TPL        OldTpl;
1480   EFI_STATUS     Status;
1481   NET_FRAGMENT   Frag;
1482   NET_BUF        *Wrap;
1483   UDP_END_POINT  EndPoint;
1484   IP4_ADDR       Ip;
1485   DHCP_SERVICE   *DhcpSb;
1486   EFI_IP_ADDRESS Gateway;
1487   IP4_ADDR       ClientAddr;
1488   INTN           Class;
1489   IP4_ADDR       SubnetMask;
1490 
1491   if ((This == NULL) || (Token == NULL) || (Token->Packet == NULL)) {
1492     return EFI_INVALID_PARAMETER;
1493   }
1494 
1495   Instance = DHCP_INSTANCE_FROM_THIS (This);
1496   DhcpSb   = Instance->Service;
1497 
1498   if (Instance->Token != NULL) {
1499     //
1500     // The previous call to TransmitReceive is not finished.
1501     //
1502     return EFI_NOT_READY;
1503   }
1504 
1505   if ((Token->Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC)                   ||
1506       (NTOHL (Token->Packet->Dhcp4.Header.Xid) == Instance->Service->Xid) ||
1507       (Token->TimeoutValue == 0)                                          ||
1508       ((Token->ListenPointCount != 0) && (Token->ListenPoints == NULL))   ||
1509       EFI_ERROR (DhcpValidateOptions (Token->Packet, NULL))               ||
1510       EFI_IP4_EQUAL (&Token->RemoteAddress, &mZeroIp4Addr)
1511       ) {
1512     //
1513     // The DHCP packet isn't well-formed, the Transaction ID is already used,
1514     // the timeout value is zero, the ListenPoint is invalid, or the
1515     // RemoteAddress is zero.
1516     //
1517     return EFI_INVALID_PARAMETER;
1518   }
1519 
1520   ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr);
1521 
1522   if (ClientAddr == 0) {
1523     return EFI_NO_MAPPING;
1524   }
1525 
1526   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
1527 
1528   //
1529   // Save the token and the timeout value.
1530   //
1531   Instance->Token   = Token;
1532   Instance->Timeout = Token->TimeoutValue;
1533 
1534   //
1535   // Create a UDP IO for this transmit receive transaction.
1536   //
1537   Status = Dhcp4InstanceCreateUdpIo (Instance);
1538   if (EFI_ERROR (Status)) {
1539     goto ON_ERROR;
1540   }
1541 
1542   //
1543   // Save the Client Address is sent out
1544   //
1545   CopyMem (
1546     &DhcpSb->ClientAddressSendOut[0],
1547     &Token->Packet->Dhcp4.Header.ClientHwAddr[0],
1548     Token->Packet->Dhcp4.Header.HwAddrLen
1549     );
1550 
1551   //
1552   // Wrap the DHCP packet into a net buffer.
1553   //
1554   Frag.Bulk = (UINT8 *) &Token->Packet->Dhcp4;
1555   Frag.Len  = Token->Packet->Length;
1556   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
1557   if (Wrap == NULL) {
1558     Status = EFI_OUT_OF_RESOURCES;
1559     goto ON_ERROR;
1560   }
1561 
1562   //
1563   // Set the local address and local port to ZERO.
1564   //
1565   ZeroMem (&EndPoint, sizeof (UDP_END_POINT));
1566 
1567   //
1568   // Set the destination address and destination port.
1569   //
1570   CopyMem (&Ip, &Token->RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
1571   EndPoint.RemoteAddr.Addr[0] = NTOHL (Ip);
1572 
1573   if (Token->RemotePort == 0) {
1574     EndPoint.RemotePort = DHCP_SERVER_PORT;
1575   } else {
1576     EndPoint.RemotePort = Token->RemotePort;
1577   }
1578 
1579   //
1580   // Get the gateway.
1581   //
1582   Class = NetGetIpClass (ClientAddr);
1583   ASSERT (Class < IP4_ADDR_CLASSE);
1584   SubnetMask = gIp4AllMasks[Class << 3];
1585   ZeroMem (&Gateway, sizeof (Gateway));
1586   if (!IP4_NET_EQUAL (ClientAddr, EndPoint.RemoteAddr.Addr[0], SubnetMask)) {
1587     CopyMem (&Gateway.v4, &Token->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
1588     Gateway.Addr[0] = NTOHL (Gateway.Addr[0]);
1589   }
1590 
1591   //
1592   // Transmit the DHCP packet.
1593   //
1594   Status = UdpIoSendDatagram (Instance->UdpIo, Wrap, &EndPoint, &Gateway, DhcpOnPacketSent, NULL);
1595   if (EFI_ERROR (Status)) {
1596     NetbufFree (Wrap);
1597     goto ON_ERROR;
1598   }
1599 
1600   //
1601   // Start to receive the DHCP response.
1602   //
1603   Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
1604   if (EFI_ERROR (Status)) {
1605     goto ON_ERROR;
1606   }
1607 
1608 ON_ERROR:
1609 
1610   if (EFI_ERROR (Status) && (Instance->UdpIo != NULL)) {
1611     UdpIoCleanIo (Instance->UdpIo);
1612     gBS->CloseProtocol (
1613            Instance->UdpIo->UdpHandle,
1614            &gEfiUdp4ProtocolGuid,
1615            Instance->Service->Image,
1616            Instance->Handle
1617            );
1618     UdpIoFreeIo (Instance->UdpIo);
1619     Instance->UdpIo = NULL;
1620     Instance->Token = NULL;
1621   }
1622 
1623   gBS->RestoreTPL (OldTpl);
1624 
1625   if (!EFI_ERROR (Status) && (Token->CompletionEvent == NULL)) {
1626     //
1627     // Keep polling until timeout if no error happens and the CompletionEvent
1628     // is NULL.
1629     //
1630     while (TRUE) {
1631       OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
1632       //
1633       // Raise TPL to protect the UDPIO in instance, in case that DhcpOnTimerTick
1634       // free it when timeout.
1635       //
1636       if (Instance->Timeout > 0) {
1637         Instance->UdpIo->Protocol.Udp4->Poll (Instance->UdpIo->Protocol.Udp4);
1638         gBS->RestoreTPL (OldTpl);
1639       } else {
1640         gBS->RestoreTPL (OldTpl);
1641         break;
1642       }
1643     }
1644   }
1645 
1646   return Status;
1647 }
1648 
1649 
1650 /**
1651   Callback function for DhcpIterateOptions. This callback sets the
1652   EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point
1653   the individual DHCP option in the packet.
1654 
1655   @param[in]  Tag                    The DHCP option type
1656   @param[in]  Len                    Length of the DHCP option data
1657   @param[in]  Data                   The DHCP option data
1658   @param[in]  Context                The context, to pass several parameters in.
1659 
1660   @retval EFI_SUCCESS            It always returns EFI_SUCCESS
1661 
1662 **/
1663 EFI_STATUS
Dhcp4ParseCheckOption(IN UINT8 Tag,IN UINT8 Len,IN UINT8 * Data,IN VOID * Context)1664 Dhcp4ParseCheckOption (
1665   IN UINT8                  Tag,
1666   IN UINT8                  Len,
1667   IN UINT8                  *Data,
1668   IN VOID                   *Context
1669   )
1670 {
1671   DHCP_PARSE_CONTEXT        *Parse;
1672 
1673   Parse = (DHCP_PARSE_CONTEXT *) Context;
1674   Parse->Index++;
1675 
1676   if (Parse->Index <= Parse->OptionCount) {
1677     //
1678     // Use BASE_CR to get the memory position of EFI_DHCP4_PACKET_OPTION for
1679     // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only
1680     // pass in the point to option data.
1681     //
1682     Parse->Option[Parse->Index - 1] = BASE_CR (Data, EFI_DHCP4_PACKET_OPTION, Data);
1683   }
1684 
1685   return EFI_SUCCESS;
1686 }
1687 
1688 
1689 /**
1690   Parses the packed DHCP option data.
1691 
1692   The Parse() function is used to retrieve the option list from a DHCP packet.
1693   If *OptionCount isn't zero, and there is enough space for all the DHCP options
1694   in the Packet, each element of PacketOptionList is set to point to somewhere in
1695   the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported,
1696   the caller should reassemble the parsed DHCP options to get the finial result.
1697   If *OptionCount is zero or there isn't enough space for all of them, the number
1698   of DHCP options in the Packet is returned in OptionCount.
1699 
1700   @param  This             Pointer to the EFI_DHCP4_PROTOCOL instance.
1701   @param  Packet           Pointer to packet to be parsed.
1702   @param  OptionCount      On input, the number of entries in the PacketOptionList.
1703                            On output, the number of entries that were written into the
1704                            PacketOptionList.
1705   @param  PacketOptionList List of packet option entries to be filled in. End option or pad
1706                            options are not included.
1707 
1708   @retval EFI_SUCCESS           The packet was successfully parsed.
1709   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
1710   @retval EFI_BUFFER_TOO_SMALL  One or more of the following conditions is TRUE:
1711                                 1) *OptionCount is smaller than the number of options that
1712                                 were found in the Packet.
1713                                 2) PacketOptionList is NULL.
1714 
1715 **/
1716 EFI_STATUS
1717 EFIAPI
EfiDhcp4Parse(IN EFI_DHCP4_PROTOCOL * This,IN EFI_DHCP4_PACKET * Packet,IN OUT UINT32 * OptionCount,OUT EFI_DHCP4_PACKET_OPTION * PacketOptionList[]OPTIONAL)1718 EfiDhcp4Parse (
1719   IN EFI_DHCP4_PROTOCOL       *This,
1720   IN EFI_DHCP4_PACKET         *Packet,
1721   IN OUT UINT32               *OptionCount,
1722   OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
1723   )
1724 {
1725   DHCP_PARSE_CONTEXT        Context;
1726   EFI_STATUS                Status;
1727 
1728   //
1729   // First validate the parameters
1730   //
1731   if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {
1732     return EFI_INVALID_PARAMETER;
1733   }
1734 
1735   if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) ||
1736       (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
1737       EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
1738 
1739     return EFI_INVALID_PARAMETER;
1740   }
1741 
1742   if ((*OptionCount != 0) && (PacketOptionList == NULL)) {
1743     return EFI_BUFFER_TOO_SMALL;
1744   }
1745 
1746   ZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
1747 
1748   Context.Option      = PacketOptionList;
1749   Context.OptionCount = *OptionCount;
1750   Context.Index       = 0;
1751 
1752   Status              = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context);
1753 
1754   if (EFI_ERROR (Status)) {
1755     return Status;
1756   }
1757 
1758   *OptionCount = Context.Index;
1759 
1760   if (Context.Index > Context.OptionCount) {
1761     return EFI_BUFFER_TOO_SMALL;
1762   }
1763 
1764   return EFI_SUCCESS;
1765 }
1766 
1767 /**
1768   Set the elapsed time based on the given instance and the pointer to the
1769   elapsed time option.
1770 
1771   @param[in]      Elapsed       The pointer to the position to append.
1772   @param[in]      Instance      The pointer to the Dhcp4 instance.
1773 **/
1774 VOID
SetElapsedTime(IN UINT16 * Elapsed,IN DHCP_PROTOCOL * Instance)1775 SetElapsedTime (
1776   IN     UINT16                 *Elapsed,
1777   IN     DHCP_PROTOCOL          *Instance
1778   )
1779 {
1780   WriteUnaligned16 (Elapsed, HTONS(Instance->ElaspedTime));
1781 }
1782