1217 lines
38 KiB
C
1217 lines
38 KiB
C
/** @file
|
|
This EFI_DHCP6_PROTOCOL interface implementation.
|
|
|
|
Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php.
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "Dhcp6Impl.h"
|
|
|
|
//
|
|
// Well-known multi-cast address defined in section-24.1 of rfc-3315
|
|
//
|
|
// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
|
|
// ALL_DHCP_Servers address: FF05::1:3
|
|
//
|
|
EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
|
|
EFI_IPv6_ADDRESS mAllDhcpServersAddress = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}};
|
|
|
|
EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = {
|
|
EfiDhcp6GetModeData,
|
|
EfiDhcp6Configure,
|
|
EfiDhcp6Start,
|
|
EfiDhcp6InfoRequest,
|
|
EfiDhcp6RenewRebind,
|
|
EfiDhcp6Decline,
|
|
EfiDhcp6Release,
|
|
EfiDhcp6Stop,
|
|
EfiDhcp6Parse
|
|
};
|
|
|
|
/**
|
|
Starts the DHCPv6 standard S.A.R.R. process.
|
|
|
|
The Start() function starts the DHCPv6 standard process. This function can
|
|
be called only when the state of Dhcp6 instance is in the Dhcp6Init state.
|
|
If the DHCP process completes successfully, the state of the Dhcp6 instance
|
|
will be transferred through Dhcp6Selecting and Dhcp6Requesting to the
|
|
Dhcp6Bound state.
|
|
Refer to rfc-3315 for precise state transitions during this process. At the
|
|
time when each event occurs in this process, the callback function that was set
|
|
by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this
|
|
opportunity to control the process.
|
|
|
|
@param[in] This The pointer to Dhcp6 protocol.
|
|
|
|
@retval EFI_SUCCESS The DHCPv6 standard process has started, or it has
|
|
completed when CompletionEvent is NULL.
|
|
@retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured.
|
|
@retval EFI_INVALID_PARAMETER This is NULL.
|
|
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
|
|
@retval EFI_TIMEOUT The DHCPv6 configuration process failed because no
|
|
response was received from the server within the
|
|
specified timeout value.
|
|
@retval EFI_ABORTED The user aborted the DHCPv6 process.
|
|
@retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6
|
|
standard process.
|
|
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
|
|
@retval EFI_NO_MEDIA There was a media error.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EfiDhcp6Start (
|
|
IN EFI_DHCP6_PROTOCOL *This
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_TPL OldTpl;
|
|
DHCP6_INSTANCE *Instance;
|
|
DHCP6_SERVICE *Service;
|
|
|
|
if (This == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Instance = DHCP6_INSTANCE_FROM_THIS (This);
|
|
Service = Instance->Service;
|
|
|
|
//
|
|
// The instance hasn't been configured.
|
|
//
|
|
if (Instance->Config == NULL) {
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
ASSERT (Instance->IaCb.Ia != NULL);
|
|
|
|
//
|
|
// The instance has already been started.
|
|
//
|
|
if (Instance->IaCb.Ia->State != Dhcp6Init) {
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
|
|
Instance->UdpSts = EFI_ALREADY_STARTED;
|
|
|
|
//
|
|
// 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);
|
|
|
|
//
|
|
// No valid REPLY message received yet, cleanup this instance directly.
|
|
//
|
|
if (Instance->IaCb.Ia->State == Dhcp6Init ||
|
|
Instance->IaCb.Ia->State == Dhcp6Selecting ||
|
|
Instance->IaCb.Ia->State == Dhcp6Requesting
|
|
) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Release the current ready Ia.
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
|
|
|
|
Instance->UdpSts = EFI_ALREADY_STARTED;
|
|
Status = Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia);
|
|
gBS->RestoreTPL (OldTpl);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
ON_EXIT:
|
|
//
|
|
// 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;
|
|
DHCP6_INSTANCE *Instance;
|
|
DHCP6_SERVICE *Service;
|
|
UINTN Index;
|
|
EFI_EVENT Timer;
|
|
EFI_STATUS TimerStatus;
|
|
UINTN GetMappingTimeOut;
|
|
|
|
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;
|
|
|
|
Status = Dhcp6StartInfoRequest (
|
|
Instance,
|
|
SendClientId,
|
|
OptionRequest,
|
|
OptionCount,
|
|
OptionList,
|
|
Retransmission,
|
|
TimeoutEvent,
|
|
ReplyCallback,
|
|
CallbackContext
|
|
);
|
|
if (Status == EFI_NO_MAPPING) {
|
|
//
|
|
// The link local address is not ready, wait for some time and restart
|
|
// the DHCP6 information request process.
|
|
//
|
|
Status = Dhcp6GetMappingTimeOut(Service->Ip6Cfg, &GetMappingTimeOut);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Start the timer, wait for link local address DAD to finish.
|
|
//
|
|
Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->CloseEvent (Timer);
|
|
return Status;
|
|
}
|
|
|
|
do {
|
|
TimerStatus = gBS->CheckEvent (Timer);
|
|
if (!EFI_ERROR (TimerStatus)) {
|
|
Status = Dhcp6StartInfoRequest (
|
|
Instance,
|
|
SendClientId,
|
|
OptionRequest,
|
|
OptionCount,
|
|
OptionList,
|
|
Retransmission,
|
|
TimeoutEvent,
|
|
ReplyCallback,
|
|
CallbackContext
|
|
);
|
|
}
|
|
} while (TimerStatus == EFI_NOT_READY);
|
|
|
|
gBS->CloseEvent (Timer);
|
|
}
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
|
|
/**
|
|
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;
|
|
}
|
|
|