Home | History | Annotate | Download | only in Dhcp6Dxe
      1 /** @file
      2   This EFI_DHCP6_PROTOCOL interface implementation.
      3 
      4   Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
      5 
      6   This program and the accompanying materials
      7   are licensed and made available under the terms and conditions of the BSD License
      8   which accompanies this distribution.  The full text of the license may be found at
      9   http://opensource.org/licenses/bsd-license.php.
     10 
     11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include "Dhcp6Impl.h"
     17 
     18 //
     19 // Well-known multi-cast address defined in section-24.1 of rfc-3315
     20 //
     21 //   ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
     22 //   ALL_DHCP_Servers address:                  FF05::1:3
     23 //
     24 EFI_IPv6_ADDRESS   mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
     25 EFI_IPv6_ADDRESS   mAllDhcpServersAddress         = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}};
     26 
     27 EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = {
     28   EfiDhcp6GetModeData,
     29   EfiDhcp6Configure,
     30   EfiDhcp6Start,
     31   EfiDhcp6InfoRequest,
     32   EfiDhcp6RenewRebind,
     33   EfiDhcp6Decline,
     34   EfiDhcp6Release,
     35   EfiDhcp6Stop,
     36   EfiDhcp6Parse
     37 };
     38 
     39 /**
     40   Starts the DHCPv6 standard S.A.R.R. process.
     41 
     42   The Start() function starts the DHCPv6 standard process. This function can
     43   be called only when the state of Dhcp6 instance is in the Dhcp6Init state.
     44   If the DHCP process completes successfully, the state of the Dhcp6 instance
     45   will be transferred through Dhcp6Selecting and Dhcp6Requesting to the
     46   Dhcp6Bound state.
     47   Refer to rfc-3315 for precise state transitions during this process. At the
     48   time when each event occurs in this process, the callback function that was set
     49   by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this
     50   opportunity to control the process.
     51 
     52   @param[in]  This              The pointer to Dhcp6 protocol.
     53 
     54   @retval EFI_SUCCESS           The DHCPv6 standard process has started, or it has
     55                                 completed when CompletionEvent is NULL.
     56   @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Child instance hasn't been configured.
     57   @retval EFI_INVALID_PARAMETER This is NULL.
     58   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
     59   @retval EFI_TIMEOUT           The DHCPv6 configuration process failed because no
     60                                 response was received from the server within the
     61                                 specified timeout value.
     62   @retval EFI_ABORTED           The user aborted the DHCPv6 process.
     63   @retval EFI_ALREADY_STARTED   Some other Dhcp6 instance already started the DHCPv6
     64                                 standard process.
     65   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
     66   @retval EFI_NO_MEDIA          There was a media error.
     67 
     68 **/
     69 EFI_STATUS
     70 EFIAPI
     71 EfiDhcp6Start (
     72   IN EFI_DHCP6_PROTOCOL        *This
     73   )
     74 {
     75   EFI_STATUS                   Status;
     76   EFI_TPL                      OldTpl;
     77   DHCP6_INSTANCE               *Instance;
     78   DHCP6_SERVICE                *Service;
     79 
     80   if (This == NULL) {
     81     return EFI_INVALID_PARAMETER;
     82   }
     83 
     84   Instance = DHCP6_INSTANCE_FROM_THIS (This);
     85   Service  = Instance->Service;
     86 
     87   //
     88   // The instance hasn't been configured.
     89   //
     90   if (Instance->Config == NULL) {
     91     return EFI_ACCESS_DENIED;
     92   }
     93 
     94   ASSERT (Instance->IaCb.Ia != NULL);
     95 
     96   //
     97   // The instance has already been started.
     98   //
     99   if (Instance->IaCb.Ia->State != Dhcp6Init) {
    100     return EFI_ALREADY_STARTED;
    101   }
    102 
    103   OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);
    104   Instance->UdpSts = EFI_ALREADY_STARTED;
    105 
    106   //
    107   // Send the solicit message to start S.A.R.R process.
    108   //
    109   Status = Dhcp6SendSolicitMsg (Instance);
    110 
    111   if (EFI_ERROR (Status)) {
    112     goto ON_ERROR;
    113   }
    114 
    115   //
    116   // Register receive callback for the stateful exchange process.
    117   //
    118   Status = UdpIoRecvDatagram(
    119              Service->UdpIo,
    120              Dhcp6ReceivePacket,
    121              Service,
    122              0
    123              );
    124 
    125   if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
    126     goto ON_ERROR;
    127   }
    128 
    129   gBS->RestoreTPL (OldTpl);
    130 
    131   //
    132   // Poll udp out of the net tpl if synchronous call.
    133   //
    134   if (Instance->Config->IaInfoEvent == NULL) {
    135 
    136     while (Instance->UdpSts == EFI_ALREADY_STARTED) {
    137       Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
    138     }
    139     return Instance->UdpSts;
    140   }
    141 
    142   return EFI_SUCCESS;
    143 
    144 ON_ERROR:
    145 
    146   gBS->RestoreTPL (OldTpl);
    147   return Status;
    148 }
    149 
    150 
    151 /**
    152   Stops the DHCPv6 standard S.A.R.R. process.
    153 
    154   The Stop() function is used to stop the DHCPv6 standard process. After this
    155   function is called successfully, the state of Dhcp6 instance is transferred
    156   into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called
    157   before DHCPv6 standard process can be started again. This function can be
    158   called when the Dhcp6 instance is in any state.
    159 
    160   @param[in]  This              The pointer to the Dhcp6 protocol.
    161 
    162   @retval EFI_SUCCESS           The Dhcp6 instance is now in the Dhcp6Init state.
    163   @retval EFI_INVALID_PARAMETER This is NULL.
    164 
    165 **/
    166 EFI_STATUS
    167 EFIAPI
    168 EfiDhcp6Stop (
    169   IN EFI_DHCP6_PROTOCOL        *This
    170   )
    171 {
    172   EFI_TPL                      OldTpl;
    173   EFI_STATUS                   Status;
    174   EFI_UDP6_PROTOCOL            *Udp6;
    175   DHCP6_INSTANCE               *Instance;
    176   DHCP6_SERVICE                *Service;
    177 
    178   if (This == NULL) {
    179     return EFI_INVALID_PARAMETER;
    180   }
    181 
    182   Instance = DHCP6_INSTANCE_FROM_THIS (This);
    183   Service  = Instance->Service;
    184   Udp6     = Service->UdpIo->Protocol.Udp6;
    185   Status   = EFI_SUCCESS;
    186 
    187   //
    188   // The instance hasn't been configured.
    189   //
    190   if (Instance->Config == NULL) {
    191     return Status;
    192   }
    193 
    194   ASSERT (Instance->IaCb.Ia != NULL);
    195 
    196   //
    197   // No valid REPLY message received yet, cleanup this instance directly.
    198   //
    199   if (Instance->IaCb.Ia->State == Dhcp6Init ||
    200       Instance->IaCb.Ia->State == Dhcp6Selecting ||
    201       Instance->IaCb.Ia->State == Dhcp6Requesting
    202       ) {
    203     goto ON_EXIT;
    204   }
    205 
    206   //
    207   // Release the current ready Ia.
    208   //
    209   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
    210 
    211   Instance->UdpSts = EFI_ALREADY_STARTED;
    212   Status = Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia);
    213   gBS->RestoreTPL (OldTpl);
    214   if (EFI_ERROR (Status)) {
    215     goto ON_EXIT;
    216   }
    217 
    218   //
    219   // Poll udp out of the net tpl if synchoronus call.
    220   //
    221   if (Instance->Config->IaInfoEvent == NULL) {
    222     ASSERT (Udp6 != NULL);
    223     while (Instance->UdpSts == EFI_ALREADY_STARTED) {
    224       Udp6->Poll (Udp6);
    225     }
    226     Status = Instance->UdpSts;
    227   }
    228 
    229 ON_EXIT:
    230   //
    231   // Clean up the session data for the released Ia.
    232   //
    233   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
    234   Dhcp6CleanupSession (Instance, EFI_SUCCESS);
    235   gBS->RestoreTPL (OldTpl);
    236 
    237   return Status;
    238 }
    239 
    240 
    241 /**
    242   Returns the current operating mode data for the Dhcp6 instance.
    243 
    244   The GetModeData() function returns the current operating mode and
    245   cached data packet for the Dhcp6 instance.
    246 
    247   @param[in]  This              The pointer to the Dhcp6 protocol.
    248   @param[out] Dhcp6ModeData     The pointer to the Dhcp6 mode data.
    249   @param[out] Dhcp6ConfigData   The pointer to the Dhcp6 configure data.
    250 
    251   @retval EFI_SUCCESS           The mode data was returned.
    252   @retval EFI_INVALID_PARAMETER This is NULL.
    253   @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Protocol instance was not
    254                                 configured.
    255 **/
    256 EFI_STATUS
    257 EFIAPI
    258 EfiDhcp6GetModeData (
    259   IN  EFI_DHCP6_PROTOCOL       *This,
    260   OUT EFI_DHCP6_MODE_DATA      *Dhcp6ModeData      OPTIONAL,
    261   OUT EFI_DHCP6_CONFIG_DATA    *Dhcp6ConfigData    OPTIONAL
    262   )
    263 {
    264   EFI_TPL                      OldTpl;
    265   EFI_DHCP6_IA                 *Ia;
    266   DHCP6_INSTANCE               *Instance;
    267   DHCP6_SERVICE                *Service;
    268   UINT32                       IaSize;
    269   UINT32                       IdSize;
    270 
    271   if (This == NULL || (Dhcp6ModeData == NULL && Dhcp6ConfigData == NULL)) {
    272     return EFI_INVALID_PARAMETER;
    273   }
    274 
    275   Instance = DHCP6_INSTANCE_FROM_THIS (This);
    276   Service  = Instance->Service;
    277 
    278   if (Instance->Config == NULL && Dhcp6ConfigData != NULL) {
    279     return EFI_ACCESS_DENIED;
    280   }
    281 
    282   ASSERT (Service->ClientId != NULL);
    283 
    284   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
    285 
    286   //
    287   // User needs a copy of instance config data.
    288   //
    289   if (Dhcp6ConfigData != NULL) {
    290     ZeroMem (Dhcp6ConfigData, sizeof(EFI_DHCP6_CONFIG_DATA));
    291     //
    292     // Duplicate config data, including all reference buffers.
    293     //
    294     if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) {
    295       goto ON_ERROR;
    296     }
    297   }
    298 
    299   //
    300   // User need a copy of instance mode data.
    301   //
    302   if (Dhcp6ModeData != NULL) {
    303     ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA));
    304     //
    305     // Duplicate a copy of EFI_DHCP6_DUID for client Id.
    306     //
    307     IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length);
    308 
    309     Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize);
    310     if (Dhcp6ModeData->ClientId == NULL) {
    311       goto ON_ERROR;
    312     }
    313 
    314     CopyMem (
    315       Dhcp6ModeData->ClientId,
    316       Service->ClientId,
    317       IdSize
    318       );
    319 
    320     Ia = Instance->IaCb.Ia;
    321     if (Ia != NULL) {
    322       //
    323       // Duplicate a copy of EFI_DHCP6_IA for configured Ia.
    324       //
    325       IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS);
    326 
    327       Dhcp6ModeData->Ia = AllocateZeroPool (IaSize);
    328       if (Dhcp6ModeData->Ia == NULL) {
    329         goto ON_ERROR;
    330       }
    331 
    332       CopyMem (
    333         Dhcp6ModeData->Ia,
    334         Ia,
    335         IaSize
    336         );
    337 
    338       //
    339       // Duplicate a copy of reply packet if has.
    340       //
    341       if (Ia->ReplyPacket != NULL) {
    342         Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size);
    343         if (Dhcp6ModeData->Ia->ReplyPacket == NULL) {
    344           goto ON_ERROR;
    345         }
    346         CopyMem (
    347           Dhcp6ModeData->Ia->ReplyPacket,
    348           Ia->ReplyPacket,
    349           Ia->ReplyPacket->Size
    350           );
    351       }
    352     }
    353   }
    354 
    355   gBS->RestoreTPL (OldTpl);
    356 
    357   return EFI_SUCCESS;
    358 
    359 ON_ERROR:
    360 
    361   if (Dhcp6ConfigData != NULL) {
    362     Dhcp6CleanupConfigData (Dhcp6ConfigData);
    363   }
    364   if (Dhcp6ModeData != NULL) {
    365     Dhcp6CleanupModeData (Dhcp6ModeData);
    366   }
    367   gBS->RestoreTPL (OldTpl);
    368 
    369   return EFI_OUT_OF_RESOURCES;
    370 }
    371 
    372 
    373 /**
    374   Initializes, changes, or resets the operational settings for the Dhcp6 instance.
    375 
    376   The Configure() function is used to initialize or clean up the configuration
    377   data of the Dhcp6 instance:
    378   - When Dhcp6CfgData is not NULL and Configure() is called successfully, the
    379     configuration data will be initialized in the Dhcp6 instance, and the state
    380     of the configured IA will be transferred into Dhcp6Init.
    381   - When Dhcp6CfgData is NULL and Configure() is called successfully, the
    382     configuration data will be cleaned up and no IA will be associated with
    383     the Dhcp6 instance.
    384   To update the configuration data for an Dhcp6 instance, the original data
    385   must be cleaned up before setting the new configuration data.
    386 
    387   @param[in]  This                   The pointer to the Dhcp6 protocol
    388   @param[in]  Dhcp6CfgData           The pointer to the EFI_DHCP6_CONFIG_DATA.
    389 
    390   @retval EFI_SUCCESS           The Dhcp6 is configured successfully with the
    391                                 Dhcp6Init state, or cleaned up the original
    392                                 configuration setting.
    393   @retval EFI_ACCESS_DENIED     The Dhcp6 instance was already configured.
    394                                 The Dhcp6 instance has already started the
    395                                 DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.
    396   @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.
    397   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
    398   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    399 
    400 **/
    401 EFI_STATUS
    402 EFIAPI
    403 EfiDhcp6Configure (
    404   IN EFI_DHCP6_PROTOCOL        *This,
    405   IN EFI_DHCP6_CONFIG_DATA     *Dhcp6CfgData    OPTIONAL
    406   )
    407 {
    408   EFI_TPL                      OldTpl;
    409   EFI_STATUS                   Status;
    410   LIST_ENTRY                   *Entry;
    411   DHCP6_INSTANCE               *Other;
    412   DHCP6_INSTANCE               *Instance;
    413   DHCP6_SERVICE                *Service;
    414   UINTN                        Index;
    415 
    416   if (This == NULL) {
    417     return EFI_INVALID_PARAMETER;
    418   }
    419 
    420   Instance = DHCP6_INSTANCE_FROM_THIS (This);
    421   Service  = Instance->Service;
    422 
    423   //
    424   // Check the parameter of configure data.
    425   //
    426   if (Dhcp6CfgData != NULL) {
    427     if (Dhcp6CfgData->OptionCount > 0 && Dhcp6CfgData->OptionList == NULL) {
    428       return EFI_INVALID_PARAMETER;
    429     }
    430     if (Dhcp6CfgData->OptionList != NULL) {
    431       for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) {
    432         if (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId ||
    433             Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit ||
    434             Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept ||
    435             Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana ||
    436             Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata
    437             ) {
    438           return EFI_INVALID_PARAMETER;
    439         }
    440       }
    441     }
    442 
    443     if (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA &&
    444         Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA
    445         ) {
    446       return EFI_INVALID_PARAMETER;
    447     }
    448 
    449     if (Dhcp6CfgData->IaInfoEvent == NULL && Dhcp6CfgData->SolicitRetransmission == NULL) {
    450       return EFI_INVALID_PARAMETER;
    451     }
    452 
    453     if (Dhcp6CfgData->SolicitRetransmission != NULL &&
    454         Dhcp6CfgData->SolicitRetransmission->Mrc == 0 &&
    455         Dhcp6CfgData->SolicitRetransmission->Mrd == 0
    456         ) {
    457       return EFI_INVALID_PARAMETER;
    458     }
    459 
    460     //
    461     // Make sure the (IaId, IaType) is unique over all the instances.
    462     //
    463     NET_LIST_FOR_EACH (Entry, &Service->Child) {
    464       Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link);
    465       if (Other->IaCb.Ia != NULL &&
    466           Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type &&
    467           Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId
    468           ) {
    469         return EFI_INVALID_PARAMETER;
    470       }
    471     }
    472   }
    473 
    474   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
    475 
    476   if (Dhcp6CfgData != NULL) {
    477     //
    478     // It's not allowed to configure one instance twice without configure null.
    479     //
    480     if (Instance->Config != NULL) {
    481       gBS->RestoreTPL (OldTpl);
    482       return EFI_ACCESS_DENIED;
    483     }
    484 
    485     //
    486     // Duplicate config data including all reference buffers.
    487     //
    488     Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA));
    489     if (Instance->Config == NULL) {
    490       gBS->RestoreTPL (OldTpl);
    491       return EFI_OUT_OF_RESOURCES;
    492     }
    493 
    494     Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData);
    495     if (EFI_ERROR(Status)) {
    496       FreePool (Instance->Config);
    497       gBS->RestoreTPL (OldTpl);
    498       return EFI_OUT_OF_RESOURCES;
    499     }
    500 
    501     //
    502     // Initialize the Ia descriptor from the config data, and leave the other
    503     // fields of the Ia as default value 0.
    504     //
    505     Instance->IaCb.Ia = AllocateZeroPool (sizeof(EFI_DHCP6_IA));
    506     if (Instance->IaCb.Ia == NULL) {
    507       Dhcp6CleanupConfigData (Instance->Config);
    508       FreePool (Instance->Config);
    509       gBS->RestoreTPL (OldTpl);
    510       return EFI_OUT_OF_RESOURCES;
    511     }
    512     CopyMem (
    513       &Instance->IaCb.Ia->Descriptor,
    514       &Dhcp6CfgData->IaDescriptor,
    515       sizeof(EFI_DHCP6_IA_DESCRIPTOR)
    516       );
    517 
    518   } else {
    519 
    520     if (Instance->Config == NULL) {
    521       ASSERT (Instance->IaCb.Ia == NULL);
    522       gBS->RestoreTPL (OldTpl);
    523       return EFI_SUCCESS;
    524     }
    525 
    526     //
    527     // It's not allowed to configure a started instance as null.
    528     //
    529     if (Instance->IaCb.Ia->State != Dhcp6Init) {
    530       gBS->RestoreTPL (OldTpl);
    531       return EFI_ACCESS_DENIED;
    532     }
    533 
    534     Dhcp6CleanupConfigData (Instance->Config);
    535     FreePool (Instance->Config);
    536     Instance->Config = NULL;
    537 
    538     FreePool (Instance->IaCb.Ia);
    539     Instance->IaCb.Ia = NULL;
    540   }
    541 
    542   gBS->RestoreTPL (OldTpl);
    543 
    544   return EFI_SUCCESS;
    545 }
    546 
    547 
    548 /**
    549   Request configuration information without the assignment of any
    550   Ia addresses of the client.
    551 
    552   The InfoRequest() function is used to request configuration information
    553   without the assignment of any IPv6 address of the client. The client sends
    554   out an Information Request packet to obtain the required configuration
    555   information, and DHCPv6 server responds with a Reply packet containing
    556   the information for the client. The received Reply packet will be passed
    557   to the user by ReplyCallback function. If the user returns EFI_NOT_READY from
    558   ReplyCallback, the Dhcp6 instance will continue to receive other Reply
    559   packets unless timeout according to the Retransmission parameter.
    560   Otherwise, the Information Request exchange process will be finished
    561   successfully if user returns EFI_SUCCESS from ReplyCallback.
    562 
    563   @param[in]  This              The pointer to the Dhcp6 protocol.
    564   @param[in]  SendClientId      If TRUE, the DHCPv6 protocol instance will build Client
    565                                 Identifier option and include it into Information Request
    566                                 packet. Otherwise, Client Identifier option will not be included.
    567   @param[in]  OptionRequest     The pointer to the buffer of option request options.
    568   @param[in]  OptionCount       The option number in the OptionList.
    569   @param[in]  OptionList        The list of appended options.
    570   @param[in]  Retransmission    The pointer to the retransmission of the message.
    571   @param[in]  TimeoutEvent      The event of timeout.
    572   @param[in]  ReplyCallback     The callback function when the reply was received.
    573   @param[in]  CallbackContext   The pointer to the parameter passed to the callback.
    574 
    575   @retval EFI_SUCCESS           The DHCPv6 information request exchange process
    576                                 completed when TimeoutEvent is NULL. Information
    577                                 Request packet has been sent to DHCPv6 server when
    578                                 TimeoutEvent is not NULL.
    579   @retval EFI_NO_RESPONSE       The DHCPv6 information request exchange process failed
    580                                 because of no response, or not all requested-options
    581                                 are responded by DHCPv6 servers when Timeout happened.
    582   @retval EFI_ABORTED           The DHCPv6 information request exchange process was aborted
    583                                 by user.
    584   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
    585   @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.
    586   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    587 
    588 **/
    589 EFI_STATUS
    590 EFIAPI
    591 EfiDhcp6InfoRequest (
    592   IN EFI_DHCP6_PROTOCOL        *This,
    593   IN BOOLEAN                   SendClientId,
    594   IN EFI_DHCP6_PACKET_OPTION   *OptionRequest,
    595   IN UINT32                    OptionCount,
    596   IN EFI_DHCP6_PACKET_OPTION   *OptionList[]    OPTIONAL,
    597   IN EFI_DHCP6_RETRANSMISSION  *Retransmission,
    598   IN EFI_EVENT                 TimeoutEvent     OPTIONAL,
    599   IN EFI_DHCP6_INFO_CALLBACK   ReplyCallback,
    600   IN VOID                      *CallbackContext OPTIONAL
    601   )
    602 {
    603   EFI_STATUS                   Status;
    604   DHCP6_INSTANCE               *Instance;
    605   DHCP6_SERVICE                *Service;
    606   UINTN                        Index;
    607   EFI_EVENT                    Timer;
    608   EFI_STATUS                   TimerStatus;
    609   UINTN                        GetMappingTimeOut;
    610 
    611   if (This == NULL || OptionRequest == NULL || Retransmission == NULL || ReplyCallback == NULL) {
    612     return EFI_INVALID_PARAMETER;
    613   }
    614 
    615   if (Retransmission != NULL && Retransmission->Mrc == 0 && Retransmission->Mrd == 0) {
    616     return EFI_INVALID_PARAMETER;
    617   }
    618 
    619   if (OptionCount > 0 && OptionList == NULL) {
    620     return EFI_INVALID_PARAMETER;
    621   }
    622 
    623   if (OptionList != NULL) {
    624     for (Index = 0; Index < OptionCount; Index++) {
    625       if (OptionList[Index]->OpCode == Dhcp6OptClientId || OptionList[Index]->OpCode == Dhcp6OptRequestOption) {
    626         return EFI_INVALID_PARAMETER;
    627       }
    628     }
    629   }
    630 
    631   Instance = DHCP6_INSTANCE_FROM_THIS (This);
    632   Service  = Instance->Service;
    633 
    634   Status = Dhcp6StartInfoRequest (
    635              Instance,
    636              SendClientId,
    637              OptionRequest,
    638              OptionCount,
    639              OptionList,
    640              Retransmission,
    641              TimeoutEvent,
    642              ReplyCallback,
    643              CallbackContext
    644              );
    645   if (Status == EFI_NO_MAPPING) {
    646     //
    647     // The link local address is not ready, wait for some time and restart
    648     // the DHCP6 information request process.
    649     //
    650     Status = Dhcp6GetMappingTimeOut(Service->Ip6Cfg, &GetMappingTimeOut);
    651     if (EFI_ERROR(Status)) {
    652       return Status;
    653     }
    654 
    655     Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
    656     if (EFI_ERROR (Status)) {
    657       return Status;
    658     }
    659 
    660     //
    661     // Start the timer, wait for link local address DAD to finish.
    662     //
    663     Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);
    664     if (EFI_ERROR (Status)) {
    665       gBS->CloseEvent (Timer);
    666       return Status;
    667     }
    668 
    669     do {
    670       TimerStatus = gBS->CheckEvent (Timer);
    671       if (!EFI_ERROR (TimerStatus)) {
    672         Status = Dhcp6StartInfoRequest (
    673                    Instance,
    674                    SendClientId,
    675                    OptionRequest,
    676                    OptionCount,
    677                    OptionList,
    678                    Retransmission,
    679                    TimeoutEvent,
    680                    ReplyCallback,
    681                    CallbackContext
    682                    );
    683       }
    684     } while (TimerStatus == EFI_NOT_READY);
    685 
    686     gBS->CloseEvent (Timer);
    687   }
    688   if (EFI_ERROR (Status)) {
    689     return Status;
    690   }
    691 
    692   //
    693   // Poll udp out of the net tpl if synchoronus call.
    694   //
    695   if (TimeoutEvent == NULL) {
    696 
    697     while (Instance->UdpSts == EFI_ALREADY_STARTED) {
    698       Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
    699     }
    700     return Instance->UdpSts;
    701   }
    702 
    703   return EFI_SUCCESS;
    704 }
    705 
    706 
    707 /**
    708   Manually extend the valid and preferred lifetimes for the IPv6 addresses
    709   of the configured IA and update other configuration parameters by sending a
    710   Renew or Rebind packet.
    711 
    712   The RenewRebind() function is used to manually extend the valid and preferred
    713   lifetimes for the IPv6 addresses of the configured IA, and update other
    714   configuration parameters by sending Renew or Rebind packet.
    715   - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,
    716     it sends Renew packet to the previously DHCPv6 server and transfer the
    717     state of the configured IA to Dhcp6Renewing. If valid Reply packet received,
    718     the state transfers to Dhcp6Bound and the valid and preferred timer restarts.
    719     If fails, the state transfers to Dhcp6Bound, but the timer continues.
    720   - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,
    721     it will send a Rebind packet. If valid Reply packet is received, the state transfers
    722     to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state
    723     transfers to Dhcp6Init, and the IA can't be used.
    724 
    725   @param[in]  This              The pointer to the Dhcp6 protocol.
    726   @param[in]  RebindRequest     If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.
    727                                 Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.
    728 
    729   @retval EFI_SUCCESS           The DHCPv6 renew/rebind exchange process has
    730                                 completed and at least one IPv6 address of the
    731                                 configured IA has been bound again when
    732                                 EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL.
    733                                 The EFI DHCPv6 Protocol instance has sent Renew
    734                                 or Rebind packet when
    735                                 EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.
    736   @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the
    737                                 state of the configured IA is not in Dhcp6Bound.
    738   @retval EFI_ALREADY_STARTED   The state of the configured IA has already entered
    739                                 Dhcp6Renewing when RebindRequest is FALSE.
    740                                 The state of the configured IA has already entered
    741                                 Dhcp6Rebinding when RebindRequest is TRUE.
    742   @retval EFI_ABORTED           The DHCPv6 renew/rebind exchange process aborted
    743                                 by the user.
    744   @retval EFI_NO_RESPONSE       The DHCPv6 renew/rebind exchange process failed
    745                                 because of no response.
    746   @retval EFI_NO_MAPPING        No IPv6 address has been bound to the configured
    747                                 IA after the DHCPv6 renew/rebind exchange process.
    748   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
    749   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    750 
    751 **/
    752 EFI_STATUS
    753 EFIAPI
    754 EfiDhcp6RenewRebind (
    755   IN EFI_DHCP6_PROTOCOL        *This,
    756   IN BOOLEAN                   RebindRequest
    757   )
    758 {
    759   EFI_STATUS                   Status;
    760   EFI_TPL                      OldTpl;
    761   DHCP6_INSTANCE               *Instance;
    762   DHCP6_SERVICE                *Service;
    763 
    764   if (This == NULL) {
    765     return EFI_INVALID_PARAMETER;
    766   }
    767 
    768   Instance = DHCP6_INSTANCE_FROM_THIS (This);
    769   Service  = Instance->Service;
    770 
    771   //
    772   // The instance hasn't been configured.
    773   //
    774   if (Instance->Config == NULL) {
    775     return EFI_ACCESS_DENIED;
    776   }
    777 
    778   ASSERT (Instance->IaCb.Ia != NULL);
    779 
    780   //
    781   // The instance has already entered renewing or rebinding state.
    782   //
    783   if ((Instance->IaCb.Ia->State == Dhcp6Rebinding && RebindRequest) ||
    784       (Instance->IaCb.Ia->State == Dhcp6Renewing && !RebindRequest)
    785       ) {
    786     return EFI_ALREADY_STARTED;
    787   }
    788 
    789   if (Instance->IaCb.Ia->State != Dhcp6Bound) {
    790     return EFI_ACCESS_DENIED;
    791   }
    792 
    793   OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);
    794   Instance->UdpSts = EFI_ALREADY_STARTED;
    795 
    796   //
    797   // Send renew/rebind message to start exchange process.
    798   //
    799   Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest);
    800 
    801   if (EFI_ERROR (Status)) {
    802     goto ON_ERROR;
    803   }
    804 
    805   //
    806   // Register receive callback for the stateful exchange process.
    807   //
    808   Status = UdpIoRecvDatagram(
    809              Service->UdpIo,
    810              Dhcp6ReceivePacket,
    811              Service,
    812              0
    813              );
    814 
    815   if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
    816     goto ON_ERROR;
    817   }
    818 
    819   gBS->RestoreTPL (OldTpl);
    820 
    821   //
    822   // Poll udp out of the net tpl if synchoronus call.
    823   //
    824   if (Instance->Config->IaInfoEvent == NULL) {
    825 
    826     while (Instance->UdpSts == EFI_ALREADY_STARTED) {
    827       Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
    828     }
    829     return Instance->UdpSts;
    830   }
    831 
    832   return EFI_SUCCESS;
    833 
    834 ON_ERROR:
    835 
    836   gBS->RestoreTPL (OldTpl);
    837   return Status;
    838 }
    839 
    840 
    841 /**
    842   Inform that one or more addresses assigned by a server are already
    843   in use by another node.
    844 
    845   The Decline() function is used to manually decline the assignment of
    846   IPv6 addresses, which have been already used by another node. If all
    847   IPv6 addresses of the configured IA are declined through this function,
    848   the state of the IA will switch through Dhcp6Declining to Dhcp6Init.
    849   Otherwise, the state of the IA will restore to Dhcp6Bound after the
    850   declining process. The Decline() can only be called when the IA is in
    851   Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,
    852   this function is a blocking operation. It will return after the
    853   declining process finishes, or aborted by user.
    854 
    855   @param[in]  This              The pointer to EFI_DHCP6_PROTOCOL.
    856   @param[in]  AddressCount      The number of declining addresses.
    857   @param[in]  Addresses         The pointer to the buffer stored the declining
    858                                 addresses.
    859 
    860   @retval EFI_SUCCESS           The DHCPv6 decline exchange process completed
    861                                 when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.
    862                                 The Dhcp6 instance sent Decline packet when
    863                                 EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL.
    864   @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the
    865                                 state of the configured IA is not in Dhcp6Bound.
    866   @retval EFI_ABORTED           The DHCPv6 decline exchange process aborted by user.
    867   @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with
    868                                 the configured IA for this instance.
    869   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
    870   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
    871 
    872 **/
    873 EFI_STATUS
    874 EFIAPI
    875 EfiDhcp6Decline (
    876   IN EFI_DHCP6_PROTOCOL        *This,
    877   IN UINT32                    AddressCount,
    878   IN EFI_IPv6_ADDRESS          *Addresses
    879   )
    880 {
    881   EFI_STATUS                   Status;
    882   EFI_TPL                      OldTpl;
    883   EFI_DHCP6_IA                 *DecIa;
    884   DHCP6_INSTANCE               *Instance;
    885   DHCP6_SERVICE                *Service;
    886 
    887   if (This == NULL || AddressCount == 0 || Addresses == NULL) {
    888     return EFI_INVALID_PARAMETER;
    889   }
    890 
    891   Instance = DHCP6_INSTANCE_FROM_THIS (This);
    892   Service  = Instance->Service;
    893 
    894   //
    895   // The instance hasn't been configured.
    896   //
    897   if (Instance->Config == NULL) {
    898     return EFI_ACCESS_DENIED;
    899   }
    900 
    901   ASSERT (Instance->IaCb.Ia != NULL);
    902 
    903   if (Instance->IaCb.Ia->State != Dhcp6Bound) {
    904     return EFI_ACCESS_DENIED;
    905   }
    906 
    907   //
    908   // Check whether all the declined addresses belongs to the configured Ia.
    909   //
    910   Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);
    911 
    912   if (EFI_ERROR(Status)) {
    913     return Status;
    914   }
    915 
    916   OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);
    917   Instance->UdpSts = EFI_ALREADY_STARTED;
    918 
    919   //
    920   // Deprive of all the declined addresses from the configured Ia, and create a
    921   // DeclineIa used to create decline message.
    922   //
    923   DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);
    924 
    925   if (DecIa == NULL) {
    926     Status = EFI_OUT_OF_RESOURCES;
    927     goto ON_ERROR;
    928   }
    929 
    930   //
    931   // Send the decline message to start exchange process.
    932   //
    933   Status = Dhcp6SendDeclineMsg (Instance, DecIa);
    934 
    935   if (EFI_ERROR (Status)) {
    936     goto ON_ERROR;
    937   }
    938 
    939   //
    940   // Register receive callback for the stateful exchange process.
    941   //
    942   Status = UdpIoRecvDatagram(
    943              Service->UdpIo,
    944              Dhcp6ReceivePacket,
    945              Service,
    946              0
    947              );
    948 
    949   if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
    950     goto ON_ERROR;
    951   }
    952 
    953   FreePool (DecIa);
    954   gBS->RestoreTPL (OldTpl);
    955 
    956   //
    957   // Poll udp out of the net tpl if synchoronus call.
    958   //
    959   if (Instance->Config->IaInfoEvent == NULL) {
    960 
    961     while (Instance->UdpSts == EFI_ALREADY_STARTED) {
    962       Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
    963     }
    964     return Instance->UdpSts;
    965   }
    966 
    967   return EFI_SUCCESS;
    968 
    969 ON_ERROR:
    970 
    971   if (DecIa != NULL) {
    972     FreePool (DecIa);
    973   }
    974   gBS->RestoreTPL (OldTpl);
    975 
    976   return Status;
    977 }
    978 
    979 
    980 /**
    981   Release one or more addresses associated with the configured Ia
    982   for current instance.
    983 
    984   The Release() function is used to manually release one or more
    985   IPv6 addresses. If AddressCount is zero, it will release all IPv6
    986   addresses of the configured IA. If all IPv6 addresses of the IA are
    987   released through this function, the state of the IA will switch
    988   through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the
    989   IA will restore to Dhcp6Bound after the releasing process.
    990   The Release() can only be called when the IA is in Dhcp6Bound state.
    991   If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is
    992   a blocking operation. It will return after the releasing process
    993   finishes, or is aborted by user.
    994 
    995   @param[in]  This              The pointer to the Dhcp6 protocol.
    996   @param[in]  AddressCount      The number of releasing addresses.
    997   @param[in]  Addresses         The pointer to the buffer stored the releasing
    998                                 addresses.
    999 
   1000   @retval EFI_SUCCESS           The DHCPv6 release exchange process
   1001                                 completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
   1002                                 was NULL. The Dhcp6 instance was sent Release
   1003                                 packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
   1004                                 was not NULL.
   1005   @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the
   1006                                 state of the configured IA is not in Dhcp6Bound.
   1007   @retval EFI_ABORTED           The DHCPv6 release exchange process aborted by user.
   1008   @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with
   1009                                 the configured IA for this instance.
   1010   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
   1011   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
   1012 
   1013 **/
   1014 EFI_STATUS
   1015 EFIAPI
   1016 EfiDhcp6Release (
   1017   IN EFI_DHCP6_PROTOCOL        *This,
   1018   IN UINT32                    AddressCount,
   1019   IN EFI_IPv6_ADDRESS          *Addresses
   1020   )
   1021 {
   1022   EFI_STATUS                   Status;
   1023   EFI_TPL                      OldTpl;
   1024   EFI_DHCP6_IA                 *RelIa;
   1025   DHCP6_INSTANCE               *Instance;
   1026   DHCP6_SERVICE                *Service;
   1027 
   1028   if (This == NULL || (AddressCount != 0 && Addresses == NULL)) {
   1029     return EFI_INVALID_PARAMETER;
   1030   }
   1031 
   1032   Instance = DHCP6_INSTANCE_FROM_THIS (This);
   1033   Service  = Instance->Service;
   1034 
   1035   //
   1036   // The instance hasn't been configured.
   1037   //
   1038   if (Instance->Config == NULL) {
   1039     return EFI_ACCESS_DENIED;
   1040   }
   1041 
   1042   ASSERT (Instance->IaCb.Ia != NULL);
   1043 
   1044   if (Instance->IaCb.Ia->State != Dhcp6Bound) {
   1045     return EFI_ACCESS_DENIED;
   1046   }
   1047 
   1048   //
   1049   // Check whether all the released addresses belongs to the configured Ia.
   1050   //
   1051   Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);
   1052 
   1053   if (EFI_ERROR(Status)) {
   1054     return Status;
   1055   }
   1056 
   1057   OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);
   1058   Instance->UdpSts = EFI_ALREADY_STARTED;
   1059 
   1060   //
   1061   // Deprive of all the released addresses from the configured Ia, and create a
   1062   // ReleaseIa used to create release message.
   1063   //
   1064   RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);
   1065 
   1066   if (RelIa == NULL) {
   1067     Status = EFI_OUT_OF_RESOURCES;
   1068     goto ON_ERROR;
   1069   }
   1070 
   1071   //
   1072   // Send the release message to start exchange process.
   1073   //
   1074   Status = Dhcp6SendReleaseMsg (Instance, RelIa);
   1075 
   1076   if (EFI_ERROR (Status)) {
   1077     goto ON_ERROR;
   1078   }
   1079 
   1080   //
   1081   // Register receive callback for the stateful exchange process.
   1082   //
   1083   Status = UdpIoRecvDatagram(
   1084              Service->UdpIo,
   1085              Dhcp6ReceivePacket,
   1086              Service,
   1087              0
   1088              );
   1089 
   1090   if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
   1091     goto ON_ERROR;
   1092   }
   1093 
   1094   FreePool (RelIa);
   1095   gBS->RestoreTPL (OldTpl);
   1096 
   1097   //
   1098   // Poll udp out of the net tpl if synchoronus call.
   1099   //
   1100   if (Instance->Config->IaInfoEvent == NULL) {
   1101     while (Instance->UdpSts == EFI_ALREADY_STARTED) {
   1102       Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
   1103     }
   1104     return Instance->UdpSts;
   1105   }
   1106 
   1107   return EFI_SUCCESS;
   1108 
   1109 ON_ERROR:
   1110 
   1111   if (RelIa != NULL) {
   1112     FreePool (RelIa);
   1113   }
   1114   gBS->RestoreTPL (OldTpl);
   1115 
   1116   return Status;
   1117 }
   1118 
   1119 
   1120 /**
   1121   Parse the option data in the Dhcp6 packet.
   1122 
   1123   The Parse() function is used to retrieve the option list in the DHCPv6 packet.
   1124 
   1125   @param[in]      This              The pointer to the Dhcp6 protocol.
   1126   @param[in]      Packet            The pointer to the Dhcp6 packet.
   1127   @param[in, out] OptionCount       The number of option in the packet.
   1128   @param[out]     PacketOptionList  The array of pointers to each option in the packet.
   1129 
   1130   @retval EFI_SUCCESS           The packet was successfully parsed.
   1131   @retval EFI_INVALID_PARAMETER Some parameter is NULL.
   1132   @retval EFI_BUFFER_TOO_SMALL  *OptionCount is smaller than the number of options
   1133                                 that were found in the Packet.
   1134 
   1135 **/
   1136 EFI_STATUS
   1137 EFIAPI
   1138 EfiDhcp6Parse (
   1139   IN EFI_DHCP6_PROTOCOL        *This,
   1140   IN EFI_DHCP6_PACKET          *Packet,
   1141   IN OUT UINT32                *OptionCount,
   1142   OUT EFI_DHCP6_PACKET_OPTION  *PacketOptionList[]  OPTIONAL
   1143   )
   1144 {
   1145   UINT32                       OptCnt;
   1146   UINT32                       OptLen;
   1147   UINT16                       DataLen;
   1148   UINT8                        *Start;
   1149   UINT8                        *End;
   1150 
   1151   if (This == NULL || Packet == NULL || OptionCount == NULL) {
   1152     return EFI_INVALID_PARAMETER;
   1153   }
   1154 
   1155   if (*OptionCount != 0 && PacketOptionList == NULL) {
   1156     return EFI_INVALID_PARAMETER;
   1157   }
   1158 
   1159   if (Packet->Length > Packet->Size || Packet->Length < sizeof (EFI_DHCP6_HEADER)) {
   1160     return EFI_INVALID_PARAMETER;
   1161   }
   1162 
   1163   //
   1164   //  The format of Dhcp6 option:
   1165   //
   1166   //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   1167   //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   1168   //    |          option-code          |   option-len (option data)    |
   1169   //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   1170   //    |                          option-data                          |
   1171   //    |                      (option-len octets)                      |
   1172   //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   1173   //
   1174 
   1175   OptCnt = 0;
   1176   OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER);
   1177   Start  = Packet->Dhcp6.Option;
   1178   End    = Start + OptLen;
   1179 
   1180   //
   1181   // Calculate the number of option in the packet.
   1182   //
   1183   while (Start < End) {
   1184     DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;
   1185     Start  += (NTOHS (DataLen) + 4);
   1186     OptCnt++;
   1187   }
   1188 
   1189   //
   1190   // It will return buffer too small if pass-in option count is smaller than the
   1191   // actual count of options in the packet.
   1192   //
   1193   if (OptCnt > *OptionCount) {
   1194     *OptionCount = OptCnt;
   1195     return EFI_BUFFER_TOO_SMALL;
   1196   }
   1197 
   1198   ZeroMem (
   1199     PacketOptionList,
   1200     (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *))
   1201     );
   1202 
   1203   OptCnt = 0;
   1204   Start  = Packet->Dhcp6.Option;
   1205 
   1206   while (Start < End) {
   1207 
   1208     PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *) Start;
   1209     DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;
   1210     Start  += (NTOHS (DataLen) + 4);
   1211     OptCnt++;
   1212   }
   1213 
   1214   return EFI_SUCCESS;
   1215 }
   1216 
   1217