summaryrefslogtreecommitdiff
path: root/NetworkPkg/Dhcp6Dxe
diff options
context:
space:
mode:
Diffstat (limited to 'NetworkPkg/Dhcp6Dxe')
-rw-r--r--NetworkPkg/Dhcp6Dxe/ComponentName.c312
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c782
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h155
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf69
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c1220
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h597
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Io.c2965
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Io.h193
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c1146
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h340
10 files changed, 7779 insertions, 0 deletions
diff --git a/NetworkPkg/Dhcp6Dxe/ComponentName.c b/NetworkPkg/Dhcp6Dxe/ComponentName.c
new file mode 100644
index 0000000000..314ca37d16
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/ComponentName.c
@@ -0,0 +1,312 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for Dhcp6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that attempt to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that attempts to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language, from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName = {
+ Dhcp6ComponentNameGetDriverName,
+ Dhcp6ComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Dhcp6ComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Dhcp6ComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDhcp6DriverNameTable[] = {
+ {
+ "eng;en",
+ L"DHCP6 Protocol Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDhcp6DriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gDhcp6ComponentName)
+ );
+}
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in the
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language, from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c
new file mode 100644
index 0000000000..d14f169f07
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c
@@ -0,0 +1,782 @@
+/** @file
+ Driver Binding functions and Service Binding functions
+ implementationfor for Dhcp6 Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+
+EFI_DRIVER_BINDING_PROTOCOL gDhcp6DriverBinding = {
+ Dhcp6DriverBindingSupported,
+ Dhcp6DriverBindingStart,
+ Dhcp6DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL gDhcp6ServiceBindingTemplate = {
+ Dhcp6ServiceBindingCreateChild,
+ Dhcp6ServiceBindingDestroyChild
+};
+
+
+/**
+ Configure the default Udp6Io to receive all the DHCP6 traffic
+ on this network interface.
+
+ @param[in] UdpIo The pointer to Udp6Io to be configured.
+ @param[in] Context The pointer to the context.
+
+ @retval EFI_SUCCESS The Udp6Io is successfully configured.
+ @retval Others Failed to configure the Udp6Io.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ConfigureUdpIo (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_UDP6_CONFIG_DATA *Config;
+
+ Udp6 = UdpIo->Protocol.Udp6;
+ Config = &(UdpIo->Config.Udp6);
+
+ ZeroMem (Config, sizeof (EFI_UDP6_CONFIG_DATA));
+
+ //
+ // Set Udp6 configure data for the Dhcp6 instance.
+ //
+ Config->AcceptPromiscuous = FALSE;
+ Config->AcceptAnyPort = FALSE;
+ Config->AllowDuplicatePort = FALSE;
+ Config->TrafficClass = 0;
+ Config->HopLimit = 128;
+ Config->ReceiveTimeout = 0;
+ Config->TransmitTimeout = 0;
+
+ //
+ // Configure an endpoint of client(0, 546), server(0, 0), the addresses
+ // will be overridden later. Note that we MUST not limit RemotePort.
+ // More details, refer to RFC 3315 section 5.2.
+ //
+ Config->StationPort = DHCP6_PORT_CLIENT;
+ Config->RemotePort = 0;
+
+ return Udp6->Configure (Udp6, Config);;
+}
+
+
+/**
+ Destory the Dhcp6 service. The Dhcp6 service may be partly initialized,
+ or partly destroyed. If a resource is destroyed, it is marked as such in
+ case the destroy failed and being called again later.
+
+ @param[in, out] Service The pointer to Dhcp6 service to be destroyed.
+
+**/
+VOID
+Dhcp6DestroyService (
+ IN OUT DHCP6_SERVICE *Service
+ )
+{
+ //
+ // All children instances should have been already destoryed here.
+ //
+ ASSERT (Service->NumOfChild == 0);
+
+ if (Service->ClientId != NULL) {
+ FreePool (Service->ClientId);
+ }
+
+ if (Service->UdpIo != NULL) {
+ UdpIoFreeIo (Service->UdpIo);
+ }
+
+ FreePool (Service);
+}
+
+
+/**
+ Create a new Dhcp6 service for the Nic controller.
+
+ @param[in] Controller The controller to be installed DHCP6 service
+ binding protocol.
+ @param[in] ImageHandle The image handle of the Dhcp6 driver.
+ @param[out] Service The return pointer of the new Dhcp6 service.
+
+ @retval EFI_SUCCESS The Dhcp6 service is created successfully.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
+
+**/
+EFI_STATUS
+Dhcp6CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ OUT DHCP6_SERVICE **Service
+ )
+{
+ DHCP6_SERVICE *Dhcp6Srv;
+
+ *Service = NULL;
+ Dhcp6Srv = AllocateZeroPool (sizeof (DHCP6_SERVICE));
+
+ if (Dhcp6Srv == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Open the SNP protocol to get mode data later.
+ //
+ Dhcp6Srv->Snp = NULL;
+ NetLibGetSnpHandle (Controller, &Dhcp6Srv->Snp);
+ if (Dhcp6Srv->Snp == NULL) {
+ FreePool (Dhcp6Srv);
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Initialize the fields of the new Dhcp6 service.
+ //
+ Dhcp6Srv->Signature = DHCP6_SERVICE_SIGNATURE;
+ Dhcp6Srv->InDestory = FALSE;
+ Dhcp6Srv->Controller = Controller;
+ Dhcp6Srv->Image = ImageHandle;
+ Dhcp6Srv->Xid = (0xffffff & NET_RANDOM (NetRandomInitSeed ()));
+
+ CopyMem (
+ &Dhcp6Srv->ServiceBinding,
+ &gDhcp6ServiceBindingTemplate,
+ sizeof (EFI_SERVICE_BINDING_PROTOCOL)
+ );
+
+ //
+ // Generate client Duid in the format of Duid-llt.
+ //
+ Dhcp6Srv->ClientId = Dhcp6GenerateClientId (Dhcp6Srv->Snp->Mode);
+
+ if (Dhcp6Srv->ClientId == NULL) {
+ FreePool (Dhcp6Srv);
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Create an Udp6Io for stateful transmit/receive of each Dhcp6 instance.
+ //
+ Dhcp6Srv->UdpIo = UdpIoCreateIo (
+ Controller,
+ ImageHandle,
+ Dhcp6ConfigureUdpIo,
+ UDP_IO_UDP6_VERSION,
+ NULL
+ );
+
+ if (Dhcp6Srv->UdpIo == NULL) {
+ FreePool (Dhcp6Srv->ClientId);
+ FreePool (Dhcp6Srv);
+ return EFI_DEVICE_ERROR;
+ }
+
+ InitializeListHead (&Dhcp6Srv->Child);
+
+ *Service = Dhcp6Srv;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Destroy the Dhcp6 instance and recycle the resources.
+
+ @param[in, out] Instance The pointer to the Dhcp6 instance.
+
+**/
+VOID
+Dhcp6DestroyInstance (
+ IN OUT DHCP6_INSTANCE *Instance
+ )
+{
+ //
+ // Clean up the retry list first.
+ //
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL);
+ gBS->CloseEvent (Instance->Timer);
+
+ //
+ // Clean up the current configure data.
+ //
+ if (Instance->Config != NULL) {
+ Dhcp6CleanupConfigData (Instance->Config);
+ FreePool (Instance->Config);
+ }
+
+ //
+ // Clean up the current Ia.
+ //
+ if (Instance->IaCb.Ia != NULL) {
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ }
+ FreePool (Instance->IaCb.Ia);
+ }
+
+ if (Instance->Unicast != NULL) {
+ FreePool (Instance->Unicast);
+ }
+
+ if (Instance->AdSelect != NULL) {
+ FreePool (Instance->AdSelect);
+ }
+
+ FreePool (Instance);
+}
+
+
+/**
+ Create the Dhcp6 instance and initialize it.
+
+ @param[in] Service The pointer to the Dhcp6 service.
+ @param[out] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS The Dhcp6 instance is created.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+
+**/
+EFI_STATUS
+Dhcp6CreateInstance (
+ IN DHCP6_SERVICE *Service,
+ OUT DHCP6_INSTANCE **Instance
+ )
+{
+ EFI_STATUS Status;
+ DHCP6_INSTANCE *Dhcp6Ins;
+
+ *Instance = NULL;
+ Dhcp6Ins = AllocateZeroPool (sizeof (DHCP6_INSTANCE));
+
+ if (Dhcp6Ins == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the fields of the new Dhcp6 instance.
+ //
+ Dhcp6Ins->Signature = DHCP6_INSTANCE_SIGNATURE;
+ Dhcp6Ins->UdpSts = EFI_ALREADY_STARTED;
+ Dhcp6Ins->Service = Service;
+ Dhcp6Ins->InDestory = FALSE;
+ Dhcp6Ins->MediaPresent = TRUE;
+
+ CopyMem (
+ &Dhcp6Ins->Dhcp6,
+ &gDhcp6ProtocolTemplate,
+ sizeof (EFI_DHCP6_PROTOCOL)
+ );
+
+ InitializeListHead (&Dhcp6Ins->TxList);
+ InitializeListHead (&Dhcp6Ins->InfList);
+
+ //
+ // There is a timer for each Dhcp6 instance, which is used to track the
+ // lease time of Ia and the retransmisson time of all sent packets.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Dhcp6OnTimerTick,
+ Dhcp6Ins,
+ &Dhcp6Ins->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Dhcp6Ins);
+ return Status;
+ }
+
+ *Instance = Dhcp6Ins;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Entry point of the DHCP6 driver to install various protocols.
+
+ @param[in] ImageHandle The handle of the UEFI image file.
+ @param[in] SystemTable The pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval Others Unexpected error occurs.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gDhcp6DriverBinding,
+ ImageHandle,
+ &gDhcp6ComponentName,
+ &gDhcp6ComponentName2
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @param[in] This The pointer to the driver binding protocol.
+ @param[in] ControllerHandle The handle of device to be tested.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to be started.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ return gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+}
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This The pointer to the driver binding protocol.
+ @param[in] ControllerHandle The handle of device to be started.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to be started.
+
+ @retval EFI_SUCCESS This driver is installed to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ DHCP6_SERVICE *Service;
+
+ //
+ // Check the Dhcp6 serivce whether already started.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Create and initialize the Dhcp6 service.
+ //
+ Status = Dhcp6CreateService (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &Service
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (Service != NULL);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ &Service->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ Dhcp6DestroyService (Service);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_HANDLE NicHandle;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ DHCP6_SERVICE *Service;
+ DHCP6_INSTANCE *Instance;
+
+ //
+ // Find and check the Nic handle by the controller handle.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Service = DHCP6_SERVICE_FROM_THIS (ServiceBinding);
+
+ if (Service->InDestory) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (NumberOfChildren == 0) {
+ //
+ // Destory the service itself if no child instance left.
+ //
+ Service->InDestory = TRUE;
+
+ Status = gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ if (EFI_ERROR (Status)) {
+ Service->InDestory = FALSE;
+ goto ON_EXIT;
+ }
+
+ Dhcp6DestroyService (Service);
+
+ } else {
+ //
+ // Destory all the children instances before destory the service.
+ //
+ while (!IsListEmpty (&Service->Child)) {
+ Instance = NET_LIST_HEAD (&Service->Child, DHCP6_INSTANCE, Link);
+ ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+ }
+ //
+ // Any of child failed to be destroyed.
+ //
+ if (Service->NumOfChild != 0) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing
+ UEFI handle, then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ DHCP6_SERVICE *Service;
+ DHCP6_INSTANCE *Instance;
+ VOID *Udp6;
+
+ if (This == NULL || ChildHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Service = DHCP6_SERVICE_FROM_THIS (This);
+
+ Status = Dhcp6CreateInstance (Service, &Instance);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (Instance != NULL);
+
+ //
+ // Start the timer when the instance is ready to use.
+ //
+ Status = gBS->SetTimer (
+ Instance->Timer,
+ TimerPeriodic,
+ TICKS_PER_SECOND
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the DHCP6 protocol onto ChildHandle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiDhcp6ProtocolGuid,
+ &Instance->Dhcp6,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the UDP6 protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ Service->UdpIo->UdpHandle,
+ &gEfiUdp6ProtocolGuid,
+ (VOID **) &Udp6,
+ gDhcp6DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiDhcp6ProtocolGuid,
+ &Instance->Dhcp6,
+ NULL
+ );
+ goto ON_ERROR;
+ }
+
+ //
+ // Add into the children list of its parent service.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&Service->Child, &Instance->Link);
+ Service->NumOfChild++;
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ Dhcp6DestroyInstance (Instance);
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in] ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ DHCP6_SERVICE *Service;
+ DHCP6_INSTANCE *Instance;
+
+ if (This == NULL || ChildHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiDhcp6ProtocolGuid,
+ (VOID **) &Dhcp6,
+ gDhcp6DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (Dhcp6);
+ Service = DHCP6_SERVICE_FROM_THIS (This);
+
+ if (Instance->Service != Service) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Instance->InDestory) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance->InDestory = TRUE;
+
+ Status = gBS->CloseProtocol (
+ Service->UdpIo->UdpHandle,
+ &gEfiUdp6ProtocolGuid,
+ gDhcp6DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ Instance->InDestory = FALSE;
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // Uninstall the MTFTP6 protocol first to enable a top down destruction.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiDhcp6ProtocolGuid,
+ Dhcp6
+ );
+
+ if (EFI_ERROR (Status)) {
+ Instance->InDestory = FALSE;
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // Remove it from the children list of its parent service.
+ //
+ RemoveEntryList (&Instance->Link);
+ Service->NumOfChild--;
+
+ Dhcp6DestroyInstance (Instance);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h
new file mode 100644
index 0000000000..5a88be4d3b
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h
@@ -0,0 +1,155 @@
+/** @file
+ Driver Binding functions and Service Binding functions
+ declaration for Dhcp6 Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP6_DRIVER_H__
+#define __EFI_DHCP6_DRIVER_H__
+
+#include <Protocol/ServiceBinding.h>
+
+extern EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2;
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported(), it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start(), it
+ must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of
+ children is zero, stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle.
+ @retval other This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf b/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf
new file mode 100644
index 0000000000..f10b07ac3c
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf
@@ -0,0 +1,69 @@
+## @file
+# Component description file for Dhcp6 module.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Dhcp6Dxe
+ FILE_GUID = 95E3669D-34BE-4775-A651-7EA41B69D89E
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Dhcp6DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gDhcp6DriverBinding
+# COMPONENT_NAME = gDhcp6ComponentName
+# COMPONENT_NAME2 = gDhcp6ComponentName2
+#
+
+[Sources]
+ Dhcp6Driver.c
+ Dhcp6Driver.h
+ Dhcp6Impl.c
+ Dhcp6Impl.h
+ Dhcp6Io.c
+ Dhcp6Io.h
+ Dhcp6Utility.c
+ Dhcp6Utility.h
+ ComponentName.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ DebugLib
+ NetLib
+ UdpIoLib
+
+
+[Protocols]
+ gEfiUdp6ServiceBindingProtocolGuid
+ gEfiUdp6ProtocolGuid
+ gEfiDhcp6ServiceBindingProtocolGuid
+ gEfiDhcp6ProtocolGuid
+
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c
new file mode 100644
index 0000000000..3e73976ddb
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c
@@ -0,0 +1,1220 @@
+/** @file
+ This EFI_DHCP6_PROTOCOL interface implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+//
+// Well-known multi-cast address defined in section-24.1 of rfc-3315
+//
+// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
+// ALL_DHCP_Servers address: FF05::1:3
+//
+EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
+EFI_IPv6_ADDRESS mAllDhcpServersAddress = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}};
+
+EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = {
+ EfiDhcp6GetModeData,
+ EfiDhcp6Configure,
+ EfiDhcp6Start,
+ EfiDhcp6InfoRequest,
+ EfiDhcp6RenewRebind,
+ EfiDhcp6Decline,
+ EfiDhcp6Release,
+ EfiDhcp6Stop,
+ EfiDhcp6Parse
+};
+
+/**
+ Starts the DHCPv6 standard S.A.R.R. process.
+
+ The Start() function starts the DHCPv6 standard process. This function can
+ be called only when the state of Dhcp6 instance is in the Dhcp6Init state.
+ If the DHCP process completes successfully, the state of the Dhcp6 instance
+ will be transferred through Dhcp6Selecting and Dhcp6Requesting to the
+ Dhcp6Bound state.
+ Refer to rfc-3315 for precise state transitions during this process. At the
+ time when each event occurs in this process, the callback function that was set
+ by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this
+ opportunity to control the process.
+
+ @param[in] This The pointer to Dhcp6 protocol.
+
+ @retval EFI_SUCCESS The DHCPv6 standard process has started, or it has
+ completed when CompletionEvent is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no
+ response was received from the server within the
+ specified timeout value.
+ @retval EFI_ABORTED The user aborted the DHCPv6 process.
+ @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6
+ standard process.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Start (
+ IN EFI_DHCP6_PROTOCOL *This
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ //
+ // The instance has already been started.
+ //
+ if (Instance->IaCb.Ia->State != Dhcp6Init) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Need to clear initial time to make sure that elapsed-time
+ // is set to 0 for first Solicit.
+ //
+ Instance->StartTime = 0;
+
+ //
+ // Send the solicit message to start S.A.R.R process.
+ //
+ Status = Dhcp6SendSolicitMsg (Instance);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateful exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchronous call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Stops the DHCPv6 standard S.A.R.R. process.
+
+ The Stop() function is used to stop the DHCPv6 standard process. After this
+ function is called successfully, the state of Dhcp6 instance is transferred
+ into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called
+ before DHCPv6 standard process can be started again. This function can be
+ called when the Dhcp6 instance is in any state.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+
+ @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Stop (
+ IN EFI_DHCP6_PROTOCOL *This
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ EFI_UDP6_PROTOCOL *Udp6;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+ Udp6 = Service->UdpIo->Protocol.Udp6;
+ Status = EFI_SUCCESS;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return Status;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ //
+ // The instance has already been stopped.
+ //
+ if (Instance->IaCb.Ia->State == Dhcp6Init ||
+ Instance->IaCb.Ia->State == Dhcp6Selecting ||
+ Instance->IaCb.Ia->State == Dhcp6Requesting
+ ) {
+ return Status;
+ }
+
+ //
+ // Release the current ready Ia.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+ Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia);
+
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+ ASSERT (Udp6 != NULL);
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Udp6->Poll (Udp6);
+ }
+ Status = Instance->UdpSts;
+ }
+
+ //
+ // Clean up the session data for the released Ia.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Dhcp6CleanupSession (Instance, EFI_SUCCESS);
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Returns the current operating mode data for the Dhcp6 instance.
+
+ The GetModeData() function returns the current operating mode and
+ cached data packet for the Dhcp6 instance.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data.
+ @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data.
+
+ @retval EFI_SUCCESS The mode data was returned.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance was not
+ configured.
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6GetModeData (
+ IN EFI_DHCP6_PROTOCOL *This,
+ OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL,
+ OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_DHCP6_IA *Ia;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+ UINT32 IaSize;
+ UINT32 IdSize;
+
+ if (This == NULL || (Dhcp6ModeData == NULL && Dhcp6ConfigData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ if (Instance->Config == NULL && Dhcp6ConfigData != NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Service->ClientId != NULL);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // User needs a copy of instance config data.
+ //
+ if (Dhcp6ConfigData != NULL) {
+ ZeroMem (Dhcp6ConfigData, sizeof(EFI_DHCP6_CONFIG_DATA));
+ //
+ // Duplicate config data, including all reference buffers.
+ //
+ if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // User need a copy of instance mode data.
+ //
+ if (Dhcp6ModeData != NULL) {
+ ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA));
+ //
+ // Duplicate a copy of EFI_DHCP6_DUID for client Id.
+ //
+ IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length);
+
+ Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize);
+ if (Dhcp6ModeData->ClientId == NULL) {
+ goto ON_ERROR;
+ }
+
+ CopyMem (
+ Dhcp6ModeData->ClientId,
+ Service->ClientId,
+ IdSize
+ );
+
+ Ia = Instance->IaCb.Ia;
+ if (Ia != NULL) {
+ //
+ // Duplicate a copy of EFI_DHCP6_IA for configured Ia.
+ //
+ IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+
+ Dhcp6ModeData->Ia = AllocateZeroPool (IaSize);
+ if (Dhcp6ModeData->Ia == NULL) {
+ goto ON_ERROR;
+ }
+
+ CopyMem (
+ Dhcp6ModeData->Ia,
+ Ia,
+ IaSize
+ );
+
+ //
+ // Duplicate a copy of reply packet if has.
+ //
+ if (Ia->ReplyPacket != NULL) {
+ Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size);
+ if (Dhcp6ModeData->Ia->ReplyPacket == NULL) {
+ goto ON_ERROR;
+ }
+ CopyMem (
+ Dhcp6ModeData->Ia->ReplyPacket,
+ Ia->ReplyPacket,
+ Ia->ReplyPacket->Size
+ );
+ }
+ }
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Dhcp6ConfigData != NULL) {
+ Dhcp6CleanupConfigData (Dhcp6ConfigData);
+ }
+ if (Dhcp6ModeData != NULL) {
+ Dhcp6CleanupModeData (Dhcp6ModeData);
+ }
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_OUT_OF_RESOURCES;
+}
+
+
+/**
+ Initializes, changes, or resets the operational settings for the Dhcp6 instance.
+
+ The Configure() function is used to initialize or clean up the configuration
+ data of the Dhcp6 instance:
+ - When Dhcp6CfgData is not NULL and Configure() is called successfully, the
+ configuration data will be initialized in the Dhcp6 instance, and the state
+ of the configured IA will be transferred into Dhcp6Init.
+ - When Dhcp6CfgData is NULL and Configure() is called successfully, the
+ configuration data will be cleaned up and no IA will be associated with
+ the Dhcp6 instance.
+ To update the configuration data for an Dhcp6 instance, the original data
+ must be cleaned up before setting the new configuration data.
+
+ @param[in] This The pointer to the Dhcp6 protocol
+ @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA.
+
+ @retval EFI_SUCCESS The Dhcp6 is configured successfully with the
+ Dhcp6Init state, or cleaned up the original
+ configuration setting.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance was already configured.
+ The Dhcp6 instance has already started the
+ DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.
+ @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Configure (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ DHCP6_INSTANCE *Other;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+ UINTN Index;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // Check the parameter of configure data.
+ //
+ if (Dhcp6CfgData != NULL) {
+ if (Dhcp6CfgData->OptionCount > 0 && Dhcp6CfgData->OptionList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (Dhcp6CfgData->OptionList != NULL) {
+ for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) {
+ if (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId ||
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit ||
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept ||
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana ||
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ if (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA &&
+ Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Dhcp6CfgData->IaInfoEvent == NULL && Dhcp6CfgData->SolicitRetransmission == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Dhcp6CfgData->SolicitRetransmission != NULL &&
+ Dhcp6CfgData->SolicitRetransmission->Mrc == 0 &&
+ Dhcp6CfgData->SolicitRetransmission->Mrd == 0
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Make sure the (IaId, IaType) is unique over all the instances.
+ //
+ NET_LIST_FOR_EACH (Entry, &Service->Child) {
+ Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link);
+ if (Other->IaCb.Ia != NULL &&
+ Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type &&
+ Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Dhcp6CfgData != NULL) {
+ //
+ // It's not allowed to configure one instance twice without configure null.
+ //
+ if (Instance->Config != NULL) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Duplicate config data including all reference buffers.
+ //
+ Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA));
+ if (Instance->Config == NULL) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData);
+ if (EFI_ERROR(Status)) {
+ FreePool (Instance->Config);
+ gBS->RestoreTPL (OldTpl);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the Ia descriptor from the config data, and leave the other
+ // fields of the Ia as default value 0.
+ //
+ Instance->IaCb.Ia = AllocateZeroPool (sizeof(EFI_DHCP6_IA));
+ if (Instance->IaCb.Ia == NULL) {
+ Dhcp6CleanupConfigData (Instance->Config);
+ FreePool (Instance->Config);
+ gBS->RestoreTPL (OldTpl);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (
+ &Instance->IaCb.Ia->Descriptor,
+ &Dhcp6CfgData->IaDescriptor,
+ sizeof(EFI_DHCP6_IA_DESCRIPTOR)
+ );
+
+ } else {
+
+ if (Instance->Config == NULL) {
+ ASSERT (Instance->IaCb.Ia == NULL);
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // It's not allowed to configure a started instance as null.
+ //
+ if (Instance->IaCb.Ia->State != Dhcp6Init) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_ACCESS_DENIED;
+ }
+
+ Dhcp6CleanupConfigData (Instance->Config);
+ FreePool (Instance->Config);
+ Instance->Config = NULL;
+
+ FreePool (Instance->IaCb.Ia);
+ Instance->IaCb.Ia = NULL;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Request configuration information without the assignment of any
+ Ia addresses of the client.
+
+ The InfoRequest() function is used to request configuration information
+ without the assignment of any IPv6 address of the client. The client sends
+ out an Information Request packet to obtain the required configuration
+ information, and DHCPv6 server responds with a Reply packet containing
+ the information for the client. The received Reply packet will be passed
+ to the user by ReplyCallback function. If the user returns EFI_NOT_READY from
+ ReplyCallback, the Dhcp6 instance will continue to receive other Reply
+ packets unless timeout according to the Retransmission parameter.
+ Otherwise, the Information Request exchange process will be finished
+ successfully if user returns EFI_SUCCESS from ReplyCallback.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client
+ Identifier option and include it into Information Request
+ packet. Otherwise, Client Identifier option will not be included.
+ @param[in] OptionRequest The pointer to the buffer of option request options.
+ @param[in] OptionCount The option number in the OptionList.
+ @param[in] OptionList The list of appended options.
+ @param[in] Retransmission The pointer to the retransmission of the message.
+ @param[in] TimeoutEvent The event of timeout.
+ @param[in] ReplyCallback The callback function when the reply was received.
+ @param[in] CallbackContext The pointer to the parameter passed to the callback.
+
+ @retval EFI_SUCCESS The DHCPv6 information request exchange process
+ completed when TimeoutEvent is NULL. Information
+ Request packet has been sent to DHCPv6 server when
+ TimeoutEvent is not NULL.
+ @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed
+ because of no response, or not all requested-options
+ are responded by DHCPv6 servers when Timeout happened.
+ @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted
+ by user.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6InfoRequest (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN BOOLEAN SendClientId,
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
+ IN UINT32 OptionCount,
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL,
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission,
+ IN EFI_EVENT TimeoutEvent OPTIONAL,
+ IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,
+ IN VOID *CallbackContext OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+ DHCP6_INF_CB *InfCb;
+ UINTN Index;
+
+ if (This == NULL || OptionRequest == NULL || Retransmission == NULL || ReplyCallback == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Retransmission != NULL && Retransmission->Mrc == 0 && Retransmission->Mrd == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (OptionCount > 0 && OptionList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (OptionList != NULL) {
+ for (Index = 0; Index < OptionCount; Index++) {
+ if (OptionList[Index]->OpCode == Dhcp6OptClientId || OptionList[Index]->OpCode == Dhcp6OptRequestOption) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Create and initialize the control block for the info-request.
+ //
+ InfCb = AllocateZeroPool (sizeof(DHCP6_INF_CB));
+
+ if (InfCb == NULL) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InfCb->ReplyCallback = ReplyCallback;
+ InfCb->CallbackContext = CallbackContext;
+ InfCb->TimeoutEvent = TimeoutEvent;
+
+ InsertTailList (&Instance->InfList, &InfCb->Link);
+
+ //
+ // Send the info-request message to start exchange process.
+ //
+ Status = Dhcp6SendInfoRequestMsg (
+ Instance,
+ InfCb,
+ SendClientId,
+ OptionRequest,
+ OptionCount,
+ OptionList,
+ Retransmission
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateless exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (TimeoutEvent == NULL) {
+
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ RemoveEntryList (&InfCb->Link);
+ FreePool (InfCb);
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Manually extend the valid and preferred lifetimes for the IPv6 addresses
+ of the configured IA and update other configuration parameters by sending a
+ Renew or Rebind packet.
+
+ The RenewRebind() function is used to manually extend the valid and preferred
+ lifetimes for the IPv6 addresses of the configured IA, and update other
+ configuration parameters by sending Renew or Rebind packet.
+ - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,
+ it sends Renew packet to the previously DHCPv6 server and transfer the
+ state of the configured IA to Dhcp6Renewing. If valid Reply packet received,
+ the state transfers to Dhcp6Bound and the valid and preferred timer restarts.
+ If fails, the state transfers to Dhcp6Bound, but the timer continues.
+ - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,
+ it will send a Rebind packet. If valid Reply packet is received, the state transfers
+ to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state
+ transfers to Dhcp6Init, and the IA can't be used.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.
+ Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.
+
+ @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process has
+ completed and at least one IPv6 address of the
+ configured IA has been bound again when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL.
+ The EFI DHCPv6 Protocol instance has sent Renew
+ or Rebind packet when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ALREADY_STARTED The state of the configured IA has already entered
+ Dhcp6Renewing when RebindRequest is FALSE.
+ The state of the configured IA has already entered
+ Dhcp6Rebinding when RebindRequest is TRUE.
+ @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted
+ by the user.
+ @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed
+ because of no response.
+ @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured
+ IA after the DHCPv6 renew/rebind exchange process.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6RenewRebind (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN BOOLEAN RebindRequest
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ //
+ // The instance has already entered renewing or rebinding state.
+ //
+ if ((Instance->IaCb.Ia->State == Dhcp6Rebinding && RebindRequest) ||
+ (Instance->IaCb.Ia->State == Dhcp6Renewing && !RebindRequest)
+ ) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (Instance->IaCb.Ia->State != Dhcp6Bound) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Send renew/rebind message to start exchange process.
+ //
+ Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateful exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Inform that one or more addresses assigned by a server are already
+ in use by another node.
+
+ The Decline() function is used to manually decline the assignment of
+ IPv6 addresses, which have been already used by another node. If all
+ IPv6 addresses of the configured IA are declined through this function,
+ the state of the IA will switch through Dhcp6Declining to Dhcp6Init.
+ Otherwise, the state of the IA will restore to Dhcp6Bound after the
+ declining process. The Decline() can only be called when the IA is in
+ Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,
+ this function is a blocking operation. It will return after the
+ declining process finishes, or aborted by user.
+
+ @param[in] This The pointer to EFI_DHCP6_PROTOCOL.
+ @param[in] AddressCount The number of declining addresses.
+ @param[in] Addresses The pointer to the buffer stored the declining
+ addresses.
+
+ @retval EFI_SUCCESS The DHCPv6 decline exchange process completed
+ when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.
+ The Dhcp6 instance sent Decline packet when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ABORTED The DHCPv6 decline exchange process aborted by user.
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
+ the configured IA for this instance.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Decline (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_DHCP6_IA *DecIa;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL || AddressCount == 0 || Addresses == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ if (Instance->IaCb.Ia->State != Dhcp6Bound) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Check whether all the declined addresses belongs to the configured Ia.
+ //
+ Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Deprive of all the declined addresses from the configured Ia, and create a
+ // DeclineIa used to create decline message.
+ //
+ DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);
+
+ if (DecIa == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Send the decline message to start exchange process.
+ //
+ Status = Dhcp6SendDeclineMsg (Instance, DecIa);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateful exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ FreePool (DecIa);
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (DecIa != NULL) {
+ FreePool (DecIa);
+ }
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Release one or more addresses associated with the configured Ia
+ for current instance.
+
+ The Release() function is used to manually release one or more
+ IPv6 addresses. If AddressCount is zero, it will release all IPv6
+ addresses of the configured IA. If all IPv6 addresses of the IA are
+ released through this function, the state of the IA will switch
+ through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the
+ IA will restore to Dhcp6Bound after the releasing process.
+ The Release() can only be called when the IA is in Dhcp6Bound state.
+ If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is
+ a blocking operation. It will return after the releasing process
+ finishes, or is aborted by user.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] AddressCount The number of releasing addresses.
+ @param[in] Addresses The pointer to the buffer stored the releasing
+ addresses.
+
+ @retval EFI_SUCCESS The DHCPv6 release exchange process
+ completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
+ was NULL. The Dhcp6 instance was sent Release
+ packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
+ was not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ABORTED The DHCPv6 release exchange process aborted by user.
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
+ the configured IA for this instance.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Release (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_DHCP6_IA *RelIa;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL || (AddressCount != 0 && Addresses == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ if (Instance->IaCb.Ia->State != Dhcp6Bound) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Check whether all the released addresses belongs to the configured Ia.
+ //
+ Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Deprive of all the released addresses from the configured Ia, and create a
+ // ReleaseIa used to create release message.
+ //
+ RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);
+
+ if (RelIa == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Send the release message to start exchange process.
+ //
+ Status = Dhcp6SendReleaseMsg (Instance, RelIa);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateful exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ FreePool (RelIa);
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (RelIa != NULL) {
+ FreePool (RelIa);
+ }
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Parse the option data in the Dhcp6 packet.
+
+ The Parse() function is used to retrieve the option list in the DHCPv6 packet.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] Packet The pointer to the Dhcp6 packet.
+ @param[in, out] OptionCount The number of option in the packet.
+ @param[out] PacketOptionList The array of pointers to each option in the packet.
+
+ @retval EFI_SUCCESS The packet was successfully parsed.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options
+ that were found in the Packet.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Parse (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN EFI_DHCP6_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL
+ )
+{
+ UINT32 OptCnt;
+ UINT32 OptLen;
+ UINT16 DataLen;
+ UINT8 *Start;
+ UINT8 *End;
+
+ if (This == NULL || Packet == NULL || OptionCount == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*OptionCount != 0 && PacketOptionList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet->Length > Packet->Size || Packet->Length < sizeof (EFI_DHCP6_HEADER)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The format of Dhcp6 option:
+ //
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | option-code | option-len (option data) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | option-data |
+ // | (option-len octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ OptCnt = 0;
+ OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER);
+ Start = Packet->Dhcp6.Option;
+ End = Start + OptLen;
+
+ //
+ // Calculate the number of option in the packet.
+ //
+ while (Start < End) {
+ DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;
+ Start += (NTOHS (DataLen) + 4);
+ OptCnt++;
+ }
+
+ //
+ // It will return buffer too small if pass-in option count is smaller than the
+ // actual count of options in the packet.
+ //
+ if (OptCnt > *OptionCount) {
+ *OptionCount = OptCnt;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ ZeroMem (
+ PacketOptionList,
+ (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *))
+ );
+
+ OptCnt = 0;
+ Start = Packet->Dhcp6.Option;
+
+ while (Start < End) {
+
+ PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *) Start;
+ DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;
+ Start += (NTOHS (DataLen) + 4);
+ OptCnt++;
+ }
+
+ return EFI_SUCCESS;
+}
+
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h
new file mode 100644
index 0000000000..8fb1dfa382
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h
@@ -0,0 +1,597 @@
+/** @file
+ Dhcp6 internal data structure and definition declaration.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP6_IMPL_H__
+#define __EFI_DHCP6_IMPL_H__
+
+
+#include <Uefi.h>
+
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Udp6.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+
+#include <Library/UdpIoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+
+
+typedef struct _DHCP6_IA_CB DHCP6_IA_CB;
+typedef struct _DHCP6_INF_CB DHCP6_INF_CB;
+typedef struct _DHCP6_TX_CB DHCP6_TX_CB;
+typedef struct _DHCP6_SERVICE DHCP6_SERVICE;
+typedef struct _DHCP6_INSTANCE DHCP6_INSTANCE;
+
+#include "Dhcp6Utility.h"
+#include "Dhcp6Io.h"
+#include "Dhcp6Driver.h"
+
+#define DHCP6_SERVICE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'S')
+#define DHCP6_INSTANCE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'I')
+
+//
+// Transmit parameters of solicit message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_SOL_MAX_DELAY 1
+#define DHCP6_SOL_IRT 1
+#define DHCP6_SOL_MRC 0
+#define DHCP6_SOL_MRT 120
+#define DHCP6_SOL_MRD 0
+//
+// Transmit parameters of request message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_REQ_IRT 1
+#define DHCP6_REQ_MRC 10
+#define DHCP6_REQ_MRT 30
+#define DHCP6_REQ_MRD 0
+//
+// Transmit parameters of confirm message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_CNF_MAX_DELAY 1
+#define DHCP6_CNF_IRT 1
+#define DHCP6_CNF_MRC 0
+#define DHCP6_CNF_MRT 4
+#define DHCP6_CNF_MRD 10
+//
+// Transmit parameters of renew message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_REN_IRT 10
+#define DHCP6_REN_MRC 0
+#define DHCP6_REN_MRT 600
+#define DHCP6_REN_MRD 0
+//
+// Transmit parameters of rebind message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_REB_IRT 10
+#define DHCP6_REB_MRC 0
+#define DHCP6_REB_MRT 600
+#define DHCP6_REB_MRD 0
+//
+// Transmit parameters of information request message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_INF_MAX_DELAY 1
+#define DHCP6_INF_IRT 1
+#define DHCP6_INF_MRC 0
+#define DHCP6_INF_MRT 120
+#define DHCP6_INF_MRD 0
+//
+// Transmit parameters of release message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_REL_IRT 1
+#define DHCP6_REL_MRC 5
+#define DHCP6_REL_MRT 0
+#define DHCP6_REL_MRD 0
+//
+// Transmit parameters of decline message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_DEC_IRT 1
+#define DHCP6_DEC_MRC 5
+#define DHCP6_DEC_MRT 0
+#define DHCP6_DEC_MRD 0
+
+#define DHCP6_PACKET_ALL 0
+#define DHCP6_PACKET_STATEFUL 1
+#define DHCP6_PACKET_STATELESS 2
+
+#define DHCP6_BASE_PACKET_SIZE 1024
+
+#define DHCP6_PORT_CLIENT 546
+#define DHCP6_PORT_SERVER 547
+
+#define DHCP6_INSTANCE_FROM_THIS(Instance) CR ((Instance), DHCP6_INSTANCE, Dhcp6, DHCP6_INSTANCE_SIGNATURE)
+#define DHCP6_SERVICE_FROM_THIS(Service) CR ((Service), DHCP6_SERVICE, ServiceBinding, DHCP6_SERVICE_SIGNATURE)
+
+extern EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress;
+extern EFI_IPv6_ADDRESS mAllDhcpServersAddress;
+extern EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate;
+
+//
+// Enumeration of Dhcp6 message type, refers to section-5.3 of rfc-3315.
+//
+typedef enum {
+ Dhcp6MsgSolicit = 1,
+ Dhcp6MsgAdvertise = 2,
+ Dhcp6MsgRequest = 3,
+ Dhcp6MsgConfirm = 4,
+ Dhcp6MsgRenew = 5,
+ Dhcp6MsgRebind = 6,
+ Dhcp6MsgReply = 7,
+ Dhcp6MsgRelease = 8,
+ Dhcp6MsgDecline = 9,
+ Dhcp6MsgReconfigure = 10,
+ Dhcp6MsgInfoRequest = 11
+} DHCP6_MSG_TYPE;
+
+//
+// Enumeration of option code in Dhcp6 packet, refers to section-24.3 of rfc-3315.
+//
+typedef enum {
+ Dhcp6OptClientId = 1,
+ Dhcp6OptServerId = 2,
+ Dhcp6OptIana = 3,
+ Dhcp6OptIata = 4,
+ Dhcp6OptIaAddr = 5,
+ Dhcp6OptRequestOption = 6,
+ Dhcp6OptPreference = 7,
+ Dhcp6OptElapsedTime = 8,
+ Dhcp6OptReplayMessage = 9,
+ Dhcp6OptAuthentication = 11,
+ Dhcp6OptServerUnicast = 12,
+ Dhcp6OptStatusCode = 13,
+ Dhcp6OptRapidCommit = 14,
+ Dhcp6OptUserClass = 15,
+ Dhcp6OptVendorClass = 16,
+ Dhcp6OptVendorInfo = 17,
+ Dhcp6OptInterfaceId = 18,
+ Dhcp6OptReconfigMessage = 19,
+ Dhcp6OptReconfigureAccept = 20
+} DHCP6_OPT_CODE;
+
+//
+// Enumeration of status code recorded by IANA, refers to section-24.4 of rfc-3315.
+//
+typedef enum {
+ Dhcp6StsSuccess = 0,
+ Dhcp6StsUnspecFail = 1,
+ Dhcp6StsNoAddrsAvail = 2,
+ Dhcp6StsNoBinding = 3,
+ Dhcp6StsNotOnLink = 4,
+ Dhcp6StsUseMulticast = 5
+} DHCP6_STS_CODE;
+
+//
+// Enumeration of Duid type recorded by IANA, refers to section-24.5 of rfc-3315.
+//
+typedef enum {
+ Dhcp6DuidTypeLlt = 1,
+ Dhcp6DuidTypeEn = 2,
+ Dhcp6DuidTypeLl = 3
+} DHCP6_DUID_TYPE;
+
+//
+// Control block for each IA.
+//
+struct _DHCP6_IA_CB {
+ EFI_DHCP6_IA *Ia;
+ UINT32 T1;
+ UINT32 T2;
+ UINT32 AllExpireTime;
+ UINT32 LeaseTime;
+};
+
+//
+// Control block for each transmitted message.
+//
+struct _DHCP6_TX_CB {
+ LIST_ENTRY Link;
+ UINT32 Xid;
+ EFI_DHCP6_PACKET *TxPacket;
+ EFI_DHCP6_RETRANSMISSION RetryCtl;
+ UINT32 RetryCnt;
+ UINT32 RetryExp;
+ UINT32 RetryLos;
+ UINT32 TickTime;
+ UINT16 *Elapsed;
+};
+
+//
+// Control block for each info-request message.
+//
+struct _DHCP6_INF_CB {
+ LIST_ENTRY Link;
+ UINT32 Xid;
+ EFI_DHCP6_INFO_CALLBACK ReplyCallback;
+ VOID *CallbackContext;
+ EFI_EVENT TimeoutEvent;
+};
+
+//
+// Control block for Dhcp6 instance, it's per configuration data.
+//
+struct _DHCP6_INSTANCE {
+ UINT32 Signature;
+ EFI_HANDLE Handle;
+ DHCP6_SERVICE *Service;
+ LIST_ENTRY Link;
+ EFI_DHCP6_PROTOCOL Dhcp6;
+ EFI_EVENT Timer;
+ EFI_DHCP6_CONFIG_DATA *Config;
+ EFI_DHCP6_IA *CacheIa;
+ DHCP6_IA_CB IaCb;
+ LIST_ENTRY TxList;
+ LIST_ENTRY InfList;
+ EFI_DHCP6_PACKET *AdSelect;
+ UINT8 AdPref;
+ EFI_IPv6_ADDRESS *Unicast;
+ EFI_STATUS UdpSts;
+ BOOLEAN InDestory;
+ BOOLEAN MediaPresent;
+ UINT64 StartTime;
+};
+
+//
+// Control block for Dhcp6 service, it's per Nic handle.
+//
+struct _DHCP6_SERVICE {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_DHCP6_DUID *ClientId;
+ UDP_IO *UdpIo;
+ UINT32 Xid;
+ LIST_ENTRY Child;
+ UINTN NumOfChild;
+ BOOLEAN InDestory;
+};
+
+/**
+ Starts the DHCPv6 standard S.A.R.R. process.
+
+ The Start() function starts the DHCPv6 standard process. This function can
+ be called only when the state of Dhcp6 instance is in the Dhcp6Init state.
+ If the DHCP process completes successfully, the state of the Dhcp6 instance
+ will be transferred through Dhcp6Selecting and Dhcp6Requesting to the
+ Dhcp6Bound state.
+ Refer to rfc-3315 for precise state transitions during this process. At the
+ time when each event occurs in this process, the callback function that was set
+ by EFI_DHCP6_PROTOCOL.Configure() will be called and the user can take this
+ opportunity to control the process.
+
+ @param[in] This The pointer to Dhcp6 protocol.
+
+ @retval EFI_SUCCESS The DHCPv6 standard process has started, or it
+ completed when CompletionEvent was NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no
+ response was received from the server within the
+ specified timeout value.
+ @retval EFI_ABORTED The user aborted the DHCPv6 process.
+ @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6
+ standard process.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Start (
+ IN EFI_DHCP6_PROTOCOL *This
+ );
+
+/**
+ Stops the DHCPv6 standard S.A.R.R. process.
+
+ The Stop() function is used to stop the DHCPv6 standard process. After this
+ function is called successfully, the state of Dhcp6 instance is transferred
+ into the Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called
+ before DHCPv6 standard process can be started again. This function can be
+ called when the Dhcp6 instance is in any state.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+
+ @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Stop (
+ IN EFI_DHCP6_PROTOCOL *This
+ );
+
+/**
+ Returns the current operating mode data for the Dhcp6 instance.
+
+ The GetModeData() function returns the current operating mode and
+ cached data packet for the Dhcp6 instance.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data.
+ @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data.
+
+ @retval EFI_SUCCESS The mode data was returned.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance has not
+ been configured when Dhcp6ConfigData is
+ not NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6GetModeData (
+ IN EFI_DHCP6_PROTOCOL *This,
+ OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL,
+ OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL
+ );
+
+/**
+ Initializes, changes, or resets the operational settings for the Dhcp6 instance.
+
+ The Configure() function is used to initialize or clean up the configuration
+ data of the Dhcp6 instance:
+ - When Dhcp6CfgData is not NULL and Configure() is called successfully, the
+ configuration data will be initialized in the Dhcp6 instance and the state
+ of the configured IA will be transferred into Dhcp6Init.
+ - When Dhcp6CfgData is NULL and Configure() is called successfully, the
+ configuration data will be cleaned up and no IA will be associated with
+ the Dhcp6 instance.
+ To update the configuration data for an Dhcp6 instance, the original data
+ must be cleaned up before setting the new configuration data.
+
+ @param[in] This The pointer to the Dhcp6 protocol
+ @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA.
+
+ @retval EFI_SUCCESS The Dhcp6 is configured successfully with the
+ Dhcp6Init state, or cleaned up the original
+ configuration setting.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance has been already configured
+ when Dhcp6CfgData is not NULL.
+ The Dhcp6 instance has already started the
+ DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.
+ @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Configure (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL
+ );
+
+/**
+ Request configuration information without the assignment of any
+ Ia addresses of the client.
+
+ The InfoRequest() function is used to request configuration information
+ without the assignment of any IPv6 address of the client. Client sends
+ out Information Request packet to obtain the required configuration
+ information, and DHCPv6 server responds with Reply packet containing
+ the information for the client. The received Reply packet will be passed
+ to the user by ReplyCallback function. If user returns EFI_NOT_READY from
+ ReplyCallback, the Dhcp6 instance will continue to receive other Reply
+ packets unless timeout according to the Retransmission parameter.
+ Otherwise, the Information Request exchange process will be finished
+ successfully if user returns EFI_SUCCESS from ReplyCallback.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client
+ Identifier option and include it into Information Request
+ packet. Otherwise, Client Identifier option will not be included.
+ @param[in] OptionRequest The pointer to the buffer of option request options.
+ @param[in] OptionCount The option number in the OptionList.
+ @param[in] OptionList The list of appended options.
+ @param[in] Retransmission The pointer to the retransmission of the message.
+ @param[in] TimeoutEvent The event of timeout.
+ @param[in] ReplyCallback The callback function when a reply was received.
+ @param[in] CallbackContext The pointer to the parameter passed to the callback.
+
+ @retval EFI_SUCCESS The DHCPv6 information request exchange process
+ completed when TimeoutEvent is NULL. Information
+ Request packet has been sent to DHCPv6 server when
+ TimeoutEvent is not NULL.
+ @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed
+ because of no response, or not all requested-options
+ are responded to by DHCPv6 servers when Timeout happened.
+ @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted
+ by the user.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6InfoRequest (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN BOOLEAN SendClientId,
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
+ IN UINT32 OptionCount,
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL,
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission,
+ IN EFI_EVENT TimeoutEvent OPTIONAL,
+ IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,
+ IN VOID *CallbackContext OPTIONAL
+ );
+
+/**
+ Manually extend the valid and preferred lifetimes for the IPv6 addresses
+ of the configured IA and update other configuration parameters by sending
+ Renew or Rebind packet.
+
+ The RenewRebind() function is used to manually extend the valid and preferred
+ lifetimes for the IPv6 addresses of the configured IA and update other
+ configuration parameters by sending a Renew or Rebind packet.
+ - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,
+ it will send Renew packet to the previously DHCPv6 server and transfer the
+ state of the configured IA to Dhcp6Renewing. If valid Reply packet received,
+ the state transfers to Dhcp6Bound and the valid and preferred timer restarts.
+ If fails, the state transfers to Dhcp6Bound but the timer continues.
+ - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,
+ it will send a Rebind packet. If a valid Reply packet is received, the state transfers
+ to Dhcp6Bound, and the valid and preferred timer restarts. If it fails, the state
+ transfers to Dhcp6Init, and the IA can't be used.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.
+ Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.
+
+ @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process
+ completed and at least one IPv6 address of the
+ configured IA was bound again when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.
+ The EFI DHCPv6 Protocol instance has sent Renew
+ or Rebind packet when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ALREADY_STARTED The state of the configured IA has already entered
+ Dhcp6Renewing when RebindRequest is FALSE.
+ The state of the configured IA has already entered
+ Dhcp6Rebinding when RebindRequest is TRUE.
+ @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted
+ by user.
+ @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed
+ because of no response.
+ @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured
+ IA after the DHCPv6 renew/rebind exchange process.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6RenewRebind (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN BOOLEAN RebindRequest
+ );
+
+/**
+ Inform that one or more addresses assigned by a server are already
+ in use by another node.
+
+ The Decline() function is used to manually decline the assignment of
+ IPv6 addresses, which have been already used by another node. If all
+ IPv6 addresses of the configured IA are declined through this function,
+ the state of the IA will switch through Dhcp6Declining to Dhcp6Init.
+ Otherwise, the state of the IA will restore to Dhcp6Bound after the
+ declining process. The Decline() can only be called when the IA is in
+ Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,
+ this function is a blocking operation. It will return after the
+ declining process finishes, or aborted by user.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] AddressCount The number of declining addresses.
+ @param[in] Addresses The pointer to the buffer stored the declining
+ addresses.
+
+ @retval EFI_SUCCESS The DHCPv6 decline exchange process completed
+ when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.
+ The Dhcp6 instance has sent Decline packet when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ABORTED The DHCPv6 decline exchange process was aborted by the user.
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
+ the configured IA for this instance.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Decline (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ );
+
+/**
+ Release one or more addresses associated with the configured Ia
+ for the current instance.
+
+ The Release() function is used to manually release the one or more
+ IPv6 address. If AddressCount is zero, it will release all IPv6
+ addresses of the configured IA. If all IPv6 addresses of the IA are
+ released through this function, the state of the IA will switch
+ through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the
+ IA will restore to Dhcp6Bound after the releasing process.
+ The Release() can only be called when the IA is in a Dhcp6Bound state.
+ If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is
+ a blocking operation. It will return after the releasing process
+ finishes, or aborted by user.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] AddressCount The number of releasing addresses.
+ @param[in] Addresses The pointer to the buffer stored the releasing
+ addresses.
+ @retval EFI_SUCCESS The DHCPv6 release exchange process has
+ completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
+ is NULL. The Dhcp6 instance has sent Release
+ packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
+ is not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ABORTED The DHCPv6 release exchange process was aborted by the user.
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
+ the configured IA for this instance.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Release (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ );
+
+/**
+ Parse the option data in the Dhcp6 packet.
+
+ The Parse() function is used to retrieve the option list in the DHCPv6 packet.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] Packet The pointer to the Dhcp6 packet.
+ @param[in, out] OptionCount The number of option in the packet.
+ @param[out] PacketOptionList The array of pointers to the each option in the packet.
+
+ @retval EFI_SUCCESS The packet was successfully parsed.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options
+ that were found in the Packet.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Parse (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN EFI_DHCP6_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL
+ );
+
+#endif
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c
new file mode 100644
index 0000000000..761f9c2d36
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c
@@ -0,0 +1,2965 @@
+/** @file
+ Dhcp6 internal functions implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+
+/**
+ Enqueue the packet into the retry list in case of timeout.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to the Dhcp6 packet to retry.
+ @param[in] Elapsed The pointer to the elapsed time value in the packet.
+ @param[in] RetryCtl The pointer to the transmission control of the packet.
+ This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS Successfully enqueued the packet into the retry list according
+ to its message type.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected message type.
+
+**/
+EFI_STATUS
+Dhcp6EnqueueRetry (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet,
+ IN UINT16 *Elapsed,
+ IN EFI_DHCP6_RETRANSMISSION *RetryCtl OPTIONAL
+ )
+{
+ DHCP6_TX_CB *TxCb;
+ DHCP6_IA_CB *IaCb;
+
+ ASSERT (Packet != NULL);
+
+ IaCb = &Instance->IaCb;
+ TxCb = AllocateZeroPool (sizeof (DHCP6_TX_CB));
+
+ if (TxCb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Save tx packet pointer, and it will be destoryed when reply received.
+ //
+ TxCb->TxPacket = Packet;
+ TxCb->Xid = Packet->Dhcp6.Header.TransactionId;
+
+ //
+ // Save pointer to elapsed-time value so we can update it on retransmits.
+ //
+ TxCb->Elapsed = Elapsed;
+
+ //
+ // Calculate the retransmission according to the the message type.
+ //
+ switch (Packet->Dhcp6.Header.MessageType) {
+ case Dhcp6MsgSolicit:
+ //
+ // Calculate the retransmission threshold value for solicit packet.
+ // Use the default value by rfc-3315 if user doesn't configure.
+ //
+ if (RetryCtl == NULL) {
+ TxCb->RetryCtl.Irt = DHCP6_SOL_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_SOL_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_SOL_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_SOL_MRD;
+ } else {
+ TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_SOL_IRT;
+ TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_SOL_MRC;
+ TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_SOL_MRT;
+ TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_SOL_MRD;
+ }
+
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ FALSE
+ );
+ break;
+
+ case Dhcp6MsgRequest:
+ //
+ // Calculate the retransmission threshold value for request packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_REQ_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_REQ_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_REQ_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_REQ_MRD;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgConfirm:
+ //
+ // Calculate the retransmission threshold value for confirm packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_CNF_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_CNF_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_CNF_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_CNF_MRD;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgRenew:
+ //
+ // Calculate the retransmission threshold value for renew packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_REB_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_REB_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_REB_MRT;
+ TxCb->RetryCtl.Mrd = IaCb->T2 - IaCb->T1;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgRebind:
+ //
+ // Calculate the retransmission threshold value for rebind packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_REN_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_REN_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_REN_MRT;
+ TxCb->RetryCtl.Mrd = IaCb->AllExpireTime - IaCb->T2;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgDecline:
+ //
+ // Calculate the retransmission threshold value for decline packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_DEC_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_DEC_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_DEC_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_DEC_MRD;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgRelease:
+ //
+ // Calculate the retransmission threshold value for release packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_REL_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_REL_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_REL_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_REL_MRD;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgInfoRequest:
+ //
+ // Calculate the retransmission threshold value for info-request packet.
+ // Use the default value by rfc-3315 if user doesn't configure.
+ //
+ if (RetryCtl == NULL) {
+ TxCb->RetryCtl.Irt = DHCP6_INF_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_INF_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_INF_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_INF_MRD;
+ } else {
+ TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_INF_IRT;
+ TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_INF_MRC;
+ TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_INF_MRT;
+ TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_INF_MRD;
+ }
+
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ default:
+ //
+ // Unexpected message type.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Insert into the retransmit list of the instance.
+ //
+ InsertTailList (&Instance->TxList, &TxCb->Link);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Dequeue the packet from retry list if reply received or timeout at last.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] PacketXid The packet transaction id to match.
+ @param[in] NeedSignal If TRUE, then an timeout event need be signaled when it is existed.
+ Otherwise, this parameter is ignored.
+
+ @retval EFI_SUCCESS Successfully dequeued the packet into retry list .
+ @retval EFI_NOT_FOUND There is no xid matched in retry list.
+
+**/
+EFI_STATUS
+Dhcp6DequeueRetry (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT32 PacketXid,
+ IN BOOLEAN NeedSignal
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ DHCP6_TX_CB *TxCb;
+ DHCP6_INF_CB *InfCb;
+
+ //
+ // Seek the retransmit node in the retransmit list by packet xid.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
+ ASSERT(TxCb->TxPacket);
+
+ if (TxCb->Xid == PacketXid) {
+
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
+
+ //
+ // Seek the info-request node in the info-request list by packet xid.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {
+
+ InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);
+
+ if (InfCb->Xid == PacketXid) {
+ //
+ // Remove the info-request node, and signal the event if timeout.
+ //
+ if (InfCb->TimeoutEvent != NULL && NeedSignal) {
+ gBS->SignalEvent (InfCb->TimeoutEvent);
+ }
+
+ RemoveEntryList (&InfCb->Link);
+ FreePool (InfCb);
+ }
+ }
+ }
+ //
+ // Remove the retransmit node.
+ //
+ RemoveEntryList (&TxCb->Link);
+ ASSERT(TxCb->TxPacket);
+ FreePool (TxCb->TxPacket);
+ FreePool (TxCb);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Clean up the specific nodes in the retry list.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Scope The scope of cleanup nodes.
+
+**/
+VOID
+Dhcp6CleanupRetry (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT32 Scope
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ DHCP6_TX_CB *TxCb;
+ DHCP6_INF_CB *InfCb;
+
+ //
+ // Clean up all the stateful messages from the retransmit list.
+ //
+ if (Scope == DHCP6_PACKET_STATEFUL || Scope == DHCP6_PACKET_ALL) {
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
+ ASSERT(TxCb->TxPacket);
+
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType != Dhcp6MsgInfoRequest) {
+ RemoveEntryList (&TxCb->Link);
+ FreePool (TxCb->TxPacket);
+ FreePool (TxCb);
+ }
+ }
+ }
+
+ //
+ // Clean up all the stateless messages from the retransmit list.
+ //
+ if (Scope == DHCP6_PACKET_STATELESS || Scope == DHCP6_PACKET_ALL) {
+
+ //
+ // Clean up all the retransmit list for stateless messages.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
+ ASSERT(TxCb->TxPacket);
+
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
+ RemoveEntryList (&TxCb->Link);
+ FreePool (TxCb->TxPacket);
+ FreePool (TxCb);
+ }
+ }
+
+ //
+ // Clean up all the info-request messages list.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {
+
+ InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);
+
+ if (InfCb->TimeoutEvent != NULL) {
+ gBS->SignalEvent (InfCb->TimeoutEvent);
+ }
+ RemoveEntryList (&InfCb->Link);
+ FreePool (InfCb);
+ }
+ }
+}
+
+
+/**
+ Clean up the session of the instance stateful exchange.
+
+ @param[in, out] Instance The pointer to the Dhcp6 instance.
+ @param[in] Status The return status from udp.
+
+**/
+VOID
+Dhcp6CleanupSession (
+ IN OUT DHCP6_INSTANCE *Instance,
+ IN EFI_STATUS Status
+ )
+{
+ UINTN Index;
+ EFI_DHCP6_IA *Ia;
+
+ ASSERT(Instance->Config);
+ ASSERT(Instance->IaCb.Ia);
+
+ //
+ // Clean up the retransmit list for stateful messages.
+ //
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATEFUL);
+
+ if (Instance->Unicast != NULL) {
+ FreePool (Instance->Unicast);
+ }
+
+ if (Instance->AdSelect != NULL) {
+ FreePool (Instance->AdSelect);
+ }
+
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ }
+
+ //
+ // Reinitialize the Ia fields of the instance.
+ //
+ Instance->UdpSts = Status;
+ Instance->AdSelect = NULL;
+ Instance->AdPref = 0;
+ Instance->Unicast = NULL;
+ Instance->IaCb.T1 = 0;
+ Instance->IaCb.T2 = 0;
+ Instance->IaCb.AllExpireTime = 0;
+ Instance->IaCb.LeaseTime = 0;
+
+ //
+ // Clear start time
+ //
+ Instance->StartTime = 0;
+
+ Ia = Instance->IaCb.Ia;
+ Ia->State = Dhcp6Init;
+ Ia->ReplyPacket = NULL;
+
+ //
+ // Set the addresses as zero lifetime, and then the notify
+ // function in Ip6Config will remove these timeout address.
+ //
+ for (Index = 0; Index < Ia->IaAddressCount; Index++) {
+ Ia->IaAddress[Index].PreferredLifetime = 0;
+ Ia->IaAddress[Index].ValidLifetime = 0;
+ }
+
+ //
+ //
+ // Signal the Ia information updated event to informal user.
+ //
+ if (Instance->Config->IaInfoEvent != NULL) {
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);
+ }
+}
+
+
+/**
+ Callback to user when Dhcp6 transmit/receive occurs.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Event The current Dhcp6 event.
+ @param[in, out] Packet The pointer to the packet sending or received.
+
+ @retval EFI_SUCCESS The user function returns success.
+ @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
+ @retval EFI_ABORTED The user function ask it to abort.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6CallbackUser (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_EVENT Event,
+ IN OUT EFI_DHCP6_PACKET **Packet
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *NewPacket;
+ EFI_DHCP6_CALLBACK Callback;
+ VOID *Context;
+
+ ASSERT (Packet != NULL);
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ NewPacket = NULL;
+ Status = EFI_SUCCESS;
+ Callback = Instance->Config->Dhcp6Callback;
+ Context = Instance->Config->CallbackContext;
+
+ //
+ // Callback to user with the new message if has.
+ //
+ if (Callback != NULL) {
+
+ Status = Callback (
+ &Instance->Dhcp6,
+ Context,
+ Instance->IaCb.Ia->State,
+ Event,
+ *Packet,
+ &NewPacket
+ );
+ //
+ // Updated the new packet from user to replace the original one.
+ //
+ if (NewPacket != NULL) {
+ ASSERT (*Packet != NULL);
+ FreePool (*Packet);
+ *Packet = NewPacket;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Update Ia according to the new reply message.
+
+ @param[in, out] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to reply messages.
+
+ @retval EFI_SUCCESS Updated the Ia information successfully.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+
+**/
+EFI_STATUS
+Dhcp6UpdateIaInfo (
+ IN OUT DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_STATE State;
+ UINT8 *Option;
+ UINT8 *IaInnerOpt;
+ UINT16 IaInnerLen;
+ UINT16 StsCode;
+ UINT32 T1;
+ UINT32 T2;
+
+ ASSERT (Instance->Config != NULL);
+ //
+ // If the reply was received in reponse to a solicit with rapid commit option,
+ // request, renew or rebind message, the client updates the information it has
+ // recorded about IAs from the IA options contained in the reply message:
+ // 1. record the T1 and T2 times
+ // 2. add any new addresses in the IA
+ // 3. discard any addresses from the IA, that have a valid lifetime of 0
+ // 4. update lifetimes for any addresses that alread recorded
+ // 5. leave unchanged any information about addresses
+ //
+ // See details in the section-18.1.8 of rfc-3315.
+ //
+ State = Dhcp6Init;
+ Option = Dhcp6SeekIaOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - sizeof (EFI_DHCP6_HEADER),
+ &Instance->Config->IaDescriptor
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // The format of the IA_NA option is:
+ //
+ // 0 1 2 3
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_NA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T1 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T2 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_NA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+ // The format of the IA_TA option is:
+ //
+ // 0 1 2 3
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_TA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_TA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-code + option-len + IaId) = 8
+ // sizeof (option-code + option-len + IaId + T1) = 12
+ // sizeof (option-code + option-len + IaId + T1 + T2) = 16
+ //
+ // The inner options still start with 2 bytes option-code and 2 bytes option-len.
+ //
+ if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {
+ T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8)));
+ T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12)));
+ IaInnerOpt = Option + 16;
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12);
+ } else {
+ T1 = 0;
+ T2 = 0;
+ IaInnerOpt = Option + 8;
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 4);
+ }
+
+ //
+ // The format of the Status Code option is:
+ //
+ // 0 1 2 3
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_STATUS_CODE | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | status-code | |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ // . .
+ // . status-message .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-code + option-len) = 4
+ //
+ StsCode = Dhcp6StsSuccess;
+ Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);
+
+ if (Option != NULL) {
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));
+ if (StsCode != Dhcp6StsSuccess) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Generate control block for the Ia.
+ //
+ Status = Dhcp6GenerateIaCb (
+ Instance,
+ IaInnerOpt,
+ IaInnerLen,
+ T1,
+ T2
+ );
+
+ return Status;
+}
+
+
+
+/**
+ Seek StatusCode Option in package. A Status Code option may appear in the
+ options field of a DHCP message and/or in the options field of another option.
+ See details in section 22.13, RFC3315.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to reply messages.
+ @param[out] Option The pointer to status code option.
+
+ @retval EFI_SUCCESS Seek status code option successfully.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+
+**/
+EFI_STATUS
+Dhcp6SeekStsOption (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet,
+ OUT UINT8 **Option
+ )
+{
+ UINT8 *IaInnerOpt;
+ UINT16 IaInnerLen;
+ UINT16 StsCode;
+
+ //
+ // Seek StatusCode option directly in DHCP message body. That is, search in
+ // non-encapsulated option fields.
+ //
+ *Option = Dhcp6SeekOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptStatusCode
+ );
+
+ if (*Option != NULL) {
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));
+ if (StsCode != Dhcp6StsSuccess) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Seek in encapsulated options, IA_NA and IA_TA.
+ //
+ *Option = Dhcp6SeekIaOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - sizeof (EFI_DHCP6_HEADER),
+ &Instance->Config->IaDescriptor
+ );
+ if (*Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // The format of the IA_NA option is:
+ //
+ // 0 1 2 3
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_NA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T1 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T2 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_NA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+ // The format of the IA_TA option is:
+ //
+ // 0 1 2 3
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_TA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_TA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-code + option-len + IaId) = 8
+ // sizeof (option-code + option-len + IaId + T1) = 12
+ // sizeof (option-code + option-len + IaId + T1 + T2) = 16
+ //
+ // The inner options still start with 2 bytes option-code and 2 bytes option-len.
+ //
+ if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {
+ IaInnerOpt = *Option + 16;
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 12);
+ } else {
+ IaInnerOpt = *Option + 8;
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 4);
+ }
+
+ //
+ // The format of the Status Code option is:
+ //
+ // 0 1 2 3
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_STATUS_CODE | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | status-code | |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ // . .
+ // . status-message .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-code + option-len) = 4
+ //
+ *Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);
+ if (*Option != NULL) {
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));
+ if (StsCode != Dhcp6StsSuccess) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Transmit Dhcp6 message by udpio.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to transmit message.
+ @param[in] Elapsed The pointer to the elapsed time value to fill in.
+
+ @retval EFI_SUCCESS Successfully transmitted the packet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Dhcp6TransmitPacket (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet,
+ IN UINT16 *Elapsed
+ )
+{
+ EFI_STATUS Status;
+ NET_BUF *Wrap;
+ NET_FRAGMENT Frag;
+ UDP_END_POINT EndPt;
+ DHCP6_SERVICE *Service;
+
+ Service = Instance->Service;
+
+ //
+ // Wrap it into a netbuf then send it.
+ //
+ Frag.Bulk = (UINT8 *) &Packet->Dhcp6.Header;
+ Frag.Len = Packet->Length;
+
+ //
+ // Do not register free packet here, which will be handled in retry list.
+ //
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, Dhcp6DummyExtFree, NULL);
+
+ if (Wrap == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Multicast the Dhcp6 message, unless get the unicast server address by option.
+ //
+ ZeroMem (&EndPt, sizeof (UDP_END_POINT));
+
+ if (Instance->Unicast != NULL) {
+ CopyMem (
+ &EndPt.RemoteAddr,
+ Instance->Unicast,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ } else {
+ CopyMem (
+ &EndPt.RemoteAddr,
+ &mAllDhcpRelayAndServersAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ }
+
+ EndPt.RemotePort = DHCP6_PORT_SERVER;
+ EndPt.LocalPort = DHCP6_PORT_CLIENT;
+
+ //
+ // Update the elapsed time value.
+ //
+ if (Elapsed != NULL) {
+ SetElapsedTime (Elapsed, Instance);
+ }
+
+ //
+ // Send out the message by the configured Udp6Io.
+ //
+ Status = UdpIoSendDatagram (
+ Service->UdpIo,
+ Wrap,
+ &EndPt,
+ NULL,
+ Dhcp6OnTransmitted,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Wrap);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Create the solicit message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Created and sent the solicit message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the solicit message.
+
+**/
+EFI_STATUS
+Dhcp6SendSolicitMsg (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ EFI_DHCP6_DUID *ClientId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT32 UserLen;
+ UINTN Index;
+ UINT16 Length;
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ UserLen = 0;
+
+ ASSERT (Service->ClientId != NULL);
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgSolicit;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for solicit message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendIaOption (
+ Cursor,
+ Instance->IaCb.Ia,
+ Instance->IaCb.T1,
+ Instance->IaCb.T2
+ );
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+
+ UserOpt = Instance->Config->OptionList[Index];
+ Cursor = Dhcp6AppendOption(
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendSolicit, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send solicit packet with the state transition from Dhcp6init to
+ // Dhcp6selecting.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Selecting;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (
+ Instance,
+ Packet,
+ Elapsed,
+ Instance->Config->SolicitRetransmission
+ );
+}
+
+/**
+ Configure some parameter to initiate SolicitMsg.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Created and sent the solicit message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the solicit message.
+
+**/
+EFI_STATUS
+Dhcp6InitSolicitMsg (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ Instance->IaCb.T1 = 0;
+ Instance->IaCb.T2 = 0;
+ Instance->IaCb.Ia->IaAddressCount = 0;
+
+ return Dhcp6SendSolicitMsg (Instance);
+}
+
+
+/**
+ Create the request message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Created and sent the request message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the request message.
+
+**/
+EFI_STATUS
+Dhcp6SendRequestMsg (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_DUID *ServerId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT32 UserLen;
+ UINTN Index;
+ UINT16 Length;
+
+ ASSERT(Instance->AdSelect != NULL);
+ ASSERT(Instance->Config != NULL);
+ ASSERT(Instance->IaCb.Ia != NULL);
+ ASSERT(Instance->Service != NULL);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+
+ ASSERT(ClientId != NULL);
+
+ //
+ // Get the server Id from the selected advertisement message.
+ //
+ Option = Dhcp6SeekOption (
+ Instance->AdSelect->Dhcp6.Option,
+ Instance->AdSelect->Length - 4,
+ Dhcp6OptServerId
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ UserLen = 0;
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgRequest;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for request message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptServerId),
+ ServerId->Length,
+ ServerId->Duid
+ );
+
+ Cursor = Dhcp6AppendIaOption (
+ Cursor,
+ Instance->IaCb.Ia,
+ Instance->IaCb.T1,
+ Instance->IaCb.T2
+ );
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+
+ UserOpt = Instance->Config->OptionList[Index];
+ Cursor = Dhcp6AppendOption(
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendRequest, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send request packet with the state transition from Dhcp6selecting to
+ // Dhcp6requesting.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Requesting;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+/**
+ Create the decline message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] DecIa The pointer to the decline Ia.
+
+ @retval EFI_SUCCESS Created and sent the decline message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the decline message.
+
+**/
+EFI_STATUS
+Dhcp6SendDeclineMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_IA *DecIa
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET *LastReply;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_DUID *ServerId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT16 Length;
+
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+ ASSERT (Instance->Service != NULL);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ LastReply = Instance->IaCb.Ia->ReplyPacket;
+
+ ASSERT (ClientId != NULL);
+ ASSERT (LastReply != NULL);
+
+ //
+ // Get the server Id from the last reply message.
+ //
+ Option = Dhcp6SeekOption (
+ LastReply->Dhcp6.Option,
+ LastReply->Length - 4,
+ Dhcp6OptServerId
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // EFI_DHCP6_DUID contains a length field of 2 bytes.
+ //
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgDecline;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for rebind/renew message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptServerId),
+ ServerId->Length,
+ ServerId->Duid
+ );
+
+ Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0);
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendDecline, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send decline packet with the state transition from Dhcp6bound to
+ // Dhcp6declining.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Declining;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+/**
+ Create the release message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] RelIa The pointer to the release Ia.
+
+ @retval EFI_SUCCESS Created and sent the release message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the release message.
+
+**/
+EFI_STATUS
+Dhcp6SendReleaseMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_IA *RelIa
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET *LastReply;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_DUID *ServerId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT16 Length;
+
+ ASSERT(Instance->Config);
+ ASSERT(Instance->IaCb.Ia);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ LastReply = Instance->IaCb.Ia->ReplyPacket;
+
+ ASSERT(ClientId);
+ ASSERT(LastReply);
+
+ //
+ // Get the server Id from the last reply message.
+ //
+ Option = Dhcp6SeekOption (
+ LastReply->Dhcp6.Option,
+ LastReply->Length - 4,
+ Dhcp6OptServerId
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgRelease;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for rebind/renew message
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ //
+ // ServerId is extracted from packet, it's network order.
+ //
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptServerId),
+ ServerId->Length,
+ ServerId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0);
+
+ //
+ // Determine the size/length of packet
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendRelease, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send release packet with the state transition from Dhcp6bound to
+ // Dhcp6releasing.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Releasing;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+/**
+ Create the renew/rebind message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] RebindRequest If TRUE, it is a Rebind type message.
+ Otherwise, it is a Renew type message.
+
+ @retval EFI_SUCCESS Created and sent the renew/rebind message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the renew/rebind message.
+
+**/
+EFI_STATUS
+Dhcp6SendRenewRebindMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN BOOLEAN RebindRequest
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET *LastReply;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_DUID *ServerId;
+ EFI_DHCP6_STATE State;
+ EFI_DHCP6_EVENT Event;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT32 UserLen;
+ UINTN Index;
+ UINT16 Length;
+
+ ASSERT(Instance->Config);
+ ASSERT(Instance->IaCb.Ia);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+
+ ASSERT(ClientId);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ UserLen = 0;
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = RebindRequest ? Dhcp6MsgRebind : Dhcp6MsgRenew;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for rebind/renew message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendIaOption (
+ Cursor,
+ Instance->IaCb.Ia,
+ Instance->IaCb.T1,
+ Instance->IaCb.T2
+ );
+
+ if (!RebindRequest) {
+ //
+ // Get the server Id from the last reply message and
+ // insert it for rebind request.
+ //
+ LastReply = Instance->IaCb.Ia->ReplyPacket;
+ ASSERT (LastReply);
+
+ Option = Dhcp6SeekOption (
+ LastReply->Dhcp6.Option,
+ LastReply->Length - 4,
+ Dhcp6OptServerId
+ );
+ if (Option == NULL) {
+ FreePool (Packet);
+ return EFI_DEVICE_ERROR;
+ }
+
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);
+
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptServerId),
+ ServerId->Length,
+ ServerId->Duid
+ );
+ }
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+
+ UserOpt = Instance->Config->OptionList[Index];
+ Cursor = Dhcp6AppendOption(
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ State = (RebindRequest) ? Dhcp6Rebinding : Dhcp6Renewing;
+ Event = (RebindRequest) ? Dhcp6EnterRebinding : Dhcp6EnterRenewing;
+
+ Status = Dhcp6CallbackUser (Instance, Event, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send renew/rebind packet with the state transition from Dhcp6bound to
+ // Dhcp6renew/rebind.
+ // And sync the lease time when send renew/rebind, in case that user send
+ // renew/rebind actively.
+ //
+ Instance->IaCb.Ia->State = State;
+ Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+/**
+ Create the information request message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] InfCb The pointer to the information request control block.
+ @param[in] SendClientId If TRUE, the client identifier option will be included in
+ information request message. Otherwise, the client identifier
+ option will not be included.
+ @param[in] OptionRequest The pointer to the option request option.
+ @param[in] OptionCount The number options in the OptionList.
+ @param[in] OptionList The array pointers to the appended options.
+ @param[in] Retransmission The pointer to the retransmission control.
+
+ @retval EFI_SUCCESS Created and sent the info-request message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the info-request message.
+
+**/
+EFI_STATUS
+Dhcp6SendInfoRequestMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN DHCP6_INF_CB *InfCb,
+ IN BOOLEAN SendClientId,
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
+ IN UINT32 OptionCount,
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[],
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ EFI_DHCP6_DUID *ClientId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT32 UserLen;
+ UINTN Index;
+ UINT16 Length;
+
+ ASSERT(OptionRequest);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ UserLen = NTOHS (OptionRequest->OpLen) + 4;
+
+ ASSERT(ClientId);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ for (Index = 0; Index < OptionCount; Index++) {
+ UserLen += (NTOHS (OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgInfoRequest;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ InfCb->Xid = Packet->Dhcp6.Header.TransactionId;
+
+ //
+ // Assembly Dhcp6 options for info-request message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ if (SendClientId) {
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+ }
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ OptionRequest->OpCode,
+ OptionRequest->OpLen,
+ OptionRequest->Data
+ );
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < OptionCount; Index++) {
+
+ UserOpt = OptionList[Index];
+ Cursor = Dhcp6AppendOption(
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Send info-request packet with no state.
+ //
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, Retransmission);
+}
+
+
+/**
+ Create the Confirm message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Created and sent the confirm message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the confirm message.
+
+**/
+EFI_STATUS
+Dhcp6SendConfirmMsg (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ UINT8 *Cursor;
+ UINTN Index;
+ UINT16 Length;
+ UINT32 UserLen;
+ EFI_STATUS Status;
+ DHCP6_SERVICE *Service;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ UINT16 *Elapsed;
+
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+ ASSERT (Instance->Service != NULL);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ ASSERT (ClientId != NULL);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ UserLen = 0;
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize common fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgConfirm;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for solicit message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendIaOption (
+ Cursor,
+ Instance->IaCb.Ia,
+ Instance->IaCb.T1,
+ Instance->IaCb.T2
+ );
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserOpt = Instance->Config->OptionList[Index];
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendConfirm, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send confirm packet with the state transition from Dhcp6Bound to
+ // Dhcp6Confirming.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Confirming;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+
+/**
+ Handle with the Dhcp6 reply message.
+
+ @param[in] Instance The pointer to Dhcp6 instance.
+ @param[in] Packet The pointer to the Dhcp6 reply message.
+
+ @retval EFI_SUCCESS Processed the reply message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to process the reply message.
+
+**/
+EFI_STATUS
+Dhcp6HandleReplyMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Option;
+ UINT16 StsCode;
+
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+ ASSERT (Packet != NULL);
+
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // If the client subsequently receives a valid reply message that includes a
+ // rapid commit option since send a solicit with rapid commit option before,
+ // preocess the reply message and discard any reply messages received in
+ // response to the request message.
+ // See details in the section-17.1.4 of rfc-3315.
+ //
+ Option = Dhcp6SeekOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptRapidCommit
+ );
+
+ if ((Option != NULL && !Instance->Config->RapidCommit) || (Option == NULL && Instance->Config->RapidCommit)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // As to a valid reply packet in response to a request/renew/rebind packet,
+ // ignore the packet if not contains the Ia option
+ //
+ if (Instance->IaCb.Ia->State == Dhcp6Requesting ||
+ Instance->IaCb.Ia->State == Dhcp6Renewing ||
+ Instance->IaCb.Ia->State == Dhcp6Rebinding
+ ) {
+
+ Option = Dhcp6SeekIaOption (
+ Packet->Dhcp6.Option,
+ Packet->Length,
+ &Instance->Config->IaDescriptor
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Callback to user with the received packet and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdReply, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Dequeue the sent packet from retransmit list since reply received.
+ //
+ Status = Dhcp6DequeueRetry (
+ Instance,
+ Packet->Dhcp6.Header.TransactionId,
+ FALSE
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // When receive a valid reply packet in response to a decline/release packet,
+ // the client considers the decline/release event completed regardless of the
+ // status code.
+ //
+ if (Instance->IaCb.Ia->State == Dhcp6Declining || Instance->IaCb.Ia->State == Dhcp6Releasing) {
+
+ if (Instance->IaCb.Ia->IaAddressCount != 0) {
+ Instance->IaCb.Ia->State = Dhcp6Bound;
+ } else {
+ ASSERT (Instance->IaCb.Ia->ReplyPacket);
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ Instance->IaCb.Ia->ReplyPacket = NULL;
+ Instance->IaCb.Ia->State = Dhcp6Init;
+ }
+
+ //
+ // For sync, set the success flag out of polling in decline/release.
+ //
+ Instance->UdpSts = EFI_SUCCESS;
+
+ //
+ // For async, signal the Ia event to inform Ia infomation update.
+ //
+ if (Instance->Config->IaInfoEvent != NULL) {
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);
+ }
+
+ //
+ // Reset start time for next exchange.
+ //
+ Instance->StartTime = 0;
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Upon the receipt of a valid reply packet in response to a solicit, request,
+ // confirm, renew and rebind, the behavior depends on the status code option.
+ // See the details in the section-18.1.8 of rfc-3315.
+ //
+ Option = NULL;
+ Status = Dhcp6SeekStsOption (
+ Instance,
+ Packet,
+ &Option
+ );
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Reset start time for next exchange.
+ //
+ Instance->StartTime = 0;
+
+ //
+ // No status code or no error status code means succeed to reply.
+ //
+ Status = Dhcp6UpdateIaInfo (Instance, Packet);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Set bound state and store the reply packet.
+ //
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ }
+
+ Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size);
+
+ if (Instance->IaCb.Ia->ReplyPacket == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size);
+
+ Instance->IaCb.Ia->State = Dhcp6Bound;
+
+ //
+ // For sync, set the success flag out of polling in start/renewrebind.
+ //
+ Instance->UdpSts = EFI_SUCCESS;
+
+ //
+ // Maybe this is a new round DHCP process due to some reason, such as NotOnLink
+ // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that
+ // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP
+ // consumers can be notified to flush old address.
+ //
+ Dhcp6AppendCacheIa (Instance);
+
+ //
+ // For async, signal the Ia event to inform Ia infomation update.
+ //
+ if (Instance->Config->IaInfoEvent != NULL) {
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);
+ }
+ } else if (Option != NULL) {
+ //
+ // Any error status code option is found.
+ //
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));
+ switch (StsCode) {
+ case Dhcp6StsUnspecFail:
+ //
+ // It indicates the server is unable to process the message due to an
+ // unspecified failure condition, so just retry if possible.
+ //
+ break;
+
+ case Dhcp6StsUseMulticast:
+ //
+ // It indicates the server receives a message via unicast from a client
+ // to which the server has not sent a unicast option, so retry it by
+ // multi-cast address.
+ //
+ if (Instance->Unicast != NULL) {
+ FreePool (Instance->Unicast);
+ Instance->Unicast = NULL;
+ }
+ break;
+
+ case Dhcp6StsNotOnLink:
+ if (Instance->IaCb.Ia->State == Dhcp6Confirming) {
+ //
+ // Before initiate new round DHCP, cache the current IA.
+ //
+ Status = Dhcp6CacheIa (Instance);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Restart S.A.R.R process to acquire new address.
+ //
+ Status = Dhcp6InitSolicitMsg (Instance);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ break;
+
+ default:
+ //
+ // The other status code, just restart solicitation.
+ //
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Select the appointed Dhcp6 advertisement message.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] AdSelect The pointer to the selected Dhcp6 advertisement message.
+
+ @retval EFI_SUCCESS Selected the right advertisement message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to select the advertise message.
+
+**/
+EFI_STATUS
+Dhcp6SelectAdvertiseMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *AdSelect
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Option;
+
+ ASSERT (AdSelect != NULL);
+
+ //
+ // Callback to user with the selected advertisement packet, and the user
+ // might overwrite it.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SelectAdvertise, &AdSelect);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Instance->AdSelect = AdSelect;
+
+ //
+ // Dequeue the sent packet for the retransmission since advertisement selected.
+ //
+ Status = Dhcp6DequeueRetry (
+ Instance,
+ AdSelect->Dhcp6.Header.TransactionId,
+ FALSE
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Check whether there is server unicast option in the selected advertise
+ // packet, and update it.
+ //
+ Option = Dhcp6SeekOption(
+ AdSelect->Dhcp6.Option,
+ AdSelect->Length - 4,
+ Dhcp6OptServerUnicast
+ );
+
+ if (Option != NULL) {
+
+ Instance->Unicast = AllocateZeroPool (sizeof(EFI_IPv6_ADDRESS));
+
+ if (Instance->Unicast == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (Instance->Unicast, Option + 4, sizeof(EFI_IPv6_ADDRESS));
+ }
+
+ //
+ // Update the information of the Ia by the selected advertisement message.
+ //
+ Status = Dhcp6UpdateIaInfo (Instance, AdSelect);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Send the request message to continue the S.A.R.R. process.
+ //
+ return Dhcp6SendRequestMsg (Instance);
+}
+
+
+/**
+ Handle with the Dhcp6 advertisement message.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to the Dhcp6 advertisement message.
+
+ @retval EFI_SUCCESS Processed the advertisement message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to process the advertise message.
+
+**/
+EFI_STATUS
+Dhcp6HandleAdvertiseMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Option;
+ UINT16 StsCode;
+ BOOLEAN Timeout;
+
+ ASSERT(Instance->Config);
+ ASSERT(Instance->IaCb.Ia);
+
+ Timeout = FALSE;
+ StsCode = Dhcp6StsSuccess;
+
+ //
+ // If the client does receives a valid reply message that includes a rapid
+ // commit option since a solicit with rapid commit optioin sent before, select
+ // this reply message. Or else, process the advertise messages as normal.
+ // See details in the section-17.1.4 of rfc-3315.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptRapidCommit
+ );
+
+ if (Option != NULL && Instance->Config->RapidCommit && Packet->Dhcp6.Header.MessageType == Dhcp6MsgReply) {
+
+ return Dhcp6HandleReplyMsg (Instance, Packet);
+ }
+
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Client must ignore any advertise message that includes a status code option
+ // containing the value noaddrsavail, with the exception that the client may
+ // display the associated status message to the user.
+ // See the details in the section-17.1.3 of rfc-3315.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptStatusCode
+ );
+
+ if (Option != NULL) {
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));
+ if (StsCode != Dhcp6StsSuccess) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Callback to user with the received packet and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdAdvertise, &Packet);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Success means user choose the current advertisement packet.
+ //
+ if (Instance->AdSelect != NULL) {
+ FreePool (Instance->AdSelect);
+ }
+
+ //
+ // Store the selected advertisement packet and set a flag.
+ //
+ Instance->AdSelect = AllocateZeroPool (Packet->Size);
+
+ if (Instance->AdSelect == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (Instance->AdSelect, Packet, Packet->Size);
+
+ Instance->AdPref = 0xff;
+
+ } else if (Status == EFI_NOT_READY) {
+ //
+ // Not_ready means user wants to continue to receive more advertise packets.
+ //
+ if (Instance->AdPref == 0xff && Instance->AdSelect == NULL) {
+ //
+ // It's a tricky point. The timer routine set adpref as 0xff if the first
+ // rt timeout and no advertisement received, which means any advertisement
+ // received will be selected after the first rt.
+ //
+ Timeout = TRUE;
+ }
+
+ //
+ // Check whether the current packet has a 255 preference option or not.
+ // Take non-preference option as 0 value.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptPreference
+ );
+
+ if (Instance->AdSelect == NULL || (Option != NULL && *(Option + 4) > Instance->AdPref)) {
+ //
+ // No advertisements received before or preference is more than other
+ // advertisements received before. Then store the new packet and the
+ // preference value.
+ //
+ if (Instance->AdSelect != NULL) {
+ FreePool (Instance->AdSelect);
+ }
+
+ Instance->AdSelect = AllocateZeroPool (Packet->Size);
+
+ if (Instance->AdSelect == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (Instance->AdSelect, Packet, Packet->Size);
+
+ if (Option != NULL) {
+ Instance->AdPref = *(Option + 4);
+ }
+ } else {
+ //
+ // Non-preference and other advertisements received before or current
+ // preference is less than other advertisements received before.
+ // Leave the packet alone.
+ }
+
+ } else {
+ //
+ // Other error status means termination.
+ //
+ return Status;
+ }
+
+ //
+ // Client must collect advertise messages as more as possible until the first
+ // RT has elapsed, or get a highest preference 255 advertise.
+ // See details in the section-17.1.2 of rfc-3315.
+ //
+ if (Instance->AdPref == 0xff || Timeout) {
+ Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);
+ }
+
+ return Status;
+}
+
+
+/**
+ The Dhcp6 stateful exchange process routine.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to the received Dhcp6 message.
+
+**/
+VOID
+Dhcp6HandleStateful (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_DUID *ClientId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ Status = EFI_SUCCESS;
+
+ if (Instance->InDestory || Instance->Config == NULL) {
+ goto ON_CONTINUE;
+ }
+
+ ASSERT (ClientId);
+ ASSERT (Instance->Config);
+ ASSERT (Instance->IaCb.Ia);
+
+ //
+ // Discard the packet if not advertisement or reply packet.
+ //
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise && Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {
+ goto ON_CONTINUE;
+ }
+
+ //
+ // Check whether include client Id or not.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptClientId
+ );
+
+ if (Option == NULL || CompareMem (Option + 4, ClientId->Duid, ClientId->Length) != 0) {
+ goto ON_CONTINUE;
+ }
+
+ //
+ // Check whether include server Id or not.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptServerId
+ );
+
+ if (Option == NULL) {
+ goto ON_CONTINUE;
+ }
+
+ switch (Instance->IaCb.Ia->State) {
+ case Dhcp6Selecting:
+ //
+ // Handle the advertisement message when in the Dhcp6Selecting state.
+ // Do not need check return status, if failed, just continue to the next.
+ //
+ Dhcp6HandleAdvertiseMsg (Instance, Packet);
+ break;
+
+ case Dhcp6Requesting:
+ case Dhcp6Confirming:
+ case Dhcp6Renewing:
+ case Dhcp6Rebinding:
+ case Dhcp6Releasing:
+ case Dhcp6Declining:
+ //
+ // Handle the reply message when in the Dhcp6Requesting, Dhcp6Renewing
+ // Dhcp6Rebinding, Dhcp6Releasing and Dhcp6Declining state.
+ // If failed here, it should reset the current session.
+ //
+ Status = Dhcp6HandleReplyMsg (Instance, Packet);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ break;
+ default:
+ //
+ // Other state has not supported yet.
+ //
+ break;
+ }
+
+ON_CONTINUE:
+ //
+ // Continue to receive the following Dhcp6 message.
+ //
+ Status = UdpIoRecvDatagram (
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+ON_EXIT:
+ if (EFI_ERROR (Status)) {
+ Dhcp6CleanupSession (Instance, Status);
+ }
+}
+
+
+/**
+ The Dhcp6 stateless exchange process routine.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to the received Dhcp6 message.
+
+**/
+VOID
+Dhcp6HandleStateless (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ DHCP6_SERVICE *Service;
+ DHCP6_INF_CB *InfCb;
+ UINT8 *Option;
+ BOOLEAN IsMatched;
+
+ Service = Instance->Service;
+ Status = EFI_SUCCESS;
+ IsMatched = FALSE;
+ InfCb = NULL;
+
+ if (Instance->InDestory) {
+ goto ON_EXIT;
+ }
+
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether it's a desired Info-request message by Xid.
+ //
+ while (!IsListEmpty (&Instance->InfList)) {
+ InfCb = NET_LIST_HEAD (&Instance->InfList, DHCP6_INF_CB, Link);
+ if (InfCb->Xid == Packet->Dhcp6.Header.TransactionId) {
+ IsMatched = TRUE;
+ break;
+ }
+ }
+
+ if (!IsMatched) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether include server Id or not.
+ //
+ Option = Dhcp6SeekOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptServerId
+ );
+
+ if (Option == NULL) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Callback to user with the received packet and check the user's feedback.
+ //
+ Status = InfCb->ReplyCallback (
+ &Instance->Dhcp6,
+ InfCb->CallbackContext,
+ Packet
+ );
+
+ if (Status == EFI_NOT_READY) {
+ //
+ // Success or aborted will both stop this info-request exchange process,
+ // but not ready means user wants to continue to receive reply.
+ //
+ goto ON_EXIT;
+ }
+
+ //
+ // Dequeue the sent packet from the txlist if the xid matched, and ignore
+ // if no xid matched.
+ //
+ Dhcp6DequeueRetry (
+ Instance,
+ Packet->Dhcp6.Header.TransactionId,
+ FALSE
+ );
+
+ //
+ // For sync, set the status out of polling for info-request.
+ //
+ Instance->UdpSts = Status;
+
+ON_EXIT:
+
+ Status = UdpIoRecvDatagram (
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATELESS);
+ }
+}
+
+
+/**
+ The receive callback function for Dhcp6 exchange process.
+
+ @param[in] Udp6Wrap The pointer to the received net buffer.
+ @param[in] EndPoint The pointer to the udp end point.
+ @param[in] IoStatus The return status from udp io.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6ReceivePacket (
+ IN NET_BUF *Udp6Wrap,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ EFI_DHCP6_HEADER *Head;
+ EFI_DHCP6_PACKET *Packet;
+ DHCP6_SERVICE *Service;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_TX_CB *TxCb;
+ UINT32 Size;
+ BOOLEAN IsDispatched;
+ BOOLEAN IsStateless;
+ LIST_ENTRY *Entry1;
+ LIST_ENTRY *Next1;
+ LIST_ENTRY *Entry2;
+ LIST_ENTRY *Next2;
+
+ ASSERT (Udp6Wrap != NULL);
+ ASSERT (Context != NULL);
+
+ Service = (DHCP6_SERVICE *) Context;
+ Instance = NULL;
+ Packet = NULL;
+ IsDispatched = FALSE;
+ IsStateless = FALSE;
+
+ if (EFI_ERROR (IoStatus)) {
+ return ;
+ }
+
+ //
+ // Copy the net buffer received from upd6 to a Dhcp6 packet.
+ //
+ Size = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize;
+ Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size);
+
+ if (Packet == NULL) {
+ goto ON_CONTINUE;
+ }
+
+ Packet->Size = Size;
+ Head = &Packet->Dhcp6.Header;
+ Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head);
+
+ if (Packet->Length == 0) {
+ goto ON_CONTINUE;
+ }
+
+ //
+ // Dispatch packet to right instance by transaction id.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {
+
+ Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);
+
+ NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link);
+
+ if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) {
+ //
+ // Find the corresponding packet in tx list, and check it whether belongs
+ // to stateful exchange process.
+ //
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
+ IsStateless = TRUE;
+ }
+ IsDispatched = TRUE;
+ break;
+ }
+ }
+
+ if (IsDispatched) {
+ break;
+ }
+ }
+
+ //
+ // Skip this packet if not dispatched to any instance.
+ //
+ if (!IsDispatched) {
+ goto ON_CONTINUE;
+ }
+
+ //
+ // Dispatch the received packet ot the right instance.
+ //
+ if (IsStateless) {
+ Dhcp6HandleStateless (Instance, Packet);
+ } else {
+ Dhcp6HandleStateful (Instance, Packet);
+ }
+
+ON_CONTINUE:
+
+ NetbufFree (Udp6Wrap);
+
+ if (Packet != NULL) {
+ FreePool (Packet);
+ }
+}
+
+/**
+ Detect Link movement for specified network device.
+
+ This routine will try to invoke Snp->GetStatus() to get the media status.
+ If media present status switches from unpresent to present, a link movement
+ is detected. Note that the underlying UNDI driver may not support reporting
+ media status from GET_STATUS command. If that, fail to detect link movement.
+
+ @param[in] Instance The pointer to DHCP6_INSTANCE.
+
+ @retval TRUE A link movement is detected.
+ @retval FALSE A link movement is not detected.
+
+**/
+BOOLEAN
+Dhcp6LinkMovDetect (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ UINT32 InterruptStatus;
+ BOOLEAN MediaPresent;
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+
+ ASSERT (Instance != NULL);
+ Snp = Instance->Service->Snp;
+ MediaPresent = Instance->MediaPresent;
+
+ //
+ // Check whether SNP support media detection
+ //
+ if (!Snp->Mode->MediaPresentSupported) {
+ return FALSE;
+ }
+
+ //
+ // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data
+ //
+ Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ Instance->MediaPresent = Snp->Mode->MediaPresent;
+ //
+ // Media transimit Unpresent to Present means new link movement is detected.
+ //
+ if (!MediaPresent && Instance->MediaPresent) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/**
+ The timer routine of the Dhcp6 instance for each second.
+
+ @param[in] Event The timer event.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_TX_CB *TxCb;
+ DHCP6_IA_CB *IaCb;
+ UINT32 LossTime;
+
+ ASSERT (Context != NULL);
+
+ Instance = (DHCP6_INSTANCE *) Context;
+
+ //
+ // 1. Loop the tx list, count live time of every tx packet to check whether
+ // need re-transmit or not.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
+
+ TxCb->TickTime++;
+
+ if (TxCb->TickTime > TxCb->RetryExp) {
+ //
+ // Handle the first rt in the transmission of solicit specially.
+ //
+ if (TxCb->RetryCnt == 0 && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {
+ if (Instance->AdSelect == NULL) {
+ //
+ // Set adpref as 0xff here to indicate select any advertisement
+ // afterwards.
+ //
+ Instance->AdPref = 0xff;
+ } else {
+ //
+ // Select the advertisement received before.
+ //
+ Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);
+ return;
+ }
+ }
+ //
+ // Increase the retry count for the packet and add up the total loss time.
+ //
+ TxCb->RetryCnt++;
+ TxCb->RetryLos += TxCb->RetryExp;
+
+ //
+ // Check whether overflow the max retry count limit for this packet
+ //
+ if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) {
+ goto ON_CLOSE;
+ }
+
+ //
+ // Check whether overflow the max retry duration for this packet
+ //
+ if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) {
+ goto ON_CLOSE;
+ }
+
+ //
+ // Re-calculate retry expire timeout for the next time.
+ //
+ // Firstly, Check the new calculated time whether overflow the max retry
+ // expire time.
+ //
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryExp,
+ FALSE,
+ TRUE
+ );
+
+ if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) {
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Mrt,
+ TRUE,
+ TRUE
+ );
+ }
+
+ //
+ // Secondly, Check the new calculated time whether overflow the max retry
+ // duration time.
+ //
+ LossTime = TxCb->RetryLos + TxCb->RetryExp;
+ if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) {
+ TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos;
+ }
+
+ //
+ // Reset the tick time for the next retransmission
+ //
+ TxCb->TickTime = 0;
+
+ //
+ // Retransmit the last sent packet again.
+ //
+ Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed);
+ }
+ }
+
+ //
+ // 2. Check the configured Ia, count lease time of every valid Ia to check
+ // whether need to renew or rebind this Ia.
+ //
+ IaCb = &Instance->IaCb;
+
+ if (Instance->Config == NULL || IaCb->Ia == NULL) {
+ return;
+ }
+
+ if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) {
+
+ IaCb->LeaseTime++;
+
+ if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) {
+ //
+ // Exceed t2, send rebind packet to extend the Ia lease.
+ //
+ Dhcp6SendRenewRebindMsg (Instance, TRUE);
+
+ } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) {
+
+ //
+ // Exceed t1, send renew packet to extend the Ia lease.
+ //
+ Dhcp6SendRenewRebindMsg (Instance, FALSE);
+ }
+ }
+
+ //
+ // 3. In any situation when a client may have moved to a new link, the
+ // client MUST initiate a Confirm/Reply message exchange.
+ //
+ if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) {
+ Dhcp6SendConfirmMsg (Instance);
+ }
+
+ return;
+
+ ON_CLOSE:
+
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest ||
+ TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew ||
+ TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm
+ ) {
+ //
+ // The failure of renew/Confirm will still switch to the bound state.
+ //
+ if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) ||
+ (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) {
+ ASSERT (Instance->IaCb.Ia);
+ Instance->IaCb.Ia->State = Dhcp6Bound;
+ }
+ //
+ // The failure of info-request will return no response.
+ //
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
+ Instance->UdpSts = EFI_NO_RESPONSE;
+ }
+ Dhcp6DequeueRetry (
+ Instance,
+ TxCb->Xid,
+ TRUE
+ );
+ } else {
+ //
+ // The failure of the others will terminate current state machine if timeout.
+ //
+ Dhcp6CleanupSession (Instance, EFI_NO_RESPONSE);
+ }
+}
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h
new file mode 100644
index 0000000000..31459c96d3
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h
@@ -0,0 +1,193 @@
+/** @file
+ Dhcp6 internal functions declaration.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP6_IO_H__
+#define __EFI_DHCP6_IO_H__
+
+
+/**
+ Clean up the specific nodes in the retry list.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Scope The scope of cleanup nodes.
+
+**/
+VOID
+Dhcp6CleanupRetry (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT32 Scope
+ );
+
+/**
+ Clean up the session of the instance stateful exchange.
+
+ @param[in, out] Instance The pointer to the Dhcp6 instance.
+ @param[in] Status The return status from udp.
+
+**/
+VOID
+Dhcp6CleanupSession (
+ IN OUT DHCP6_INSTANCE *Instance,
+ IN EFI_STATUS Status
+ );
+
+/**
+ Create the solicit message and send it.
+
+ @param[in] Instance The pointer to Dhcp6 instance.
+
+ @retval EFI_SUCCESS Create and send the solicit message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the solicit message.
+
+**/
+EFI_STATUS
+Dhcp6SendSolicitMsg (
+ IN DHCP6_INSTANCE *Instance
+ );
+
+/**
+ Create the request message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Create and send the request message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the request message.
+
+**/
+EFI_STATUS
+Dhcp6SendRequestMsg (
+ IN DHCP6_INSTANCE *Instance
+ );
+
+/**
+ Create the renew/rebind message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] RebindRequest If TRUE, it is a Rebind type message.
+ Otherwise, it is a Renew type message.
+
+ @retval EFI_SUCCESS Create and send the renew/rebind message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the renew/rebind message.
+
+**/
+EFI_STATUS
+Dhcp6SendRenewRebindMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN BOOLEAN RebindRequest
+ );
+
+/**
+ Create the decline message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] DecIa The pointer to the decline Ia.
+
+ @retval EFI_SUCCESS Create and send the decline message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the decline message.
+
+**/
+EFI_STATUS
+Dhcp6SendDeclineMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_IA *DecIa
+ );
+
+/**
+ Create the release message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] RelIa The pointer to the release Ia.
+
+ @retval EFI_SUCCESS Create and send the release message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the release message.
+
+**/
+EFI_STATUS
+Dhcp6SendReleaseMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_IA *RelIa
+ );
+
+/**
+ Create the information request message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] InfCb The pointer to the information request control block.
+ @param[in] SendClientId If TRUE, the client identifier option will be included in
+ information request message. Otherwise, the client identifier
+ option will not be included.
+ @param[in] OptionRequest The pointer to the option request option.
+ @param[in] OptionCount The number options in the OptionList.
+ @param[in] OptionList The array pointers to the appended options.
+ @param[in] Retransmission The pointer to the retransmission control.
+
+ @retval EFI_SUCCESS Create and send the info-request message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the info-request message.
+
+**/
+EFI_STATUS
+Dhcp6SendInfoRequestMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN DHCP6_INF_CB *InfCb,
+ IN BOOLEAN SendClientId,
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
+ IN UINT32 OptionCount,
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[],
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission
+ );
+
+/**
+ The receive callback function for the Dhcp6 exchange process.
+
+ @param[in] Udp6Wrap The pointer to the received net buffer.
+ @param[in] EndPoint The pointer to the udp end point.
+ @param[in] IoStatus The return status from udp io.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6ReceivePacket (
+ IN NET_BUF *Udp6Wrap,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+/**
+ The timer routine of the Dhcp6 instance for each second.
+
+ @param[in] Event The timer event.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+#endif
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c
new file mode 100644
index 0000000000..be7a985551
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c
@@ -0,0 +1,1146 @@
+/** @file
+ Dhcp6 support functions implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+
+/**
+ Generate client Duid in the format of Duid-llt.
+
+ @param[in] Mode The pointer to the mode of SNP.
+
+ @retval NULL If it failed to generate a client Id.
+ @retval others The pointer to the new client id.
+
+**/
+EFI_DHCP6_DUID *
+Dhcp6GenerateClientId (
+ IN EFI_SIMPLE_NETWORK_MODE *Mode
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_DUID *Duid;
+ EFI_TIME Time;
+ UINT32 Stamp;
+
+ //
+ // Attempt to get client Id from variable to keep it constant.
+ // See details in section-9 of rfc-3315.
+ //
+ Duid = GetVariable (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid);
+ if (Duid != NULL) {
+ return Duid;
+ }
+
+ //
+ // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month.
+ //
+ gRT->GetTime (&Time, NULL);
+ Stamp = (UINT32)
+ (
+ (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) *
+ 60 +
+ Time.Second
+ );
+
+ //
+ // The format of client identifier option:
+ //
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_CLIENTID | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // . .
+ // . DUID .
+ // . (variable length) .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+ //
+ // The format of DUID-LLT:
+ //
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Duid type (1) | hardware type (16 bits) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | time (32 bits) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // . .
+ // . link-layer address (variable length) .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes
+ //
+ Duid = AllocateZeroPool (10 + Mode->HwAddressSize);
+ if (Duid == NULL) {
+ return NULL;
+ }
+
+ //
+ // sizeof (Duid-type + hardware-type + time) = 8 bytes
+ //
+ Duid->Length = (UINT16) (Mode->HwAddressSize + 8);
+
+ //
+ // Set the Duid-type, hardware-type, time and copy the hardware address.
+ //
+ WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeLlt));
+ WriteUnaligned16 ((UINT16 *) (Duid->Duid + 2), HTONS (NET_IFTYPE_ETHERNET));
+ WriteUnaligned32 ((UINT32 *) (Duid->Duid + 4), HTONL (Stamp));
+
+ CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize);
+
+ Status = gRT->SetVariable (
+ L"ClientId",
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ Duid->Length + 2,
+ (VOID *) Duid
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Duid;
+}
+
+
+/**
+ Copy the Dhcp6 configure data.
+
+ @param[in] DstCfg The pointer to the destination configure data.
+ @param[in] SorCfg The pointer to the source configure data.
+
+ @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6CopyConfigData (
+ IN EFI_DHCP6_CONFIG_DATA *DstCfg,
+ IN EFI_DHCP6_CONFIG_DATA *SorCfg
+ )
+{
+ UINTN Index;
+ UINTN OptionListSize;
+ UINTN OptionSize;
+
+ CopyMem (DstCfg, SorCfg, sizeof (EFI_DHCP6_CONFIG_DATA));
+
+ //
+ // Allocate another buffer for solicitretransmission, and copy it.
+ //
+ if (SorCfg->SolicitRetransmission != NULL) {
+
+ DstCfg->SolicitRetransmission = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
+
+ if (DstCfg->SolicitRetransmission == NULL) {
+ //
+ // Error will be handled out of this function.
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (
+ DstCfg->SolicitRetransmission,
+ SorCfg->SolicitRetransmission,
+ sizeof (EFI_DHCP6_RETRANSMISSION)
+ );
+ }
+
+ if (SorCfg->OptionList != NULL && SorCfg->OptionCount != 0) {
+
+ OptionListSize = SorCfg->OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *);
+ DstCfg->OptionList = AllocateZeroPool (OptionListSize);
+
+ if (DstCfg->OptionList == NULL) {
+ //
+ // Error will be handled out of this function.
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < SorCfg->OptionCount; Index++) {
+
+ OptionSize = NTOHS (SorCfg->OptionList[Index]->OpLen) + 4;
+ DstCfg->OptionList[Index] = AllocateZeroPool (OptionSize);
+
+ if (DstCfg->OptionList[Index] == NULL) {
+ //
+ // Error will be handled out of this function.
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (
+ DstCfg->OptionList[Index],
+ SorCfg->OptionList[Index],
+ OptionSize
+ );
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Clean up the configure data.
+
+ @param[in, out] CfgData The pointer to the configure data.
+
+**/
+VOID
+Dhcp6CleanupConfigData (
+ IN OUT EFI_DHCP6_CONFIG_DATA *CfgData
+ )
+{
+ UINTN Index;
+
+ ASSERT (CfgData != NULL);
+ //
+ // Clean up all fields in config data including the reference buffers, but do
+ // not free the config data buffer itself.
+ //
+ if (CfgData->OptionList != NULL) {
+ for (Index = 0; Index < CfgData->OptionCount; Index++) {
+ if (CfgData->OptionList[Index] != NULL) {
+ FreePool (CfgData->OptionList[Index]);
+ }
+ }
+ FreePool (CfgData->OptionList);
+ }
+
+ if (CfgData->SolicitRetransmission != NULL) {
+ FreePool (CfgData->SolicitRetransmission);
+ }
+
+ ZeroMem (CfgData, sizeof (EFI_DHCP6_CONFIG_DATA));
+}
+
+
+/**
+ Clean up the mode data.
+
+ @param[in, out] ModeData The pointer to the mode data.
+
+**/
+VOID
+Dhcp6CleanupModeData (
+ IN OUT EFI_DHCP6_MODE_DATA *ModeData
+ )
+{
+ ASSERT (ModeData != NULL);
+ //
+ // Clean up all fields in mode data including the reference buffers, but do
+ // not free the mode data buffer itself.
+ //
+ if (ModeData->ClientId != NULL) {
+ FreePool (ModeData->ClientId);
+ }
+
+ if (ModeData->Ia != NULL) {
+
+ if (ModeData->Ia->ReplyPacket != NULL) {
+ FreePool (ModeData->Ia->ReplyPacket);
+ }
+ FreePool (ModeData->Ia);
+ }
+
+ ZeroMem (ModeData, sizeof (EFI_DHCP6_MODE_DATA));
+}
+
+
+/**
+ Calculate the expire time by the algorithm defined in rfc.
+
+ @param[in] Base The base value of the time.
+ @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time.
+ @param[in] NeedSigned If TRUE, the the signed factor is needed.
+
+ @return Expire The calculated result for the new expire time.
+
+**/
+UINT32
+Dhcp6CalculateExpireTime (
+ IN UINT32 Base,
+ IN BOOLEAN IsFirstRt,
+ IN BOOLEAN NeedSigned
+ )
+{
+ EFI_TIME Time;
+ BOOLEAN Signed;
+ UINT32 Seed;
+ UINT32 Expire;
+
+ //
+ // Take the 10bits of microsecond in system time as a uniform distribution.
+ // Take the 10th bit as a flag to determine it's signed or not.
+ //
+ gRT->GetTime (&Time, NULL);
+ Seed = ((Time.Nanosecond >> 10) & DHCP6_10_BIT_MASK);
+ Signed = (BOOLEAN) ((((Time.Nanosecond >> 9) & 0x01) != 0) ? TRUE : FALSE);
+ Signed = (BOOLEAN) (NeedSigned ? Signed : FALSE);
+
+ //
+ // Calculate expire by the following algo:
+ // 1. base + base * (-0.1 ~ 0) for the first solicit
+ // 2. base + base * (-0.1 ~ 0.1) for the first other messages
+ // 3. 2 * base + base * (-0.1 ~ 0.1) for the subsequent all messages
+ // 4. base + base * (-0.1 ~ 0) for the more than mrt timeout
+ //
+ // The (Seed / 0x3ff / 10) is used to a random range (0, 0.1).
+ //
+ if (IsFirstRt && Signed) {
+
+ Expire = Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
+
+ } else if (IsFirstRt && !Signed) {
+
+ Expire = Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
+
+ } else if (!IsFirstRt && Signed) {
+
+ Expire = 2 * Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
+
+ } else {
+
+ Expire = 2 * Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
+ }
+
+ Expire = (Expire != 0) ? Expire : 1;
+
+ return Expire;
+}
+
+
+/**
+ Calculate the lease time by the algorithm defined in rfc.
+
+ @param[in] IaCb The pointer to the Ia control block.
+
+**/
+VOID
+Dhcp6CalculateLeaseTime (
+ IN DHCP6_IA_CB *IaCb
+ )
+{
+ EFI_DHCP6_IA_ADDRESS *IaAddr;
+ UINT32 MinLt;
+ UINT32 MaxLt;
+ UINTN Index;
+
+ ASSERT (IaCb->Ia->IaAddressCount > 0);
+
+ MinLt = (UINT32) (-1);
+ MaxLt = 0;
+
+ //
+ // Calculate minlt as min of all valid life time, and maxlt as max of all
+ // valid life time.
+ //
+ for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) {
+ IaAddr = IaCb->Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);
+ MinLt = MIN (MinLt, IaAddr->ValidLifetime);
+ MaxLt = MAX (MinLt, IaAddr->ValidLifetime);
+ }
+
+ //
+ // Take 50% minlt as t1, and 80% maxlt as t2 if Dhcp6 server doesn't offer
+ // such information.
+ //
+ IaCb->T1 = (IaCb->T1 != 0) ? IaCb->T1 : (UINT32)(MinLt * 5 / 10);
+ IaCb->T2 = (IaCb->T2 != 0) ? IaCb->T2 : (UINT32)(MinLt * 8 / 10);
+ IaCb->AllExpireTime = MaxLt;
+ IaCb->LeaseTime = 0;
+}
+
+
+/**
+ Check whether the addresses are all included by the configured Ia.
+
+ @param[in] Ia The pointer to the Ia.
+ @param[in] AddressCount The number of addresses.
+ @param[in] Addresses The pointer to the addresses buffer.
+
+ @retval EFI_SUCCESS The addresses are all included by the configured IA.
+ @retval EFI_NOT_FOUND The addresses are not included by the configured IA.
+
+**/
+EFI_STATUS
+Dhcp6CheckAddress (
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ )
+{
+ UINTN Index1;
+ UINTN Index2;
+ BOOLEAN Found;
+
+ //
+ // Check whether the addresses are all included by the configured IA. And it
+ // will return success if address count is zero, which means all addresses.
+ //
+ for (Index1 = 0; Index1 < AddressCount; Index1++) {
+
+ Found = FALSE;
+
+ for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {
+
+ if (CompareMem (
+ &Addresses[Index1],
+ &Ia->IaAddress[Index2],
+ sizeof (EFI_IPv6_ADDRESS)
+ ) == 0) {
+
+ Found = TRUE;
+ break;
+ }
+ }
+
+ if (!Found) {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Deprive the addresses from current Ia, and generate another eliminated Ia.
+
+ @param[in] Ia The pointer to the Ia.
+ @param[in] AddressCount The number of addresses.
+ @param[in] Addresses The pointer to the addresses buffer.
+
+ @retval NULL If it failed to generate the deprived Ia.
+ @retval others The pointer to the deprived Ia.
+
+**/
+EFI_DHCP6_IA *
+Dhcp6DepriveAddress (
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ )
+{
+ EFI_DHCP6_IA *IaCopy;
+ UINTN IaCopySize;
+ UINTN Index1;
+ UINTN Index2;
+ BOOLEAN Found;
+
+ if (AddressCount == 0) {
+ //
+ // It means release all Ia addresses if address count is zero.
+ //
+ AddressCount = Ia->IaAddressCount;
+ }
+
+ ASSERT (AddressCount != 0);
+
+ IaCopySize = sizeof (EFI_DHCP6_IA) + (AddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+ IaCopy = AllocateZeroPool (IaCopySize);
+
+ if (IaCopy == NULL) {
+ return NULL;
+ }
+
+ if (AddressCount == Ia->IaAddressCount) {
+ //
+ // If release all Ia addresses, just copy the configured Ia and then set
+ // its address count as zero.
+ // We may decline/release part of addresses at the begining. So it's a
+ // forwarding step to update address infor for decline/release, while the
+ // other infor such as Ia state will be updated when receiving reply.
+ //
+ CopyMem (IaCopy, Ia, IaCopySize);
+ Ia->IaAddressCount = 0;
+ return IaCopy;
+ }
+
+ CopyMem (IaCopy, Ia, sizeof (EFI_DHCP6_IA));
+
+ //
+ // Move the addresses from the Ia of instance to the deprived Ia.
+ //
+ for (Index1 = 0; Index1 < AddressCount; Index1++) {
+
+ Found = FALSE;
+
+ for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {
+
+ if (CompareMem (
+ &Addresses[Index1],
+ &Ia->IaAddress[Index2],
+ sizeof (EFI_IPv6_ADDRESS)
+ ) == 0) {
+ //
+ // Copy the deprived address to the copy of Ia
+ //
+ CopyMem (
+ &IaCopy->IaAddress[Index1],
+ &Ia->IaAddress[Index2],
+ sizeof (EFI_DHCP6_IA_ADDRESS)
+ );
+ //
+ // Delete the deprived address from the instance Ia
+ //
+ if (Index2 + 1 < Ia->IaAddressCount) {
+ CopyMem (
+ &Ia->IaAddress[Index2],
+ &Ia->IaAddress[Index2 + 1],
+ (Ia->IaAddressCount - Index2 - 1) * sizeof (EFI_DHCP6_IA_ADDRESS)
+ );
+ }
+ Found = TRUE;
+ break;
+ }
+ }
+ ASSERT (Found == TRUE);
+ }
+
+ Ia->IaAddressCount -= AddressCount;
+ IaCopy->IaAddressCount = AddressCount;
+
+ return IaCopy;
+}
+
+
+/**
+ The dummy ext buffer free callback routine.
+
+ @param[in] Arg The pointer to the parameter.
+
+**/
+VOID
+EFIAPI
+Dhcp6DummyExtFree (
+ IN VOID *Arg
+ )
+{
+}
+
+
+/**
+ The callback routine once message transmitted.
+
+ @param[in] Udp6Wrap The pointer to the received net buffer.
+ @param[in] EndPoint The pointer to the udp end point.
+ @param[in] IoStatus The return status from udp io.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6OnTransmitted (
+ IN NET_BUF *Wrap,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ NetbufFree (Wrap);
+}
+
+
+/**
+ Append the option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the buffer.
+ @param[in] OptType The option type.
+ @param[in] OptLen The length of option contents.
+ @param[in] Data The pointer to the option content.
+
+ @return Buf The position to append the next option.
+
+**/
+UINT8 *
+Dhcp6AppendOption (
+ IN OUT UINT8 *Buf,
+ IN UINT16 OptType,
+ IN UINT16 OptLen,
+ IN UINT8 *Data
+ )
+{
+ //
+ // The format of Dhcp6 option:
+ //
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | option-code | option-len (option data) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | option-data |
+ // | (option-len octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ ASSERT (OptLen != 0);
+
+ WriteUnaligned16 ((UINT16 *) Buf, OptType);
+ Buf += 2;
+ WriteUnaligned16 ((UINT16 *) Buf, OptLen);
+ Buf += 2;
+ CopyMem (Buf, Data, NTOHS (OptLen));
+ Buf += NTOHS (OptLen);
+
+ return Buf;
+}
+
+
+/**
+ Append the appointed Ia option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the position to append.
+ @param[in] Ia The pointer to the Ia.
+ @param[in] T1 The time of T1.
+ @param[in] T2 The time of T2.
+
+ @return Buf The position to append the next Ia option.
+
+**/
+UINT8 *
+Dhcp6AppendIaOption (
+ IN OUT UINT8 *Buf,
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 T1,
+ IN UINT32 T2
+ )
+{
+ UINT8 *AddrOpt;
+ UINT16 *Len;
+ UINTN Index;
+ UINT16 Length;
+
+ //
+ // The format of IA_NA and IA_TA option:
+ //
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_NA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T1 (only for IA_NA) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T2 (only for IA_NA) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_NA-options/IA_TA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // Fill the value of Ia option type
+ //
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS (Ia->Descriptor.Type));
+ Buf += 2;
+
+ //
+ // Fill the len of Ia option later, keep the pointer first
+ //
+ Len = (UINT16 *) Buf;
+ Buf += 2;
+
+ //
+ // Fill the value of iaid
+ //
+ WriteUnaligned32 ((UINT32 *) Buf, HTONL (Ia->Descriptor.IaId));
+ Buf += 4;
+
+ //
+ // Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified.
+ //
+ if (Ia->Descriptor.Type == Dhcp6OptIana) {
+ WriteUnaligned32 ((UINT32 *) Buf, ((T1 != 0) ? T1 : 0xffffffff));
+ Buf += 4;
+ WriteUnaligned32 ((UINT32 *) Buf, ((T2 != 0) ? T2 : 0xffffffff));
+ Buf += 4;
+ }
+
+ //
+ // Fill all the addresses belong to the Ia
+ //
+ for (Index = 0; Index < Ia->IaAddressCount; Index++) {
+
+ AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);
+ Length = HTONS ((UINT16) sizeof (EFI_DHCP6_IA_ADDRESS));
+ Buf = Dhcp6AppendOption (
+ Buf,
+ HTONS (Dhcp6OptIaAddr),
+ Length,
+ AddrOpt
+ );
+ }
+
+ //
+ // Fill the value of Ia option length
+ //
+ *Len = HTONS ((UINT16) (Buf - (UINT8 *) Len - 2));
+
+ return Buf;
+}
+
+/**
+ Append the appointed Elapsed time option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[out] Elapsed The pointer to the elapsed time value in
+ the generated packet.
+
+ @return Buf The position to append the next Ia option.
+
+**/
+UINT8 *
+Dhcp6AppendETOption (
+ IN OUT UINT8 *Buf,
+ IN DHCP6_INSTANCE *Instance,
+ OUT UINT16 **Elapsed
+ )
+{
+ //
+ // The format of elapsed time option:
+ //
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_ELAPSED_TIME | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | elapsed-time |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // Fill the value of elapsed-time option type.
+ //
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptElapsedTime));
+ Buf += 2;
+
+ //
+ // Fill the len of elapsed-time option, which is fixed.
+ //
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS(2));
+ Buf += 2;
+
+ //
+ // Fill in elapsed time value with 0 value for now. The actual value is
+ // filled in later just before the packet is transmitted.
+ //
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS(0));
+ *Elapsed = (UINT16 *) Buf;
+ Buf += 2;
+
+ return Buf;
+}
+
+/**
+ Set the elapsed time based on the given instance and the pointer to the
+ elapsed time option.
+
+ @param[in] Elapsed The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+**/
+VOID
+SetElapsedTime (
+ IN UINT16 *Elapsed,
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ EFI_TIME Time;
+ UINT64 CurrentStamp;
+ UINT64 ElapsedTimeValue;
+
+ //
+ // Generate a time stamp of the centiseconds from 2000/1/1, assume 30day/month.
+ //
+ gRT->GetTime (&Time, NULL);
+ CurrentStamp = (UINT64)
+ (
+ ((((((Time.Year - 2000) * 360 +
+ (Time.Month - 1)) * 30 +
+ (Time.Day - 1)) * 24 + Time.Hour) * 60 +
+ Time.Minute) * 60 + Time.Second) * 100
+ + DivU64x32(Time.Nanosecond, 10000000)
+ );
+
+ //
+ // Sentinel value of 0 means that this is the first DHCP packet that we are
+ // sending and that we need to initialize the value. First DHCP Solicit
+ // gets 0 elapsed-time. Otherwise, calculate based on StartTime.
+ //
+ if (Instance->StartTime == 0) {
+ ElapsedTimeValue = 0;
+ Instance->StartTime = CurrentStamp;
+ } else {
+ ElapsedTimeValue = CurrentStamp - Instance->StartTime;
+
+ //
+ // If elapsed time cannot fit in two bytes, set it to 0xffff.
+ //
+ if (ElapsedTimeValue > 0xffff) {
+ ElapsedTimeValue = 0xffff;
+ }
+ }
+ WriteUnaligned16 (Elapsed, HTONS((UINT16) ElapsedTimeValue));
+}
+
+
+/**
+ Seek the address of the first byte of the option header.
+
+ @param[in] Buf The pointer to the buffer.
+ @param[in] SeekLen The length to seek.
+ @param[in] OptType The option type.
+
+ @retval NULL If it failed to seek the option.
+ @retval others The position to the option.
+
+**/
+UINT8 *
+Dhcp6SeekOption (
+ IN UINT8 *Buf,
+ IN UINT32 SeekLen,
+ IN UINT16 OptType
+ )
+{
+ UINT8 *Cursor;
+ UINT8 *Option;
+ UINT16 DataLen;
+ UINT16 OpCode;
+
+ Option = NULL;
+ Cursor = Buf;
+
+ //
+ // The format of Dhcp6 option refers to Dhcp6AppendOption().
+ //
+ while (Cursor < Buf + SeekLen) {
+ OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
+ if (OpCode == HTONS (OptType)) {
+ Option = Cursor;
+ break;
+ }
+ DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
+ Cursor += (DataLen + 4);
+ }
+
+ return Option;
+}
+
+
+/**
+ Seek the address of the first byte of the Ia option header.
+
+ @param[in] Buf The pointer to the buffer.
+ @param[in] SeekLen The length to seek.
+ @param[in] IaDesc The pointer to the Ia descriptor.
+
+ @retval NULL If it failed to seek the Ia option.
+ @retval others The position to the Ia option.
+
+**/
+UINT8 *
+Dhcp6SeekIaOption (
+ IN UINT8 *Buf,
+ IN UINT32 SeekLen,
+ IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc
+ )
+{
+ UINT8 *Cursor;
+ UINT8 *Option;
+ UINT16 DataLen;
+ UINT16 OpCode;
+ UINT32 IaId;
+
+ //
+ // The format of IA_NA and IA_TA option refers to Dhcp6AppendIaOption().
+ //
+ Option = NULL;
+ Cursor = Buf;
+
+ while (Cursor < Buf + SeekLen) {
+ OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
+ IaId = ReadUnaligned32 ((UINT32 *) (Cursor + 4));
+ if (OpCode == HTONS (IaDesc->Type) && IaId == HTONL (IaDesc->IaId)) {
+ Option = Cursor;
+ break;
+ }
+ DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
+ Cursor += (DataLen + 4);
+ }
+
+ return Option;
+}
+
+
+/**
+ Parse the address option and update the address infomation.
+
+ @param[in] IaInnerOpt The pointer to the buffer.
+ @param[in] IaInnerLen The length to parse.
+ @param[out] AddrNum The number of addresses.
+ @param[in, out] AddrBuf The pointer to the address buffer.
+
+**/
+VOID
+Dhcp6ParseAddrOption (
+ IN UINT8 *IaInnerOpt,
+ IN UINT16 IaInnerLen,
+ OUT UINT32 *AddrNum,
+ IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf
+ )
+{
+ UINT8 *Cursor;
+ UINT16 DataLen;
+ UINT16 OpCode;
+ UINT32 ValidLt;
+
+ //
+ // The format of the IA Address option:
+ //
+ // 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
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IAADDR | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // | IPv6 address |
+ // | |
+ // | |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | preferred-lifetime |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | valid-lifetime |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // . .
+ // . IAaddr-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // Two usage model:
+ //
+ // 1. Pass addrbuf == null, to get the addrnum over the Ia inner options.
+ // 2. Pass addrbuf != null, to resolve the addresses over the Ia inner
+ // options to the addrbuf.
+ //
+
+ Cursor = IaInnerOpt;
+ *AddrNum = 0;
+
+ while (Cursor < IaInnerOpt + IaInnerLen) {
+ //
+ // Count the Ia address option with non-0 valid time.
+ //
+ OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
+ ValidLt = ReadUnaligned32 ((UINT32 *) (Cursor + 24));
+ if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt != 0) {
+
+ if (AddrBuf != NULL) {
+ CopyMem (AddrBuf, Cursor + 4, sizeof (EFI_DHCP6_IA_ADDRESS));
+ AddrBuf->PreferredLifetime = NTOHL (AddrBuf->PreferredLifetime);
+ AddrBuf->ValidLifetime = NTOHL (AddrBuf->ValidLifetime);
+ AddrBuf = (EFI_DHCP6_IA_ADDRESS *) ((UINT8 *) AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS));
+ }
+
+ (*AddrNum)++;
+ }
+ DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
+ Cursor += (DataLen + 4);
+ }
+}
+
+
+/**
+ Create a control blcok for the Ia according to the corresponding options.
+
+ @param[in] Instance The pointer to DHCP6 Instance.
+ @param[in] IaInnerOpt The pointer to the inner options in the Ia option.
+ @param[in] IaInnerLen The length of all the inner options in the Ia option.
+ @param[in] T1 T1 time in the Ia option.
+ @param[in] T2 T2 time in the Ia option.
+
+ @retval EFI_NOT_FOUND No valid IA option is found.
+ @retval EFI_SUCCESS Create an IA control block successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6GenerateIaCb (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT8 *IaInnerOpt,
+ IN UINT16 IaInnerLen,
+ IN UINT32 T1,
+ IN UINT32 T2
+ )
+{
+ UINT32 AddrNum;
+ UINT32 IaSize;
+ EFI_DHCP6_IA *Ia;
+
+ if (Instance->IaCb.Ia == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Calculate the number of addresses for this Ia, excluding the addresses with
+ // the value 0 of valid lifetime.
+ //
+ Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, NULL);
+
+ if (AddrNum == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Allocate for new IA.
+ //
+ IaSize = sizeof (EFI_DHCP6_IA) + (AddrNum - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+ Ia = AllocateZeroPool (IaSize);
+
+ if (Ia == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Fill up this new IA fields.
+ //
+ Ia->State = Instance->IaCb.Ia->State;
+ Ia->IaAddressCount = AddrNum;
+ CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR));
+ Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress);
+
+ //
+ // Free original IA resource.
+ //
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ }
+ FreePool (Instance->IaCb.Ia);
+
+
+ ZeroMem (&Instance->IaCb, sizeof (DHCP6_IA_CB));
+
+ //
+ // Update IaCb to use new IA.
+ //
+ Instance->IaCb.Ia = Ia;
+
+ //
+
+ // Fill in IaCb fields. Such as T1, T2, AllExpireTime and LeaseTime.
+ //
+ Instance->IaCb.T1 = T1;
+ Instance->IaCb.T2 = T2;
+ Dhcp6CalculateLeaseTime (&Instance->IaCb);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cache the current IA configuration information.
+
+ @param[in] Instance The pointer to DHCP6 Instance.
+
+ @retval EFI_SUCCESS Cache the current IA successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6CacheIa (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ UINTN IaSize;
+ EFI_DHCP6_IA *Ia;
+
+ Ia = Instance->IaCb.Ia;
+
+ if ((Instance->CacheIa == NULL) && (Ia != NULL)) {
+ //
+ // Cache the current IA.
+ //
+ IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+
+ Instance->CacheIa = AllocateZeroPool (IaSize);
+ if (Instance->CacheIa == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (Instance->CacheIa, Ia, IaSize);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.
+
+ @param[in] Instance The pointer to DHCP6 instance.
+
+**/
+VOID
+Dhcp6AppendCacheIa (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ UINT8 *Ptr;
+ UINTN Index;
+ UINTN IaSize;
+ UINTN NewIaSize;
+ EFI_DHCP6_IA *Ia;
+ EFI_DHCP6_IA *NewIa;
+ EFI_DHCP6_IA *CacheIa;
+
+ Ia = Instance->IaCb.Ia;
+ CacheIa = Instance->CacheIa;
+
+ if ((CacheIa != NULL) && (CacheIa->IaAddressCount != 0)) {
+ //
+ // There are old addresses existing. Merge with current addresses.
+ //
+ NewIaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount + CacheIa->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+ NewIa = AllocateZeroPool (NewIaSize);
+ if (NewIa == NULL) {
+ return;
+ }
+
+ IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+ CopyMem (NewIa, Ia, IaSize);
+
+ //
+ // Clear old address.ValidLifetime
+ //
+ for (Index = 0; Index < CacheIa->IaAddressCount; Index++) {
+ CacheIa->IaAddress[Index].ValidLifetime = 0;
+ }
+
+ NewIa->IaAddressCount += CacheIa->IaAddressCount;
+ Ptr = (UINT8*)&NewIa->IaAddress[Ia->IaAddressCount];
+ CopyMem (Ptr, CacheIa->IaAddress, CacheIa->IaAddressCount * sizeof (EFI_DHCP6_IA_ADDRESS));
+
+ //
+ // Migrate to the NewIa and free previous.
+ //
+ FreePool (Instance->CacheIa);
+ FreePool (Instance->IaCb.Ia);
+ Instance->CacheIa = NULL;
+ Instance->IaCb.Ia = NewIa;
+ }
+}
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h
new file mode 100644
index 0000000000..62985a3d72
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h
@@ -0,0 +1,340 @@
+/** @file
+ Dhcp6 support functions declaration.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP6_UTILITY_H__
+#define __EFI_DHCP6_UTILITY_H__
+
+
+#define DHCP6_10_BIT_MASK 0x3ff
+
+/**
+ Generate client Duid in the format of Duid-llt.
+
+ @param[in] Mode The pointer to the mode of SNP.
+
+ @retval NULL if failed to generate client Id.
+ @retval Others The pointer to the new client id.
+
+**/
+EFI_DHCP6_DUID *
+Dhcp6GenerateClientId (
+ IN EFI_SIMPLE_NETWORK_MODE *Mode
+ );
+
+/**
+ Copy the Dhcp6 configure data.
+
+ @param[in] DstCfg The pointer to the destination configure data.
+ @param[in] SorCfg The pointer to the source configure data.
+
+ @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6CopyConfigData (
+ IN EFI_DHCP6_CONFIG_DATA *DstCfg,
+ IN EFI_DHCP6_CONFIG_DATA *SorCfg
+ );
+
+/**
+ Clean up the configure data.
+
+ @param[in, out] CfgData The pointer to the configure data.
+
+**/
+VOID
+Dhcp6CleanupConfigData (
+ IN OUT EFI_DHCP6_CONFIG_DATA *CfgData
+ );
+
+/**
+ Clean up the mode data.
+
+ @param[in, out] ModeData The pointer to the mode data.
+
+**/
+VOID
+Dhcp6CleanupModeData (
+ IN OUT EFI_DHCP6_MODE_DATA *ModeData
+ );
+
+/**
+ Calculate the expire time by the algorithm defined in rfc.
+
+ @param[in] Base The base value of the time.
+ @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time.
+ @param[in] NeedSigned If TRUE, the the signed factor is needed.
+
+ @return Expire The calculated result for the new expire time.
+
+**/
+UINT32
+Dhcp6CalculateExpireTime (
+ IN UINT32 Base,
+ IN BOOLEAN IsFirstRt,
+ IN BOOLEAN NeedSigned
+ );
+
+/**
+ Calculate the lease time by the algorithm defined in rfc.
+
+ @param[in] IaCb The pointer to the Ia control block.
+
+**/
+VOID
+Dhcp6CalculateLeaseTime (
+ IN DHCP6_IA_CB *IaCb
+ );
+
+/**
+ Check whether the addresses are all included by the configured Ia.
+
+ @param[in] Ia The pointer to the Ia.
+ @param[in] AddressCount The number of addresses.
+ @param[in] Addresses The pointer to the addresses buffer.
+
+ @retval EFI_SUCCESS The addresses are all included by the configured IA.
+ @retval EFI_NOT_FOUND The addresses are not included by the configured IA.
+
+**/
+EFI_STATUS
+Dhcp6CheckAddress (
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ );
+
+/**
+ Deprive the addresses from current Ia, and generate another eliminated Ia.
+
+ @param[in] Ia The pointer to the Ia.
+ @param[in] AddressCount The number of addresses.
+ @param[in] Addresses The pointer to the addresses buffer.
+
+ @retval NULL If failed to generate the deprived Ia.
+ @retval others The pointer to the deprived Ia.
+
+**/
+EFI_DHCP6_IA *
+Dhcp6DepriveAddress (
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ );
+
+/**
+ The dummy ext buffer free callback routine.
+
+ @param[in] Arg The pointer to the parameter.
+
+**/
+VOID
+EFIAPI
+Dhcp6DummyExtFree (
+ IN VOID *Arg
+ );
+
+/**
+ The callback routine once message transmitted.
+
+ @param[in] Udp6Wrap The pointer to the received net buffer.
+ @param[in] EndPoint The pointer to the udp end point.
+ @param[in] IoStatus The return status from udp io.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6OnTransmitted (
+ IN NET_BUF *Wrap,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+/**
+ Append the appointed option to the buf, and move the buf to the end.
+
+ @param[in, out] Buf The pointer to buffer.
+ @param[in] OptType The option type.
+ @param[in] OptLen The lenght of option content.s
+ @param[in] Data The pointer to the option content.
+
+ @return Buf The position to append the next option.
+
+**/
+UINT8 *
+Dhcp6AppendOption (
+ IN OUT UINT8 *Buf,
+ IN UINT16 OptType,
+ IN UINT16 OptLen,
+ IN UINT8 *Data
+ );
+
+/**
+ Append the Ia option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the position to append.
+ @param[in] Ia The pointer to the Ia.
+ @param[in] T1 The time of T1.
+ @param[in] T2 The time of T2.
+
+ @return Buf The position to append the next Ia option.
+
+**/
+UINT8 *
+Dhcp6AppendIaOption (
+ IN OUT UINT8 *Buf,
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 T1,
+ IN UINT32 T2
+ );
+
+/**
+ Append the appointed Elapsed time option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[out] Elapsed The pointer to the elapsed time value in
+ the generated packet.
+
+ @return Buf The position to append the next Ia option.
+
+**/
+UINT8 *
+Dhcp6AppendETOption (
+ IN OUT UINT8 *Buf,
+ IN DHCP6_INSTANCE *Instance,
+ OUT UINT16 **Elapsed
+ );
+
+/**
+ Set the elapsed time based on the given instance and the pointer to the
+ elapsed time option.
+
+ @param[in] Elapsed The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp6 instance.
+**/
+VOID
+SetElapsedTime (
+ IN UINT16 *Elapsed,
+ IN DHCP6_INSTANCE *Instance
+ );
+
+/**
+ Seek the address of the first byte of the option header.
+
+ @param[in] Buf The pointer to buffer.
+ @param[in] SeekLen The length to seek.
+ @param[in] OptType The option type.
+
+ @retval NULL If failed to seek the option.
+ @retval others The position to the option.
+
+**/
+UINT8 *
+Dhcp6SeekOption (
+ IN UINT8 *Buf,
+ IN UINT32 SeekLen,
+ IN UINT16 OptType
+ );
+
+/**
+ Seek the address of the first byte of the Ia option header.
+
+ @param[in] Buf The pointer to the buffer.
+ @param[in] SeekLen The length to seek.
+ @param[in] IaDesc The pointer to the Ia descriptor.
+
+ @retval NULL If failed to seek the Ia option.
+ @retval others The position to the Ia option.
+
+**/
+UINT8 *
+Dhcp6SeekIaOption (
+ IN UINT8 *Buf,
+ IN UINT32 SeekLen,
+ IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc
+ );
+
+/**
+ Parse the address option and update the address info.
+
+ @param[in] IaInnerOpt The pointer to the buffer.
+ @param[in] IaInnerLen The length to parse.
+ @param[out] AddrNum The number of addresses.
+ @param[in, out] AddrBuf The pointer to the address buffer.
+
+**/
+VOID
+Dhcp6ParseAddrOption (
+ IN UINT8 *IaInnerOpt,
+ IN UINT16 IaInnerLen,
+ OUT UINT32 *AddrNum,
+ IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf
+ );
+
+/**
+ Create a control blcok for the Ia according to the corresponding options.
+
+ @param[in] Instance The pointer to DHCP6 Instance.
+ @param[in] IaInnerOpt The pointer to the inner options in the Ia option.
+ @param[in] IaInnerLen The length of all the inner options in the Ia option.
+ @param[in] T1 T1 time in the Ia option.
+ @param[in] T2 T2 time in the Ia option.
+
+ @retval EFI_NOT_FOUND No valid IA option is found.
+ @retval EFI_SUCCESS Create an IA control block successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6GenerateIaCb (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT8 *IaInnerOpt,
+ IN UINT16 IaInnerLen,
+ IN UINT32 T1,
+ IN UINT32 T2
+ );
+
+
+/**
+ Cache the current IA configuration information.
+
+ @param[in] Instance The pointer to DHCP6 Instance.
+
+ @retval EFI_SUCCESS Cache the current IA successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6CacheIa (
+ IN DHCP6_INSTANCE *Instance
+ );
+
+
+/**
+ Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.
+
+ @param[in] Instance The pointer to DHCP6 instance.
+
+**/
+VOID
+Dhcp6AppendCacheIa (
+ IN DHCP6_INSTANCE *Instance
+ );
+
+#endif