/*============================================================================
* Dhcp6Dxe.c - DHCPv6 DXE Driver (Reconstructed from IDA)
*============================================================================
* Binary: Dhcp6Dxe.efi
* SHA256: ac8951ae380a72682831be2e274a07832cee6d78fa7f9c5a35c4194f6078b688
* Image size: 0xa9c0
* Functions: 130 total
*
* Source files (from debug paths):
* e:\hs\AmiNetworkPkg\UefiNetworkStack\Ipv6\Dhcp6Dxe\Dhcp6Driver.c
* e:\hs\AmiNetworkPkg\UefiNetworkStack\Ipv6\Dhcp6Dxe\Dhcp6Impl.c
* e:\hs\AmiNetworkPkg\UefiNetworkStack\Ipv6\Dhcp6Dxe\Dhcp6Io.c
* e:\hs\AmiNetworkPkg\UefiNetworkStack\Ipv6\Dhcp6Dxe\Dhcp6Utility.c
* e:\hs\MdeModulePkg\Library\DxeUdpIoLib\DxeUdpIoLib.c
* e:\hs\MdeModulePkg\Library\DxeNetLib\NetBuffer.c
* e:\hs\MdePkg\Library\BaseLib\LinkedList.c
* e:\hs\MdePkg\Library\BaseLib\String.c
* e:\hs\MdePkg\Library\UefiDriverEntryPoint\DriverEntryPoint.c
* e:\hs\MdePkg\Library\UefiBootServicesTableLib\UefiBootServicesTableLib.c
* e:\hs\MdePkg\Library\UefiRuntimeServicesTableLib\UefiRuntimeServicesTableLib.c
* e:\hs\MdeModulePkg\Library\DxeDpcLib\DpcLib.c
*
* This is a reconstructed C representation based on static binary analysis.
*============================================================================*/
#include "Dhcp6Dxe.h"
/*============================================================================
* Globals
*============================================================================*/
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
/*============================================================================
* Forward declarations for internal functions
*============================================================================*/
/* --- Dhcp6Driver.c (0x84C-0x102C) --- */
static EFI_STATUS
Dhcp6CreateService (
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE DriverBindingHandle,
OUT DHCP6_SERVICE **Service
);
static EFI_STATUS
Dhcp6DestroyService (
IN DHCP6_SERVICE *Service
);
static EFI_STATUS
Dhcp6DriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
);
static EFI_STATUS
Dhcp6DriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
);
static EFI_STATUS
Dhcp6DriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
);
static EFI_STATUS
Dhcp6CreateInstance (
IN DHCP6_SERVICE *Service,
OUT DHCP6_INSTANCE **Instance
);
static EFI_STATUS
Dhcp6CloseInstance (
IN DHCP6_INSTANCE *Instance
);
static VOID
Dhcp6FreeInstance (
IN DHCP6_INSTANCE *Instance
);
/* --- Dhcp6Impl.c (0x22F4-0x3270) --- */
static EFI_STATUS
EfiDhcp6Start (
IN EFI_DHCP6_PROTOCOL *This
);
static EFI_STATUS
EfiDhcp6Stop (
IN EFI_DHCP6_PROTOCOL *This
);
static EFI_STATUS
EfiDhcp6Solicit (
IN EFI_DHCP6_PROTOCOL *This,
IN UINT32 OptionCount,
IN VOID *OptionList,
IN VOID *ClientId,
IN VOID *IaCbData,
IN UINT32 IaId
);
static EFI_STATUS
EfiDhcp6Request (
IN EFI_DHCP6_PROTOCOL *This,
IN UINT32 OptionCount,
IN VOID *OptionList,
IN VOID *ClientId,
IN VOID *IaCbData,
IN UINT32 IaId
);
static EFI_STATUS
EfiDhcp6Renew (
IN EFI_DHCP6_PROTOCOL *This
);
static EFI_STATUS
EfiDhcp6Rebind (
IN EFI_DHCP6_PROTOCOL *This,
IN UINT32 OptionCount,
IN VOID *OptionList
);
static EFI_STATUS
EfiDhcp6RenewRebind (
IN EFI_DHCP6_PROTOCOL *This,
IN UINT32 OptionCount,
IN VOID *OptionList
);
static EFI_STATUS
EfiDhcp6Decline (
IN EFI_DHCP6_PROTOCOL *This,
IN UINT32 OptionCount,
IN VOID *OptionList
);
static EFI_STATUS
EfiDhcp6Release (
IN EFI_DHCP6_PROTOCOL *This,
IN UINT32 OptionCount,
IN VOID *OptionList
);
/* --- Dhcp6Io.c (0x3360-0x59B0) --- */
static EFI_STATUS
Dhcp6TransmitWorker (
IN DHCP6_INSTANCE *Instance,
IN DHCP6_PACKET *Packet,
IN DHCP6_TX_CB *TxCb
);
static VOID
Dhcp6SetTimer (
IN DHCP6_INSTANCE *Instance,
IN UINT32 IaState
);
static VOID
Dhcp6SetRetransmitParams (
IN DHCP6_INSTANCE *Instance,
IN UINT32 IaState
);
static EFI_STATUS
Dhcp6IaStateMachine (
IN DHCP6_INSTANCE *Instance,
IN UINT32 IaState
);
static EFI_STATUS
Dhcp6BuildSolicitAdvertFw (
IN DHCP6_INSTANCE *Instance,
OUT VOID **Packet
);
static EFI_STATUS
Dhcp6BuildRequestFw (
IN DHCP6_INSTANCE *Instance,
IN UINT16 *ServerId,
OUT VOID **Packet
);
static EFI_STATUS
Dhcp6BuildRenewFw (
IN DHCP6_INSTANCE *Instance,
IN UINT16 *ServerId,
OUT VOID **Packet
);
static UINT32
Dhcp6CalculateWait (
IN UINT32 T1,
IN UINT32 T2
);
static EFI_STATUS
Dhcp6CheckMedia (
IN DHCP6_INSTANCE *Instance,
OUT BOOLEAN *MediaPresent
);
static EFI_STATUS
Dhcp6BuildDeclineFw (
IN DHCP6_INSTANCE *Instance,
IN UINT16 *IaOption,
OUT VOID **Packet
);
static EFI_STATUS
Dhcp6BuildReleaseFw (
IN DHCP6_INSTANCE *Instance,
IN UINT16 *ServerId,
OUT VOID **Packet
);
static EFI_STATUS
Dhcp6StartDelayedTx (
IN DHCP6_INSTANCE *Instance,
IN DHCP6_PACKET *Packet
);
static EFI_STATUS
Dhcp6SelectAdvertise (
IN DHCP6_INSTANCE *Instance,
IN BOOLEAN IsRebind,
OUT VOID **AdSelect
);
static EFI_STATUS
Dhcp6VerifyAdvertise (
IN DHCP6_INSTANCE *Instance,
IN VOID *Advertise
);
static EFI_STATUS
Dhcp6ProcessReply (
IN DHCP6_INSTANCE *Instance,
IN VOID *Reply,
OUT VOID **AdSelect
);
static EFI_STATUS
Dhcp6ProcessMessage (
IN DHCP6_INSTANCE *Instance,
IN DHCP6_PACKET *Packet
);
/* --- Dhcp6Utility.c (0x1468-0x21F4) --- */
static VOID
Dhcp6CreateClientId (
IN DHCP6_SERVICE *Service,
OUT VOID **ClientId
);
static EFI_STATUS
Dhcp6CopyConfigData (
IN VOID *Dest,
IN VOID *Src
);
static VOID
Dhcp6FreeConfigData (
IN VOID *CfgData
);
static UINT32
Dhcp6Random (
VOID
);
static EFI_STATUS
Dhcp6CheckIaAddress (
IN IA_CB *IaCb,
IN UINT32 AddressCount,
IN VOID *AddressArray
);
static VOID *
Dhcp6PickIaAddresses (
IN IA_CB *IaCb,
IN UINT32 AddressCount,
IN VOID *AddressArray
);
static VOID *
Dhcp6AppendOption (
IN VOID *Buffer,
IN UINT16 OpCode,
IN UINT16 OpLen,
IN VOID *Data
);
static VOID *
Dhcp6FindOption (
IN VOID *Buffer,
IN UINT16 BufLen,
IN UINT16 OpCode
);
static VOID *
Dhcp6FindIaOption (
IN VOID *IaOptions,
IN UINT16 IaLen,
IN UINT16 IaType,
IN VOID *Config
);
static VOID
Dhcp6ParseIaAddresses (
IN IA_CB *IaCb,
IN VOID *IaOption,
IN UINT16 IaLen,
OUT VOID *AddressArray,
OUT UINT32 *AddressCount
);
static EFI_STATUS
Dhcp6UpdateIaAddress (
IN DHCP6_INSTANCE *Instance,
IN VOID *IaOption,
IN UINT16 IaLen,
IN UINT32 T1,
IN UINT32 T2
);
static UINT32
Dhcp6CalculateIaT1T2 (
IN UINT32 ValidLifetime,
IN UINT32 T1,
IN UINT32 T2
);
/*============================================================================
* Dhcp6Driver.c - Driver Binding Protocol Entry Point
*============================================================================*/
EFI_STATUS
EFIAPI
Dhcp6DriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
VOID *Interface;
VOID *Registration;
/* Initialize global table pointers (gImageHandle, gST, gBS, gRT) */
Status = EfiLibInitTablePointers(ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
return Status;
}
/* Register for DPC protocol */
Interface = NULL;
Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, &Interface);
ASSERT_EFI_ERROR (Status);
/* Register Unload handler and driver binding */
Status = EfiLibInstallAllDriverProtocols2 (
ImageHandle,
SystemTable,
&gDhcp6DriverBinding,
ImageHandle,
&gDhcp6ComponentName2,
&gDhcp6ComponentName,
NULL
);
ASSERT_EFI_ERROR (Status);
return Status;
}
/*============================================================================
* Dhcp6DriverBindingSupported
*============================================================================*/
EFI_STATUS
EFIAPI
Dhcp6DriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
VOID *Interface;
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiUdp6ServiceBindingProtocolGuid,
&Interface,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
gBS->CloseProtocol (
ControllerHandle,
&gEfiUdp6ServiceBindingProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return EFI_SUCCESS;
}
/*============================================================================
* Dhcp6CreateService
* Allocate and initialize DHCP6_SERVICE structure.
* Creates UDP IO, generates Client ID, seeds transaction ID.
*============================================================================*/
static EFI_STATUS
Dhcp6CreateService (
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE DriverBindingHandle,
OUT DHCP6_SERVICE **ServiceOut
)
{
DHCP6_SERVICE *Service;
EFI_STATUS Status;
UINT32 Seed;
UINT16 TimeLow;
UINT8 MacBuffer[16];
UINT8 MacAddrSize;
UINT32 Tick;
*ServiceOut = NULL;
Service = (DHCP6_SERVICE *) AllocateZeroPool (sizeof (DHCP6_SERVICE));
if (Service == NULL) {
return EFI_OUT_OF_RESOURCES;
}
/* Open UDP6 protocol */
Status = UdpIoOpen (ControllerHandle, &Service->UdpIo);
if (Status != EFI_SUCCESS || Service->UdpIo == NULL) {
goto ErrorExit;
}
/* Fill service fields */
Service->Signature = DHCP6_SERVICE_SIGNATURE;
Service->ControllerHandle = ControllerHandle;
Service->DriverBindingHandle = DriverBindingHandle;
InitializeListHead (&Service->InstanceList);
/* Generate MAC-based seed for transaction ID */
ZeroMem (MacBuffer, sizeof (MacBuffer));
gRT->GetTime (&TimeLow, NULL);
gBS->GetNextMonotonicCount (&Tick);
Status = IpIoGetMacAddress (Service->UdpIo, MacBuffer, &MacAddrSize);
if (EFI_ERROR (Status)) {
Seed = Tick + (TimeLow ^ (TimeLow >> 8) ^ (MacBuffer[0] | (MacBuffer[1] | ((MacBuffer[2] | (~MacBuffer[3] << 8)) << 8)) << 8));
} else {
Seed = Tick;
}
Seed = (1103515245 * Seed + 12345) % 0xFFFFFFFF;
Service->TransactionIdSeed = Seed & 0xFFFFFF;
/* Copy protocol GUID */
CopyMem (&Service->Udp6CfgData, &gEfiUdp6ProtocolGuid, sizeof (EFI_GUID));
/* Open loaded image protocol to get MAC address for Client ID */
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiLoadedImageProtocolGuid,
&Interface,
DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
goto ErrorExit;
}
/* Create DUID Client ID */
Service->ClientId = Dhcp6CreateClientId (Service, Interface);
if (Service->ClientId == NULL) {
Status = EFI_UNSUPPORTED;
goto ErrorExit;
}
/* Create UDP I/O instance */
Service->UdpIoHandle = Dhcp6CreateUdpIo (ControllerHandle, DriverBindingHandle);
if (Service->UdpIoHandle == NULL) {
FreePool (Service->ClientId);
goto ErrorExit;
}
InitializeListHead (&Service->RecvRequest);
*ServiceOut = Service;
return EFI_SUCCESS;
ErrorExit:
FreePool (Service);
return Status;
}
/*============================================================================
* Dhcp6DriverBindingStart
* Called when a UDP6 interface is found. Creates DHCP6_SERVICE and
* installs DHCP6 service binding protocol.
*============================================================================*/
static EFI_STATUS
EFIAPI
Dhcp6DriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
DHCP6_SERVICE *Service;
EFI_STATUS Status;
/* Check if protocol already installed */
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDhcp6ServiceBindingProtocolGuid,
NULL,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (!EFI_ERROR (Status)) {
return EFI_ALREADY_STARTED;
}
/* Create DHCP6 service */
Status = Dhcp6CreateService (ControllerHandle, This->DriverBindingHandle, &Service);
if (EFI_ERROR (Status)) {
return Status;
}
/* Install service binding protocol */
Status = gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gEfiDhcp6ServiceBindingProtocolGuid,
&Service->ServiceBinding,
NULL
);
if (EFI_ERROR (Status)) {
Dhcp6DestroyService (Service);
return Status;
}
return EFI_SUCCESS;
}
/*============================================================================
* Dhcp6DriverBindingStop
*============================================================================*/
static EFI_STATUS
EFIAPI
Dhcp6DriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
DHCP6_SERVICE *Service;
UINTN NumHandles;
EFI_HANDLE *HandleBuffer;
/* Find DHCP6 protocol handles */
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiDhcp6ProtocolGuid,
NULL,
&NumHandles,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return EFI_SUCCESS;
}
/* For each handle, check if it belongs to this controller */
for (UINTN Index = 0; Index < NumHandles; Index++) {
DHCP6_INSTANCE *Instance;
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gEfiDhcp6ProtocolGuid,
&Instance,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
continue;
}
/* Check signature */
if (Instance->Signature != DHCP6_INSTANCE_SIGNATURE) {
continue;
}
/* Check if child handle matches */
if (NumberOfChildren > 0) {
BOOLEAN Found = FALSE;
for (UINTN i = 0; i < NumberOfChildren; i++) {
if (ChildHandleBuffer[i] == HandleBuffer[Index]) {
Found = TRUE;
break;
}
}
if (!Found) {
continue;
}
}
/* Close the instance */
if (Instance->Config != NULL) {
Dhcp6FreeConfigData (Instance->Config);
}
/* Uninstall protocol and free instance */
gBS->CloseProtocol (
HandleBuffer[Index],
&gEfiDhcp6ProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
RemoveEntryList (&Instance->Link);
Dhcp6FreeInstance (Instance);
}
FreePool (HandleBuffer);
/* If no more children, destroy the service */
Service = NULL;
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDhcp6ServiceBindingProtocolGuid,
&Service,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
if (Service->NumOfChild == 0) {
gBS->UninstallMultipleProtocolInterfaces (
ControllerHandle,
&gEfiDhcp6ServiceBindingProtocolGuid,
&Service->ServiceBinding,
NULL
);
Dhcp6DestroyService (Service);
}
}
return EFI_SUCCESS;
}
/*============================================================================
* Dhcp6DestroyService
*============================================================================*/
static EFI_STATUS
Dhcp6DestroyService (
IN DHCP6_SERVICE *Service
)
{
ASSERT (Service->NumOfChild == 0);
/* Free Client ID */
if (Service->ClientId != NULL) {
FreePool (Service->ClientId);
}
/* Free UDP I/O */
UdpIoDelete (Service->UdpIo);
UdpIoFreeService (Service->UdpIo);
/* Free the service itself */
FreePool (Service);
return EFI_SUCCESS;
}
/*============================================================================
* Dhcp6Impl.c - EFI_DHCP6_PROTOCOL Implementation
*============================================================================*/
/*============================================================================
* EfiDhcp6Start
* Start DHCP6 on this instance. Checks media, configures IA_CB,
* begins Solicit/Information-Request as appropriate.
*============================================================================*/
static EFI_STATUS
EfiDhcp6Start (
IN EFI_DHCP6_PROTOCOL *This
)
{
DHCP6_INSTANCE *Instance;
DHCP6_SERVICE *Service;
BOOLEAN MediaPresent;
UINT64 Ticker;
EFI_STATUS Status;
Instance = CR (This, DHCP6_INSTANCE, Dhcp6Protocol, DHCP6_INSTANCE_SIGNATURE);
if (Instance == NULL) {
return EFI_INVALID_PARAMETER;
}
Service = Instance->Service;
if (Service == NULL) {
return EFI_NOT_READY;
}
/* Check if already started */
if (Instance->IaCb != NULL && Instance->IaCb->IaState != DHCP6_STATE_INIT) {
return EFI_ALREADY_STARTED;
}
/* Allocate ticker */
Ticker = gBS->AllocatePool (8);
Instance->CompletionStatus = EFI_ALREADY_STARTED;
Instance->Configured = TRUE;
/* Check media presence */
IpIoOpen (Service->IpIo, &MediaPresent);
if (!MediaPresent) {
Status = EFI_NO_MEDIA;
DebugPrint (0x80000000, "\nIn EfiDhcp6Start MediaPresent Status = %r\n", Status);
gBS->FreePool (Ticker);
return Status;
}
/* Check media status more thoroughly */
Status = Dhcp6CheckMedia (Instance, &MediaPresent);
if (EFI_ERROR (Status)) {
gBS->FreePool (Ticker);
return Status;
}
/* Register Unicode collation (for option string comparison) */
Status = UefiUnicodeCollationStub (Service->UdpIo, Service->DriverBindingHandle, Service);
if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
gBS->FreePool (Ticker);
return Status;
}
gBS->FreePool (Ticker);
/* If config has no IA, we are done (Information-Request only) */
if (Instance->Config != NULL && Instance->Config->IaDescriptorType != 0) {
/* Wait for completion */
while (Instance->CompletionStatus == EFI_ALREADY_STARTED) {
/* Poll UDP */
}
return Instance->CompletionStatus;
}
return EFI_SUCCESS;
}
/*============================================================================
* EfiDhcp6Stop
* Stop DHCP6 on this instance. Cleans up IA state and TX blocks.
*============================================================================*/
static EFI_STATUS
EfiDhcp6Stop (
IN EFI_DHCP6_PROTOCOL *This
)
{
DHCP6_INSTANCE *Instance;
EFI_STATUS Status;
UINT64 Ticker;
Instance = CR (This, DHCP6_INSTANCE, Dhcp6Protocol, DHCP6_INSTANCE_SIGNATURE);
if (Instance == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Instance->IaCb == NULL) {
return EFI_SUCCESS;
}
if (Instance->IaCb->IaState <= DHCP6_STATE_RELEASING) {
Ticker = gBS->AllocatePool (8);
Instance->CompletionStatus = EFI_ALREADY_STARTED;
Status = Dhcp6StartDelayedTx (Instance, (DHCP6_PACKET *)Instance->IaCb);
gBS->FreePool (Ticker);
if (EFI_ERROR (Status) && Instance->Config != NULL) {
Service = Instance->Service;
if (Service == NULL || Service->Udp6 == NULL) {
ASSERT (Service->Udp6 != NULL);
}
while (Instance->CompletionStatus == EFI_ALREADY_STARTED) {
/* Poll UDP */
}
Status = Instance->CompletionStatus;
}
}
Ticker = gBS->AllocatePool (8);
Dhcp6IaStateMachine (Instance, DHCP6_STATE_INIT);
gBS->FreePool (Ticker);
return Status;
}
/*============================================================================
* EfiDhcp6Solicit
* Initiate a Solicit-InformationRequest exchange.
*============================================================================*/
static EFI_STATUS
EfiDhcp6Solicit (
IN EFI_DHCP6_PROTOCOL *This,
IN UINT32 OptionCount,
IN VOID *OptionList,
IN VOID *ClientId,
IN VOID *IaCbData,
IN UINT32 IaId
)
{
DHCP6_INSTANCE *Instance;
DHCP6_SERVICE *Service;
Instance = CR (This, DHCP6_INSTANCE, Dhcp6Protocol, DHCP6_INSTANCE_SIGNATURE);
if (Instance == NULL) {
return EFI_INVALID_PARAMETER;
}
if (ClientId == NULL && IaCbData == NULL) {
return EFI_INVALID_PARAMETER;
}
Service = Instance->Service;
if (Instance->Config == NULL && IaCbData != NULL) {
/* Config not set yet, allocate */
Instance->Config = AllocateZeroPool (sizeof (DHCP6_CONFIG_DATA));
if (Instance->Config == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Dhcp6CopyConfigData (IaCbData, Instance->Config);
}
/* Check Service has ClientId */
if (Service->ClientId == NULL) {
ASSERT (Service->ClientId != NULL);
}
/* Allocate return buffers */
UINT64 Ticker = gBS->AllocatePool (8);
if (IaCbData != NULL) {
ZeroMem (IaCbData, sizeof (DHCP6_CONFIG_DATA));
if (Dhcp6CopyConfigData (IaCbData, Instance->Config) != EFI_SUCCESS) {
return EFI_DEVICE_ERROR;
}
}
if (ClientId != NULL) {
UINT16 ClientIdLen = *(UINT16 *)Service->ClientId + 2;
*(VOID **)ClientId = AllocatePool (ClientIdLen);
if (*(VOID **)ClientId == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (*(VOID **)ClientId, Service->ClientId, ClientIdLen);
/* IA_CB data */
if (Instance->IaCb != NULL) {
UINT32 IaCbSize = 24 * Instance->IaCb->IaAddressCount + 32;
*(VOID **)(ClientId + 8) = AllocatePool (IaCbSize);
if (*(VOID **)(ClientId + 8) == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (*(VOID **)(ClientId + 8), Instance->IaCb, IaCbSize);
if (Instance->IaCb->OptionList != NULL) {
*(VOID **)(*(VOID **)(ClientId + 8) + 16) = AllocatePool (*Instance->IaCb->OptionList);
if (*(VOID **)(*(VOID **)(ClientId + 8) + 16) == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (*(VOID **)(*(VOID **)(ClientId + 8) + 16), Instance->IaCb->OptionList, *Instance->IaCb->OptionList);
}
}
}
/* Transition state machine to SELECTING */
Instance->CompletionStatus = EFI_ALREADY_STARTED;
/* Start Solicit transmission via worker threads */
Status = Dhcp6TransmitSolicit (Instance, ClientId, IaId);
if (EFI_ERROR (Status)) {
goto Done;
}
/* Register with Unicode collation */
UefiUnicodeCollationStub (Service->UdpIo, Service->DriverBindingHandle, Service);
/* Wait for completion */
if (Instance->Config != NULL && Instance->Config->IaDescriptorType != 0) {
while (Instance->CompletionStatus == EFI_ALREADY_STARTED) {
/* Poll */
}
return Instance->CompletionStatus;
}
Done:
gBS->FreePool (Ticker);
return Status;
}
/*============================================================================
* EfiDhcp6Request, EfiDhcp6Renew, EfiDhcp6Rebind, EfiDhcp6RenewRebind
* EfiDhcp6Decline, EfiDhcp6Release
*============================================================================*/
/* (Remaining protocol functions follow the same pattern) */
/*============================================================================
* Dhcp6Io.c - Network I/O, Packet TX/RX, Retransmission State Machine
*============================================================================*/
/*============================================================================
* Dhcp6TransmitWorker
* Build and transmit a DHCPv6 packet over UDP6.
*============================================================================*/
static EFI_STATUS
Dhcp6TransmitWorker (
IN DHCP6_INSTANCE *Instance,
IN DHCP6_PACKET *Packet,
IN DHCP6_TX_CB *TxCb
)
{
ASSERT (Instance->Config != NULL);
ASSERT (Instance->IaCb != NULL);
ASSERT (Instance->Service != NULL);
ASSERT (Packet != NULL);
ASSERT (Packet->Size > Packet->Length + 8);
/* Build the TX frame (DHCPv6 message + options) */
UINT32 BufferLen;
VOID *Buffer;
UINT16 *ClientId;
UINT32 IaOptionsSize;
IA_CB *IaCb;
ClientId = (UINT16 *)Instance->Service->ClientId;
IaCb = Instance->IaCb;
/* Allocate buffer for the packet */
BufferLen = Packet->Length + 8 + *ClientId + 2;
if (IaCb != NULL) {
BufferLen += 24 * IaCb->IaAddressCount + 32;
}
Buffer = AllocatePool (BufferLen);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
/* Build DHCPv6 message header */
*(UINT8 *)Buffer = Packet->MessageType;
*(UINT32 *)(Buffer + 1) = Instance->Service->TransactionIdSeed << 8;
/* Options follow... */
UINT8 *Options = (UINT8 *)Buffer + 4;
/* Append Client ID option */
Options = Dhcp6AppendOption (Options, DHCP6_OPT_CLIENTID, *ClientId, ClientId + 1);
/* Append Server ID (if known) */
if (TxCb->ServerId != NULL) {
Options = Dhcp6AppendOption (Options, DHCP6_OPT_SERVERID, *TxCb->ServerId, TxCb->ServerId + 1);
}
/* Append IA option (IA_NA or IA_TA) */
if (IaCb != NULL) {
Options = Dhcp6BuildIaOption (Buffer, IaCb, Options);
}
/* Send over UDP via UdpIo */
UdpIoSend (Instance->Service->UdpIo, Buffer, EFI_SUCCESS, Instance);
return EFI_SUCCESS;
}
/*============================================================================
* Dhcp6ReceiveCallback
* Callback invoked by UdpIo when a DHCPv6 packet is received.
* Dispatches to the appropriate state machine handler.
*============================================================================*/
static VOID
EFIAPI
Dhcp6ReceiveCallback (
IN VOID *Udp6Wrap,
IN EFI_STATUS Status,
IN VOID *Context,
IN VOID *RxToken
)
{
DHCP6_INSTANCE *Instance;
DHCP6_PACKET *Packet;
EFI_STATUS ParseStatus;
UINT32 *RxBuffer;
UINT32 RxDataLen;
ASSERT (Udp6Wrap != NULL);
ASSERT (Context != NULL);
Instance = (DHCP6_INSTANCE *)Context;
if (EFI_ERROR (Status)) {
/* Just clean up and return */
return;
}
/* Parse received IPv6/UDP6 data into DHCPv6 packet */
RxDataLen = *(UINT32 *)((UINT8 *)Udp6Wrap + 132);
if (RxDataLen >= 4) {
RxBuffer = (UINT32 *)AllocatePool (RxDataLen + 13);
if (RxBuffer == NULL) {
return;
}
*RxBuffer = RxDataLen + 13;
*(UINT32 *)((UINT8 *)RxBuffer + 4) = Dhcp6ParseUdpData (Udp6Wrap, RxToken, RxDataLen, RxBuffer + 2);
if (*(UINT32 *)((UINT8 *)RxBuffer + 4) != 0) {
/* Check TX list for matching Transaction ID */
LIST_ENTRY *TxList = &Instance->TxList;
LIST_ENTRY *Entry = TxList->ForwardLink;
while (Entry != TxList) {
DHCP6_TX_CB *TxCb = CR (Entry, DHCP6_TX_CB, Link, DHCP6_TX_CB_SIGNATURE);
DHCP6_PACKET *TxPacket = TxCb->TxPacket;
/* Check transaction ID match */
LIST_ENTRY *TxChildList = TxPacket->TxList.ForwardLink;
LIST_ENTRY *TxChildEnd = &TxPacket->TxList;
while (TxChildList != TxChildEnd) {
if (((UINT32 *)TxChildList)[4] >> 8 == *(UINT32 *)(RxBuffer + 2)) {
/* Transaction match -- process reply */
goto FoundMatch;
}
TxChildList = ((LIST_ENTRY *)TxChildList)->ForwardLink;
}
Entry = Entry->ForwardLink;
}
FoundMatch:
/* Process message based on current state */
Dhcp6ProcessMessage (Instance, (DHCP6_PACKET *)RxBuffer);
}
}
}
/*============================================================================
* Dhcp6TimerTickng
* Timer notification function. Drives retransmission logic.
*============================================================================*/
static VOID
EFIAPI
Dhcp6TimerTickng (
IN EFI_EVENT Event,
IN DHCP6_INSTANCE *Instance
)
{
EFI_STATUS Status;
DHCP6_SERVICE *Service;
DHCP6_TX_CB *TxCb;
LIST_ENTRY *Entry;
LIST_ENTRY *NextEntry;
ASSERT (Context != NULL);
Instance = (DHCP6_INSTANCE *)Context;
if (Instance->IaCb == NULL) {
return;
}
/* Check for timeout on pending transmissions */
if (Instance->CompletionStatus != EFI_SUCCESS) {
/* Already completed or errored */
return;
}
/* Iterate TX list for retransmission timeout */
Entry = Instance->TxList.ForwardLink;
while (Entry != &Instance->TxList) {
TxCb = CR (Entry, DHCP6_TX_CB, Link, DHCP6_TX_CB_SIGNATURE);
NextEntry = Entry->ForwardLink;
/* Check if this TX has timed out */
if (TxCb->RetransmitCount > 0) {
TxCb->RetransmitCount--;
if (TxCb->RetransmitCount == 0) {
/* Timeout -- retransmit or fail */
if (TxCb->RetransmitMaxCount == 0 ||
TxCb->RetransmitCurTime < TxCb->RetransmitMaxTimeout) {
/* Retransmit */
UINT32 WaitTime = Dhcp6CalculateWait (TxCb->RetransmitTimeout, TxCb->RetransmitMaxTimeout);
TxCb->RetransmitTimeout = WaitTime;
TxCb->RetransmitTotal += WaitTime;
TxCb->RetransmitCurTime = WaitTime;
UINT32 RandomDelay = Dhcp6Random (100);
TxCb->RetransmitCurTime += (TxCb->RetransmitCurTime * RandomDelay) / 100;
Dhcp6TransmitWorker (Instance, TxCb->TxPacket, TxCb);
} else {
/* MRT exceeded -- complete with timeout */
Instance->CompletionStatus = EFI_TIMEOUT;
Dhcp6IaStateMachine (Instance, DHCP6_STATE_INIT);
break;
}
}
}
Entry = NextEntry;
}
}
/*============================================================================
* Dhcp6Utility.c - DHCPv6 Option Encoding/Decoding and Helpers
*============================================================================*/
/*============================================================================
* Dhcp6AppendOption
* Write a DHCPv6 option TLV (OpCode/OpLen/Data) at Buffer.
* Returns pointer to next option position.
*============================================================================*/
static VOID *
Dhcp6AppendOption (
IN VOID *Buffer,
IN UINT16 OpCode,
IN UINT16 OpLen,
IN VOID *Data
)
{
ASSERT (OpLen != 0);
*(UINT16 *)Buffer = SwapBytes16 (OpCode);
*(UINT16 *)((UINT8 *)Buffer + 2) = SwapBytes16 (OpLen);
VOID *OptionData = (UINT8 *)Buffer + 4;
UINT16 NetLen = SwapBytes16 (OpLen);
CopyMem (OptionData, Data, NetLen);
return (UINT8 *)OptionData + NetLen;
}
/*============================================================================
* Dhcp6FindOption
* Search Buffer (BufLen bytes) for option with OpCode.
* Returns pointer to option header or NULL.
*============================================================================*/
static VOID *
Dhcp6FindOption (
IN VOID *Buffer,
IN UINT16 BufLen,
IN UINT16 OpCode
)
{
UINT8 *Ptr;
UINT16 OptionCode;
UINT16 OptionLen;
/* Write option header code (network order) */
*(UINT16 *)Buffer = SwapBytes16 (OpCode);
Ptr = (UINT8 *)Buffer;
Ptr += 4; /* skip length field */
/* Parse */
UINT8 *OptionStart = (UINT8 *)Buffer;
*(UINT16 *)(OptionStart + 2) = 0; /* will be filled later */
*(UINT16 *)(OptionStart + 4) = SwapBytes16 ((UINT16)(UINT32)(*(UINT32 *)(Buffer + 4)));
*(UINT16 *)(OptionStart + 2) = SwapBytes16 (
(UINT16)(((UINT32)SwapBytes16 (*(UINT16 *)((UINT8 *)Buffer + 4))) & 0xFFFF)
);
return OptionStart;
}
/*============================================================================
* Dhcp6CalculateIaT1T2
* Calculate IA T1/T2 from ValidLifetime with constraints.
*============================================================================*/
static UINT32
Dhcp6CalculateIaT1T2 (
IN UINT32 ValidLifetime,
IN UINT32 T1,
IN UINT32 T2
)
{
UINT32 Result;
Result = T1 | (T2 << 8);
/* Clamp to valid range */
if (T1 >= 30 && T1 <= 60 && T2 >= 60) {
/* Scale based on ValidLifetime */
UINT64 Scaled = (UINT64)ValidLifetime * 0x462BE000;
Scaled >>= 32;
if (Scaled < 23) {
Scaled = 23;
}
Result = (UINT32)((UINT64)ValidLifetime * 0x26CF1600 >> 32);
if (Result > 0xFFFF) {
Result = 0xFFFF;
}
}
return Result;
}
/*============================================================================
* Dhcp6UpdateIaAddress
* Update or add IA addresses from received IA option.
*============================================================================*/
static EFI_STATUS
Dhcp6UpdateIaAddress (
IN DHCP6_INSTANCE *Instance,
IN VOID *IaOption,
IN UINT16 IaLen,
IN UINT32 T1,
IN UINT32 T2
)
{
IA_CB *IaCb;
UINT8 *IaAddr;
UINT32 CurrentCount;
IaCb = Instance->IaCb;
if (IaCb == NULL || Instance->Config == NULL) {
return EFI_INVALID_PARAMETER;
}
IaAddr = (UINT8 *)IaOption + 4; /* skip IA header */
CurrentCount = 0;
while ((UINTN)IaAddr < (UINTN)IaOption + IaLen + 4) {
UINT16 OptionLen = SwapBytes16 (*(UINT16 *)(IaAddr + 2)) + 4;
if (OptionLen < 4) break;
if (*(UINT16 *)IaAddr == SwapBytes16 (DHCP6_OPT_IAADDR)) {
/* Found IA Address option, copy it */
if (CurrentCount < DHCP6_MAX_IA_ADDRESSES) {
CopyMem (&IaCb->IaAddress[CurrentCount].IpAddress, IaAddr + 4, 16);
IaCb->IaAddress[CurrentCount].PreferredLifetime = SwapBytes32 (*(UINT32 *)(IaAddr + 20));
IaCb->IaAddress[CurrentCount].ValidLifetime = SwapBytes32 (*(UINT32 *)(IaAddr + 24));
CurrentCount++;
}
}
IaAddr += OptionLen;
}
IaCb->IaAddressCount = CurrentCount;
return EFI_SUCCESS;
}
/*============================================================================
* UdpIo wrapper functions (from DxeUdpIoLib)
*============================================================================*/
/*============================================================================
* UdpIoCreateService
* Allocate and initialize a UdpIo wrapper instance.
*============================================================================*/
UINTN
UdpIoCreateService (
IN EFI_HANDLE DriverBindingHandle,
IN EFI_HANDLE ControllerHandle,
IN EFI_GUID *ProtocolGuid,
OUT VOID **UdpIo
)
{
*UdpIo = AllocatePool (sizeof (UDP_IO));
if (*UdpIo == NULL) return 0;
/* Initialize UdpIo structure */
InitializeListHead (*UdpIo);
/* ... */
return *UdpIo;
}
/*============================================================================
* UdpIoFreeService
* Free a UdpIo wrapper instance.
*============================================================================*/
VOID
UdpIoFreeService (
IN VOID *UdpIo
)
{
FreePool (UdpIo);
}
/*============================================================================
* Module Entry Point
*============================================================================*/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Interface;
/* Initialize globals */
EfiLibInitTablePointers (ImageHandle, SystemTable);
/* Locate DPC protocol */
Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, &Interface);
if (EFI_ERROR (Status)) {
goto Done;
}
/* Set up driver unload handler */
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, &Interface);
if (EFI_ERROR (Status)) {
RvdhPrint (0, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
ASSERT_EFI_ERROR (Status);
}
/* Register driver model */
*(VOID **)((UINT8 *)Interface + 88) = UefiDriverModelUnload;
/* Install driver binding + component name */
Status = EfiLibInstallAllDriverProtocols (
ImageHandle,
SystemTable,
&gDhcp6DriverBinding,
ImageHandle,
&gDhcp6ComponentName2,
NULL,
NULL
);
Done:
return Status;
}