From a3bcde70e6dc69000f85cc5deee98101d2ae200a Mon Sep 17 00:00:00 2001 From: hhtian Date: Mon, 1 Nov 2010 06:13:54 +0000 Subject: Add NetworkPkg (P.UDK2010.UP3.Network.P1) git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524 --- NetworkPkg/Dhcp6Dxe/ComponentName.c | 312 ++++ NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c | 782 +++++++++ NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h | 155 ++ NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf | 69 + NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c | 1220 ++++++++++++++ NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h | 597 +++++++ NetworkPkg/Dhcp6Dxe/Dhcp6Io.c | 2965 +++++++++++++++++++++++++++++++++++ NetworkPkg/Dhcp6Dxe/Dhcp6Io.h | 193 +++ NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c | 1146 ++++++++++++++ NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h | 340 ++++ 10 files changed, 7779 insertions(+) create mode 100644 NetworkPkg/Dhcp6Dxe/ComponentName.c create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Io.c create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Io.h create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h (limited to 'NetworkPkg/Dhcp6Dxe') 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.
+ + 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.
+ + 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.
+ + 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 + +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.
+# +# 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.
+ + 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.
+ + 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 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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.
+ + 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.
+ + 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.
+ + 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.
+ + 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 -- cgit v1.2.3