/** @file | |
The implementation of EFI IPv6 Configuration Protocol. | |
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | |
Copyright (c) Microsoft Corporation.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "Ip6Impl.h" | |
LIST_ENTRY mIp6ConfigInstanceList = { &mIp6ConfigInstanceList, &mIp6ConfigInstanceList }; | |
/** | |
The event process routine when the DHCPv6 service binding protocol is installed | |
in the system. | |
@param[in] Event Not used. | |
@param[in] Context Pointer to the IP6 config instance data. | |
**/ | |
VOID | |
EFIAPI | |
Ip6ConfigOnDhcp6SbInstalled ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
); | |
/** | |
Update the current policy to NewPolicy. During the transition | |
period, the default router list, on-link prefix list, autonomous prefix list | |
and address list in all interfaces will be released. | |
@param[in] IpSb The IP6 service binding instance. | |
@param[in] NewPolicy The new policy to be updated to. | |
**/ | |
VOID | |
Ip6ConfigOnPolicyChanged ( | |
IN IP6_SERVICE *IpSb, | |
IN EFI_IP6_CONFIG_POLICY NewPolicy | |
) | |
{ | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *Entry2; | |
LIST_ENTRY *Next; | |
IP6_INTERFACE *IpIf; | |
IP6_DAD_ENTRY *DadEntry; | |
IP6_DELAY_JOIN_LIST *DelayNode; | |
IP6_ADDRESS_INFO *AddrInfo; | |
IP6_PROTOCOL *Instance; | |
BOOLEAN Recovery; | |
Recovery = FALSE; | |
// | |
// Currently there are only two policies: Manual and Automatic. Regardless of | |
// what transition is going on, i.e., Manual -> Automatic and Automatic -> | |
// Manual, we have to free default router list, on-link prefix list, autonomous | |
// prefix list, address list in all the interfaces and destroy any IPv6 child | |
// instance whose local IP is neither 0 nor the link-local address. | |
// | |
Ip6CleanDefaultRouterList (IpSb); | |
Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix); | |
Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix); | |
// | |
// It's tricky... If the LinkLocal address is O.K., add back the link-local | |
// prefix to the on-link prefix table. | |
// | |
if (IpSb->LinkLocalOk) { | |
Ip6CreatePrefixListEntry ( | |
IpSb, | |
TRUE, | |
(UINT32)IP6_INFINIT_LIFETIME, | |
(UINT32)IP6_INFINIT_LIFETIME, | |
IP6_LINK_LOCAL_PREFIX_LENGTH, | |
&IpSb->LinkLocalAddr | |
); | |
} | |
if (!IsListEmpty (&IpSb->DefaultInterface->AddressList) && (IpSb->DefaultInterface->AddressCount > 0)) { | |
// | |
// If any IPv6 children (Instance) in configured state and use global unicast address, it will be | |
// destroyed in Ip6RemoveAddr() function later. Then, the upper layer driver's Stop() function will be | |
// called, which may break the upper layer network stacks. So, the driver should take the responsibility | |
// for the recovery by using ConnectController() after Ip6RemoveAddr(). | |
// Here, just check whether need to recover the upper layer network stacks later. | |
// | |
NET_LIST_FOR_EACH (Entry, &IpSb->DefaultInterface->AddressList) { | |
AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); | |
if (!IsListEmpty (&IpSb->Children)) { | |
NET_LIST_FOR_EACH (Entry2, &IpSb->Children) { | |
Instance = NET_LIST_USER_STRUCT_S (Entry2, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); | |
if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, &AddrInfo->Address)) { | |
Recovery = TRUE; | |
break; | |
} | |
} | |
} | |
} | |
// | |
// All IPv6 children that use global unicast address as its source address | |
// should be destroyed now. The survivers are those use the link-local address | |
// or the unspecified address as the source address. | |
// TODO: Conduct a check here. | |
Ip6RemoveAddr ( | |
IpSb, | |
&IpSb->DefaultInterface->AddressList, | |
&IpSb->DefaultInterface->AddressCount, | |
NULL, | |
0 | |
); | |
if ((IpSb->Controller != NULL) && Recovery) { | |
// | |
// ConnectController() to recover the upper layer network stacks. | |
// | |
gBS->ConnectController (IpSb->Controller, NULL, NULL, TRUE); | |
} | |
} | |
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { | |
// | |
// remove all pending delay node and DAD entries for the global addresses. | |
// | |
IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); | |
NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) { | |
DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link); | |
if (!NetIp6IsLinkLocalAddr (&DelayNode->AddressInfo->Address)) { | |
RemoveEntryList (&DelayNode->Link); | |
FreePool (DelayNode); | |
} | |
} | |
NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) { | |
DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE); | |
if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) { | |
// | |
// Fail this DAD entry if the address is not link-local. | |
// | |
Ip6OnDADFinished (FALSE, IpIf, DadEntry); | |
} | |
} | |
} | |
if (NewPolicy == Ip6ConfigPolicyAutomatic) { | |
// | |
// Set parameters to trigger router solicitation sending in timer handler. | |
// | |
IpSb->RouterAdvertiseReceived = FALSE; | |
IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS; | |
// | |
// delay 1 second | |
// | |
IpSb->Ticks = (UINT32)IP6_GET_TICKS (IP6_ONE_SECOND_IN_MS); | |
} | |
} | |
/** | |
The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration. | |
@param[in] Instance Pointer to the IP6 config instance data. | |
@param[in] OtherInfoOnly If FALSE, get stateful address and other information | |
via DHCPv6. Otherwise, only get the other information. | |
@retval EFI_SUCCESS The operation finished successfully. | |
@retval EFI_UNSUPPORTED The DHCP6 driver is not available. | |
**/ | |
EFI_STATUS | |
Ip6ConfigStartStatefulAutoConfig ( | |
IN IP6_CONFIG_INSTANCE *Instance, | |
IN BOOLEAN OtherInfoOnly | |
) | |
{ | |
EFI_STATUS Status; | |
IP6_SERVICE *IpSb; | |
EFI_DHCP6_CONFIG_DATA Dhcp6CfgData; | |
EFI_DHCP6_PROTOCOL *Dhcp6; | |
EFI_DHCP6_PACKET_OPTION *OptList[1]; | |
UINT16 OptBuf[4]; | |
EFI_DHCP6_PACKET_OPTION *Oro; | |
EFI_DHCP6_RETRANSMISSION InfoReqReXmit; | |
// | |
// A host must not invoke stateful address configuration if it is already | |
// participating in the statuful protocol as a result of an earlier advertisement. | |
// | |
if (Instance->Dhcp6Handle != NULL) { | |
return EFI_SUCCESS; | |
} | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
Instance->OtherInfoOnly = OtherInfoOnly; | |
Status = NetLibCreateServiceChild ( | |
IpSb->Controller, | |
IpSb->Image, | |
&gEfiDhcp6ServiceBindingProtocolGuid, | |
&Instance->Dhcp6Handle | |
); | |
if (Status == EFI_UNSUPPORTED) { | |
// | |
// No DHCPv6 Service Binding protocol, register a notify. | |
// | |
if (Instance->Dhcp6SbNotifyEvent == NULL) { | |
Instance->Dhcp6SbNotifyEvent = EfiCreateProtocolNotifyEvent ( | |
&gEfiDhcp6ServiceBindingProtocolGuid, | |
TPL_CALLBACK, | |
Ip6ConfigOnDhcp6SbInstalled, | |
(VOID *)Instance, | |
&Instance->Registration | |
); | |
} | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Instance->Dhcp6SbNotifyEvent != NULL) { | |
gBS->CloseEvent (Instance->Dhcp6SbNotifyEvent); | |
} | |
Status = gBS->OpenProtocol ( | |
Instance->Dhcp6Handle, | |
&gEfiDhcp6ProtocolGuid, | |
(VOID **)&Instance->Dhcp6, | |
IpSb->Image, | |
IpSb->Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
ASSERT_EFI_ERROR (Status); | |
Dhcp6 = Instance->Dhcp6; | |
Dhcp6->Configure (Dhcp6, NULL); | |
// | |
// Set the exta options to send. Here we only want the option request option | |
// with DNS SERVERS. | |
// | |
Oro = (EFI_DHCP6_PACKET_OPTION *)OptBuf; | |
Oro->OpCode = HTONS (DHCP6_OPT_ORO); | |
Oro->OpLen = HTONS (2); | |
*((UINT16 *)&Oro->Data[0]) = HTONS (DHCP6_OPT_DNS_SERVERS); | |
OptList[0] = Oro; | |
Status = EFI_SUCCESS; | |
if (!OtherInfoOnly) { | |
// | |
// Get stateful address and other information via DHCPv6. | |
// | |
Dhcp6CfgData.Dhcp6Callback = NULL; | |
Dhcp6CfgData.CallbackContext = NULL; | |
Dhcp6CfgData.OptionCount = 1; | |
Dhcp6CfgData.OptionList = &OptList[0]; | |
Dhcp6CfgData.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; | |
Dhcp6CfgData.IaDescriptor.IaId = Instance->IaId; | |
Dhcp6CfgData.IaInfoEvent = Instance->Dhcp6Event; | |
Dhcp6CfgData.ReconfigureAccept = FALSE; | |
Dhcp6CfgData.RapidCommit = FALSE; | |
Dhcp6CfgData.SolicitRetransmission = NULL; | |
Status = Dhcp6->Configure (Dhcp6, &Dhcp6CfgData); | |
if (!EFI_ERROR (Status)) { | |
if (IpSb->LinkLocalOk) { | |
Status = Dhcp6->Start (Dhcp6); | |
} else { | |
IpSb->Dhcp6NeedStart = TRUE; | |
} | |
} | |
} else { | |
// | |
// Only get other information via DHCPv6, this doesn't require a config | |
// action. | |
// | |
InfoReqReXmit.Irt = 4; | |
InfoReqReXmit.Mrc = 64; | |
InfoReqReXmit.Mrt = 60; | |
InfoReqReXmit.Mrd = 0; | |
if (IpSb->LinkLocalOk) { | |
Status = Dhcp6->InfoRequest ( | |
Dhcp6, | |
TRUE, | |
Oro, | |
0, | |
NULL, | |
&InfoReqReXmit, | |
Instance->Dhcp6Event, | |
Ip6ConfigOnDhcp6Reply, | |
Instance | |
); | |
} else { | |
IpSb->Dhcp6NeedInfoRequest = TRUE; | |
} | |
} | |
return Status; | |
} | |
/** | |
Signal the registered event. It is the callback routine for NetMapIterate. | |
@param[in] Map Points to the list of registered event. | |
@param[in] Item The registered event. | |
@param[in] Arg Not used. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Ip6ConfigSignalEvent ( | |
IN NET_MAP *Map, | |
IN NET_MAP_ITEM *Item, | |
IN VOID *Arg | |
) | |
{ | |
gBS->SignalEvent ((EFI_EVENT)Item->Key); | |
return EFI_SUCCESS; | |
} | |
/** | |
Read the configuration data from variable storage according to the VarName and | |
gEfiIp6ConfigProtocolGuid. It checks the integrity of variable data. If the | |
data is corrupted, it clears the variable data to ZERO. Otherwise, it outputs the | |
configuration data to IP6_CONFIG_INSTANCE. | |
@param[in] VarName The pointer to the variable name | |
@param[in, out] Instance The pointer to the IP6 config instance data. | |
@retval EFI_NOT_FOUND The variable can not be found or already corrupted. | |
@retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. | |
@retval EFI_SUCCESS The configuration data was retrieved successfully. | |
**/ | |
EFI_STATUS | |
Ip6ConfigReadConfigData ( | |
IN CHAR16 *VarName, | |
IN OUT IP6_CONFIG_INSTANCE *Instance | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN VarSize; | |
IP6_CONFIG_VARIABLE *Variable; | |
IP6_CONFIG_DATA_ITEM *DataItem; | |
UINTN Index; | |
IP6_CONFIG_DATA_RECORD DataRecord; | |
CHAR8 *Data; | |
// | |
// Try to read the configuration variable. | |
// | |
VarSize = 0; | |
Status = gRT->GetVariable ( | |
VarName, | |
&gEfiIp6ConfigProtocolGuid, | |
NULL, | |
&VarSize, | |
NULL | |
); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
// | |
// Allocate buffer and read the config variable. | |
// | |
Variable = AllocatePool (VarSize); | |
if (Variable == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Status = gRT->GetVariable ( | |
VarName, | |
&gEfiIp6ConfigProtocolGuid, | |
NULL, | |
&VarSize, | |
Variable | |
); | |
if (EFI_ERROR (Status) || ((UINT16)(~NetblockChecksum ((UINT8 *)Variable, (UINT32)VarSize)) != 0)) { | |
// | |
// GetVariable error or the variable is corrupted. | |
// | |
goto Error; | |
} | |
// | |
// Get the IAID we use. | |
// | |
Instance->IaId = Variable->IaId; | |
for (Index = 0; Index < Variable->DataRecordCount; Index++) { | |
CopyMem (&DataRecord, &Variable->DataRecord[Index], sizeof (DataRecord)); | |
DataItem = &Instance->DataItem[DataRecord.DataType]; | |
if (DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED) && | |
(DataItem->DataSize != DataRecord.DataSize) | |
) | |
{ | |
// | |
// Perhaps a corrupted data record... | |
// | |
continue; | |
} | |
if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) { | |
// | |
// This data item has variable length data. | |
// Check that the length is contained within the variable before allocating. | |
// | |
if (DataRecord.DataSize > VarSize - DataRecord.Offset) { | |
goto Error; | |
} | |
DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize); | |
if (DataItem->Data.Ptr == NULL) { | |
// | |
// no memory resource | |
// | |
continue; | |
} | |
} | |
Data = (CHAR8 *)Variable + DataRecord.Offset; | |
CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize); | |
DataItem->DataSize = DataRecord.DataSize; | |
DataItem->Status = EFI_SUCCESS; | |
} | |
FreePool (Variable); | |
return EFI_SUCCESS; | |
} | |
return Status; | |
Error: | |
// | |
// Fall back to the default value. | |
// | |
if (Variable != NULL) { | |
FreePool (Variable); | |
} | |
// | |
// Remove the problematic variable and return EFI_NOT_FOUND, a new | |
// variable will be set again. | |
// | |
gRT->SetVariable ( | |
VarName, | |
&gEfiIp6ConfigProtocolGuid, | |
IP6_CONFIG_VARIABLE_ATTRIBUTE, | |
0, | |
NULL | |
); | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Write the configuration data from IP6_CONFIG_INSTANCE to variable storage. | |
@param[in] VarName The pointer to the variable name. | |
@param[in] Instance The pointer to the IP6 configuration instance data. | |
@retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. | |
@retval EFI_SUCCESS The configuration data is written successfully. | |
**/ | |
EFI_STATUS | |
Ip6ConfigWriteConfigData ( | |
IN CHAR16 *VarName, | |
IN IP6_CONFIG_INSTANCE *Instance | |
) | |
{ | |
UINTN Index; | |
UINTN VarSize; | |
IP6_CONFIG_DATA_ITEM *DataItem; | |
IP6_CONFIG_VARIABLE *Variable; | |
IP6_CONFIG_DATA_RECORD *DataRecord; | |
CHAR8 *Heap; | |
EFI_STATUS Status; | |
VarSize = sizeof (IP6_CONFIG_VARIABLE) - sizeof (IP6_CONFIG_DATA_RECORD); | |
for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { | |
DataItem = &Instance->DataItem[Index]; | |
if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) { | |
VarSize += sizeof (IP6_CONFIG_DATA_RECORD) + DataItem->DataSize; | |
} | |
} | |
Variable = AllocatePool (VarSize); | |
if (Variable == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Variable->IaId = Instance->IaId; | |
Heap = (CHAR8 *)Variable + VarSize; | |
Variable->DataRecordCount = 0; | |
for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { | |
DataItem = &Instance->DataItem[Index]; | |
if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) { | |
Heap -= DataItem->DataSize; | |
CopyMem (Heap, DataItem->Data.Ptr, DataItem->DataSize); | |
DataRecord = &Variable->DataRecord[Variable->DataRecordCount]; | |
DataRecord->DataType = (EFI_IP6_CONFIG_DATA_TYPE)Index; | |
DataRecord->DataSize = (UINT32)DataItem->DataSize; | |
DataRecord->Offset = (UINT16)(Heap - (CHAR8 *)Variable); | |
Variable->DataRecordCount++; | |
} | |
} | |
Variable->Checksum = 0; | |
Variable->Checksum = (UINT16) ~NetblockChecksum ((UINT8 *)Variable, (UINT32)VarSize); | |
Status = gRT->SetVariable ( | |
VarName, | |
&gEfiIp6ConfigProtocolGuid, | |
IP6_CONFIG_VARIABLE_ATTRIBUTE, | |
VarSize, | |
Variable | |
); | |
FreePool (Variable); | |
return Status; | |
} | |
/** | |
The work function for EfiIp6ConfigGetData() to get the interface information | |
of the communication device this IP6Config instance manages. | |
@param[in] Instance Pointer to the IP6 config instance data. | |
@param[in, out] DataSize On input, in bytes, the size of Data. On output, in | |
bytes, the size of buffer required to store the specified | |
configuration data. | |
@param[in] Data The data buffer in which the configuration data is returned. | |
Ignored if DataSize is ZERO. | |
@retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified | |
configuration data, and the required size is | |
returned in DataSize. | |
@retval EFI_SUCCESS The specified configuration data was obtained. | |
**/ | |
EFI_STATUS | |
Ip6ConfigGetIfInfo ( | |
IN IP6_CONFIG_INSTANCE *Instance, | |
IN OUT UINTN *DataSize, | |
IN VOID *Data OPTIONAL | |
) | |
{ | |
IP6_SERVICE *IpSb; | |
UINTN Length; | |
IP6_CONFIG_DATA_ITEM *Item; | |
EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; | |
UINT32 AddressCount; | |
UINT32 RouteCount; | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
Length = sizeof (EFI_IP6_CONFIG_INTERFACE_INFO); | |
// | |
// Calculate the required length, add the buffer size for AddressInfo and | |
// RouteTable | |
// | |
Ip6BuildEfiAddressList (IpSb, &AddressCount, NULL); | |
Ip6BuildEfiRouteTable (IpSb->RouteTable, &RouteCount, NULL); | |
Length += AddressCount * sizeof (EFI_IP6_ADDRESS_INFO) + RouteCount * sizeof (EFI_IP6_ROUTE_TABLE); | |
if (*DataSize < Length) { | |
*DataSize = Length; | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
// | |
// Copy the fixed size part of the interface info. | |
// | |
Item = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo]; | |
IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *)Data; | |
CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP6_CONFIG_INTERFACE_INFO)); | |
// | |
// AddressInfo | |
// | |
IfInfo->AddressInfo = (EFI_IP6_ADDRESS_INFO *)(IfInfo + 1); | |
Ip6BuildEfiAddressList (IpSb, &IfInfo->AddressInfoCount, &IfInfo->AddressInfo); | |
// | |
// RouteTable | |
// | |
IfInfo->RouteTable = (EFI_IP6_ROUTE_TABLE *)(IfInfo->AddressInfo + IfInfo->AddressInfoCount); | |
Ip6BuildEfiRouteTable (IpSb->RouteTable, &IfInfo->RouteCount, &IfInfo->RouteTable); | |
if (IfInfo->AddressInfoCount == 0) { | |
IfInfo->AddressInfo = NULL; | |
} | |
if (IfInfo->RouteCount == 0) { | |
IfInfo->RouteTable = NULL; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
The work function for EfiIp6ConfigSetData() to set the alternative interface ID | |
for the communication device managed by this IP6Config instance, if the link local | |
IPv6 addresses generated from the interface ID based on the default source the | |
EFI IPv6 Protocol uses is a duplicate address. | |
@param[in] Instance Pointer to the IP6 configuration instance data. | |
@param[in] DataSize Size of the buffer pointed to by Data in bytes. | |
@param[in] Data The data buffer to set. | |
@retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type, | |
8 bytes. | |
@retval EFI_SUCCESS The specified configuration data for the EFI IPv6 | |
network stack was set. | |
**/ | |
EFI_STATUS | |
Ip6ConfigSetAltIfId ( | |
IN IP6_CONFIG_INSTANCE *Instance, | |
IN UINTN DataSize, | |
IN VOID *Data | |
) | |
{ | |
EFI_IP6_CONFIG_INTERFACE_ID *OldIfId; | |
EFI_IP6_CONFIG_INTERFACE_ID *NewIfId; | |
IP6_CONFIG_DATA_ITEM *DataItem; | |
if (DataSize != sizeof (EFI_IP6_CONFIG_INTERFACE_ID)) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId]; | |
OldIfId = DataItem->Data.AltIfId; | |
NewIfId = (EFI_IP6_CONFIG_INTERFACE_ID *)Data; | |
CopyMem (OldIfId, NewIfId, DataSize); | |
DataItem->Status = EFI_SUCCESS; | |
return EFI_SUCCESS; | |
} | |
/** | |
The work function for EfiIp6ConfigSetData() to set the general configuration | |
policy for the EFI IPv6 network stack that is running on the communication device | |
managed by this IP6Config instance. The policy will affect other configuration settings. | |
@param[in] Instance Pointer to the IP6 config instance data. | |
@param[in] DataSize Size of the buffer pointed to by Data in bytes. | |
@param[in] Data The data buffer to set. | |
@retval EFI_INVALID_PARAMETER The to be set policy is invalid. | |
@retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. | |
@retval EFI_ABORTED The new policy equals the current policy. | |
@retval EFI_SUCCESS The specified configuration data for the EFI IPv6 | |
network stack was set. | |
**/ | |
EFI_STATUS | |
Ip6ConfigSetPolicy ( | |
IN IP6_CONFIG_INSTANCE *Instance, | |
IN UINTN DataSize, | |
IN VOID *Data | |
) | |
{ | |
EFI_IP6_CONFIG_POLICY NewPolicy; | |
IP6_CONFIG_DATA_ITEM *DataItem; | |
IP6_SERVICE *IpSb; | |
if (DataSize != sizeof (EFI_IP6_CONFIG_POLICY)) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
NewPolicy = *((EFI_IP6_CONFIG_POLICY *)Data); | |
if (NewPolicy > Ip6ConfigPolicyAutomatic) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (NewPolicy == Instance->Policy) { | |
return EFI_ABORTED; | |
} else { | |
// | |
// Clean the ManualAddress, Gateway and DnsServers, shrink the variable | |
// data size, and fire up all the related events. | |
// | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; | |
if (DataItem->Data.Ptr != NULL) { | |
FreePool (DataItem->Data.Ptr); | |
} | |
DataItem->Data.Ptr = NULL; | |
DataItem->DataSize = 0; | |
DataItem->Status = EFI_NOT_FOUND; | |
NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway]; | |
if (DataItem->Data.Ptr != NULL) { | |
FreePool (DataItem->Data.Ptr); | |
} | |
DataItem->Data.Ptr = NULL; | |
DataItem->DataSize = 0; | |
DataItem->Status = EFI_NOT_FOUND; | |
NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; | |
DataItem->Data.Ptr = NULL; | |
DataItem->DataSize = 0; | |
DataItem->Status = EFI_NOT_FOUND; | |
NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); | |
if (NewPolicy == Ip6ConfigPolicyManual) { | |
// | |
// The policy is changed from automatic to manual. Stop the DHCPv6 process | |
// and destroy the DHCPv6 child. | |
// | |
if (Instance->Dhcp6Handle != NULL) { | |
Ip6ConfigDestroyDhcp6 (Instance); | |
} | |
} | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
Ip6ConfigOnPolicyChanged (IpSb, NewPolicy); | |
Instance->Policy = NewPolicy; | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
The work function for EfiIp6ConfigSetData() to set the number of consecutive | |
Neighbor Solicitation messages sent while performing Duplicate Address Detection | |
on a tentative address. A value of ZERO indicates that Duplicate Address Detection | |
will not be performed on a tentative address. | |
@param[in] Instance The Instance Pointer to the IP6 config instance data. | |
@param[in] DataSize Size of the buffer pointed to by Data in bytes. | |
@param[in] Data The data buffer to set. | |
@retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. | |
@retval EFI_ABORTED The new transmit count equals the current configuration. | |
@retval EFI_SUCCESS The specified configuration data for the EFI IPv6 | |
network stack was set. | |
**/ | |
EFI_STATUS | |
Ip6ConfigSetDadXmits ( | |
IN IP6_CONFIG_INSTANCE *Instance, | |
IN UINTN DataSize, | |
IN VOID *Data | |
) | |
{ | |
EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *OldDadXmits; | |
if (DataSize != sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS)) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
OldDadXmits = Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits].Data.DadXmits; | |
if ((*(UINT32 *)Data) == OldDadXmits->DupAddrDetectTransmits) { | |
return EFI_ABORTED; | |
} else { | |
OldDadXmits->DupAddrDetectTransmits = *((UINT32 *)Data); | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
The callback function for Ip6SetAddr. The prototype is defined | |
as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed | |
for the manual address set by Ip6ConfigSetManualAddress. | |
@param[in] IsDadPassed If TRUE, Duplicate Address Detection passed. | |
@param[in] TargetAddress The tentative IPv6 address to be checked. | |
@param[in] Context Pointer to the IP6 configuration instance data. | |
**/ | |
VOID | |
Ip6ManualAddrDadCallback ( | |
IN BOOLEAN IsDadPassed, | |
IN EFI_IPv6_ADDRESS *TargetAddress, | |
IN VOID *Context | |
) | |
{ | |
IP6_CONFIG_INSTANCE *Instance; | |
UINTN Index; | |
IP6_CONFIG_DATA_ITEM *Item; | |
EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddr; | |
EFI_IP6_CONFIG_MANUAL_ADDRESS *PassedAddr; | |
UINTN DadPassCount; | |
UINTN DadFailCount; | |
IP6_SERVICE *IpSb; | |
Instance = (IP6_CONFIG_INSTANCE *)Context; | |
NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); | |
Item = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; | |
ManualAddr = NULL; | |
if (Item->DataSize == 0) { | |
return; | |
} | |
for (Index = 0; Index < Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); Index++) { | |
// | |
// Find the original tag used to place into the NET_MAP. | |
// | |
ManualAddr = Item->Data.ManualAddress + Index; | |
if (EFI_IP6_EQUAL (TargetAddress, &ManualAddr->Address)) { | |
break; | |
} | |
} | |
ASSERT (Index != Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); | |
if (IsDadPassed) { | |
NetMapInsertTail (&Instance->DadPassedMap, ManualAddr, NULL); | |
} else { | |
NetMapInsertTail (&Instance->DadFailedMap, ManualAddr, NULL); | |
} | |
DadPassCount = NetMapGetCount (&Instance->DadPassedMap); | |
DadFailCount = NetMapGetCount (&Instance->DadFailedMap); | |
if ((DadPassCount + DadFailCount) == (Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS))) { | |
// | |
// All addresses have finished the configuration process. | |
// | |
if (DadFailCount != 0) { | |
// | |
// There is at least one duplicate address. | |
// | |
FreePool (Item->Data.Ptr); | |
Item->DataSize = DadPassCount * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); | |
if (Item->DataSize == 0) { | |
// | |
// All failed, bad luck. | |
// | |
Item->Data.Ptr = NULL; | |
Item->Status = EFI_NOT_FOUND; | |
} else { | |
// | |
// Part of addresses are detected to be duplicates, so update the | |
// data with those passed. | |
// | |
PassedAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *)AllocatePool (Item->DataSize); | |
ASSERT (PassedAddr != NULL); | |
Item->Data.Ptr = PassedAddr; | |
Item->Status = EFI_SUCCESS; | |
while (!NetMapIsEmpty (&Instance->DadPassedMap)) { | |
ManualAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *)NetMapRemoveHead (&Instance->DadPassedMap, NULL); | |
CopyMem (PassedAddr, ManualAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); | |
PassedAddr++; | |
} | |
ASSERT ((UINTN)PassedAddr - (UINTN)Item->Data.Ptr == Item->DataSize); | |
} | |
} else { | |
// | |
// All addresses are valid. | |
// | |
Item->Status = EFI_SUCCESS; | |
} | |
// | |
// Remove the tags we put in the NET_MAPs. | |
// | |
while (!NetMapIsEmpty (&Instance->DadFailedMap)) { | |
NetMapRemoveHead (&Instance->DadFailedMap, NULL); | |
} | |
while (!NetMapIsEmpty (&Instance->DadPassedMap)) { | |
NetMapRemoveHead (&Instance->DadPassedMap, NULL); | |
} | |
// | |
// Signal the waiting events. | |
// | |
NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL); | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
Ip6ConfigWriteConfigData (IpSb->MacString, Instance); | |
} | |
} | |
/** | |
The work function for EfiIp6ConfigSetData() to set the station addresses manually | |
for the EFI IPv6 network stack. It is only configurable when the policy is | |
Ip6ConfigPolicyManual. | |
@param[in] Instance Pointer to the IP6 configuration instance data. | |
@param[in] DataSize Size of the buffer pointed to by Data in bytes. | |
@param[in] Data The data buffer to set. | |
@retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. | |
@retval EFI_WRITE_PROTECTED The specified configuration data cannot be set | |
under the current policy. | |
@retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. | |
@retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. | |
@retval EFI_NOT_READY An asynchronous process is invoked to set the specified | |
configuration data, and the process is not finished. | |
@retval EFI_ABORTED The manual addresses to be set equal current | |
configuration. | |
@retval EFI_SUCCESS The specified configuration data for the EFI IPv6 | |
network stack was set. | |
**/ | |
EFI_STATUS | |
Ip6ConfigSetManualAddress ( | |
IN IP6_CONFIG_INSTANCE *Instance, | |
IN UINTN DataSize, | |
IN VOID *Data | |
) | |
{ | |
EFI_IP6_CONFIG_MANUAL_ADDRESS *NewAddress; | |
EFI_IP6_CONFIG_MANUAL_ADDRESS *TmpAddress; | |
IP6_CONFIG_DATA_ITEM *DataItem; | |
UINTN NewAddressCount; | |
UINTN Index1; | |
UINTN Index2; | |
IP6_SERVICE *IpSb; | |
IP6_ADDRESS_INFO *CurrentAddrInfo; | |
IP6_ADDRESS_INFO *Copy; | |
LIST_ENTRY CurrentSourceList; | |
UINT32 CurrentSourceCount; | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *Entry2; | |
IP6_INTERFACE *IpIf; | |
IP6_PREFIX_LIST_ENTRY *PrefixEntry; | |
EFI_STATUS Status; | |
BOOLEAN IsUpdated; | |
LIST_ENTRY *Next; | |
IP6_DAD_ENTRY *DadEntry; | |
IP6_DELAY_JOIN_LIST *DelayNode; | |
NewAddress = NULL; | |
TmpAddress = NULL; | |
CurrentAddrInfo = NULL; | |
Copy = NULL; | |
Entry = NULL; | |
Entry2 = NULL; | |
IpIf = NULL; | |
PrefixEntry = NULL; | |
Next = NULL; | |
DadEntry = NULL; | |
DelayNode = NULL; | |
Status = EFI_SUCCESS; | |
ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY); | |
if ((DataSize != 0) && ((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0)) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Instance->Policy != Ip6ConfigPolicyManual) { | |
return EFI_WRITE_PROTECTED; | |
} | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; | |
if ((Data != NULL) && (DataSize != 0)) { | |
NewAddressCount = DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); | |
NewAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *)Data; | |
for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) { | |
if (NetIp6IsLinkLocalAddr (&NewAddress->Address) || | |
!NetIp6IsValidUnicast (&NewAddress->Address) || | |
(NewAddress->PrefixLength > 128) | |
) | |
{ | |
// | |
// make sure the IPv6 address is unicast and not link-local address && | |
// the prefix length is valid. | |
// | |
return EFI_INVALID_PARAMETER; | |
} | |
TmpAddress = NewAddress + 1; | |
for (Index2 = Index1 + 1; Index2 < NewAddressCount; Index2++, TmpAddress++) { | |
// | |
// Any two addresses in the array can't be equal. | |
// | |
if (EFI_IP6_EQUAL (&TmpAddress->Address, &NewAddress->Address)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
} | |
// | |
// Build the current source address list. | |
// | |
InitializeListHead (&CurrentSourceList); | |
CurrentSourceCount = 0; | |
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { | |
IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); | |
NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { | |
CurrentAddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); | |
Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), CurrentAddrInfo); | |
if (Copy == NULL) { | |
break; | |
} | |
InsertTailList (&CurrentSourceList, &Copy->Link); | |
CurrentSourceCount++; | |
} | |
} | |
// | |
// Update the value... a long journey starts | |
// | |
NewAddress = AllocateCopyPool (DataSize, Data); | |
if (NewAddress == NULL) { | |
Ip6RemoveAddr (NULL, &CurrentSourceList, &CurrentSourceCount, NULL, 0); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Store the new data, and init the DataItem status to EFI_NOT_READY because | |
// we may have an asynchronous configuration process. | |
// | |
if (DataItem->Data.Ptr != NULL) { | |
FreePool (DataItem->Data.Ptr); | |
} | |
DataItem->Data.Ptr = NewAddress; | |
DataItem->DataSize = DataSize; | |
DataItem->Status = EFI_NOT_READY; | |
// | |
// Trigger DAD, it's an asynchronous process. | |
// | |
IsUpdated = FALSE; | |
for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) { | |
if (Ip6IsOneOfSetAddress (IpSb, &NewAddress->Address, NULL, &CurrentAddrInfo)) { | |
ASSERT (CurrentAddrInfo != NULL); | |
// | |
// Remove this already existing source address from the CurrentSourceList | |
// built before. | |
// | |
Ip6RemoveAddr ( | |
NULL, | |
&CurrentSourceList, | |
&CurrentSourceCount, | |
&CurrentAddrInfo->Address, | |
128 | |
); | |
// | |
// If the new address's prefix length is not specified, just use the previous configured | |
// prefix length for this address. | |
// | |
if (NewAddress->PrefixLength == 0) { | |
NewAddress->PrefixLength = CurrentAddrInfo->PrefixLength; | |
} | |
// | |
// This manual address is already in use, see whether prefix length is changed. | |
// | |
if (NewAddress->PrefixLength != CurrentAddrInfo->PrefixLength) { | |
// | |
// Remove the on-link prefix table, the route entry will be removed | |
// implicitly. | |
// | |
PrefixEntry = Ip6FindPrefixListEntry ( | |
IpSb, | |
TRUE, | |
CurrentAddrInfo->PrefixLength, | |
&CurrentAddrInfo->Address | |
); | |
if (PrefixEntry != NULL) { | |
Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE); | |
} | |
// | |
// Save the prefix length. | |
// | |
CurrentAddrInfo->PrefixLength = NewAddress->PrefixLength; | |
IsUpdated = TRUE; | |
} | |
// | |
// create a new on-link prefix entry. | |
// | |
PrefixEntry = Ip6FindPrefixListEntry ( | |
IpSb, | |
TRUE, | |
NewAddress->PrefixLength, | |
&NewAddress->Address | |
); | |
if (PrefixEntry == NULL) { | |
Ip6CreatePrefixListEntry ( | |
IpSb, | |
TRUE, | |
(UINT32)IP6_INFINIT_LIFETIME, | |
(UINT32)IP6_INFINIT_LIFETIME, | |
NewAddress->PrefixLength, | |
&NewAddress->Address | |
); | |
} | |
CurrentAddrInfo->IsAnycast = NewAddress->IsAnycast; | |
// | |
// Artificially mark this address passed DAD be'coz it is already in use. | |
// | |
Ip6ManualAddrDadCallback (TRUE, &NewAddress->Address, Instance); | |
} else { | |
// | |
// A new address. | |
// | |
IsUpdated = TRUE; | |
// | |
// Set the new address, this will trigger DAD and activate the address if | |
// DAD succeeds. | |
// | |
Ip6SetAddress ( | |
IpSb->DefaultInterface, | |
&NewAddress->Address, | |
NewAddress->IsAnycast, | |
NewAddress->PrefixLength, | |
(UINT32)IP6_INFINIT_LIFETIME, | |
(UINT32)IP6_INFINIT_LIFETIME, | |
Ip6ManualAddrDadCallback, | |
Instance | |
); | |
} | |
} | |
// | |
// Check the CurrentSourceList, it now contains those addresses currently in | |
// use and will be removed. | |
// | |
IpIf = IpSb->DefaultInterface; | |
while (!IsListEmpty (&CurrentSourceList)) { | |
IsUpdated = TRUE; | |
CurrentAddrInfo = NET_LIST_HEAD (&CurrentSourceList, IP6_ADDRESS_INFO, Link); | |
// | |
// This local address is going to be removed, the IP instances that are | |
// currently using it will be destroyed. | |
// | |
Ip6RemoveAddr ( | |
IpSb, | |
&IpIf->AddressList, | |
&IpIf->AddressCount, | |
&CurrentAddrInfo->Address, | |
128 | |
); | |
// | |
// Remove the on-link prefix table, the route entry will be removed | |
// implicitly. | |
// | |
PrefixEntry = Ip6FindPrefixListEntry ( | |
IpSb, | |
TRUE, | |
CurrentAddrInfo->PrefixLength, | |
&CurrentAddrInfo->Address | |
); | |
if (PrefixEntry != NULL) { | |
Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE); | |
} | |
RemoveEntryList (&CurrentAddrInfo->Link); | |
FreePool (CurrentAddrInfo); | |
} | |
if (IsUpdated) { | |
if (DataItem->Status == EFI_NOT_READY) { | |
// | |
// If DAD is disabled on this interface, the configuration process is | |
// actually synchronous, and the data item's status will be changed to | |
// the final status before we reach here, just check it. | |
// | |
Status = EFI_NOT_READY; | |
} else { | |
Status = EFI_SUCCESS; | |
} | |
} else { | |
// | |
// No update is taken, reset the status to success and return EFI_ABORTED. | |
// | |
DataItem->Status = EFI_SUCCESS; | |
Status = EFI_ABORTED; | |
} | |
} else { | |
// | |
// DataSize is 0 and Data is NULL, clean up the manual address. | |
// | |
if (DataItem->Data.Ptr != NULL) { | |
FreePool (DataItem->Data.Ptr); | |
} | |
DataItem->Data.Ptr = NULL; | |
DataItem->DataSize = 0; | |
DataItem->Status = EFI_NOT_FOUND; | |
Ip6CleanDefaultRouterList (IpSb); | |
Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix); | |
Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix); | |
Ip6CleanAssembleTable (&IpSb->Assemble); | |
if (IpSb->LinkLocalOk) { | |
Ip6CreatePrefixListEntry ( | |
IpSb, | |
TRUE, | |
(UINT32)IP6_INFINIT_LIFETIME, | |
(UINT32)IP6_INFINIT_LIFETIME, | |
IP6_LINK_LOCAL_PREFIX_LENGTH, | |
&IpSb->LinkLocalAddr | |
); | |
} | |
Ip6RemoveAddr ( | |
IpSb, | |
&IpSb->DefaultInterface->AddressList, | |
&IpSb->DefaultInterface->AddressCount, | |
NULL, | |
0 | |
); | |
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { | |
// | |
// Remove all pending delay node and DAD entries for the global addresses. | |
// | |
IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); | |
NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) { | |
DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link); | |
if (!NetIp6IsLinkLocalAddr (&DelayNode->AddressInfo->Address)) { | |
RemoveEntryList (&DelayNode->Link); | |
FreePool (DelayNode); | |
} | |
} | |
NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) { | |
DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE); | |
if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) { | |
// | |
// Fail this DAD entry if the address is not link-local. | |
// | |
Ip6OnDADFinished (FALSE, IpIf, DadEntry); | |
} | |
} | |
} | |
} | |
return Status; | |
} | |
/** | |
The work function for EfiIp6ConfigSetData() to set the gateway addresses manually | |
for the EFI IPv6 network stack that is running on the communication device that | |
this EFI IPv6 Configuration Protocol manages. It is not configurable when the policy is | |
Ip6ConfigPolicyAutomatic. The gateway addresses must be unicast IPv6 addresses. | |
@param[in] Instance The pointer to the IP6 config instance data. | |
@param[in] DataSize The size of the buffer pointed to by Data in bytes. | |
@param[in] Data The data buffer to set. This points to an array of | |
EFI_IPv6_ADDRESS instances. | |
@retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. | |
@retval EFI_WRITE_PROTECTED The specified configuration data cannot be set | |
under the current policy. | |
@retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource to complete the operation. | |
@retval EFI_ABORTED The manual gateway addresses to be set equal the | |
current configuration. | |
@retval EFI_SUCCESS The specified configuration data for the EFI IPv6 | |
network stack was set. | |
**/ | |
EFI_STATUS | |
Ip6ConfigSetGateway ( | |
IN IP6_CONFIG_INSTANCE *Instance, | |
IN UINTN DataSize, | |
IN VOID *Data | |
) | |
{ | |
UINTN Index1; | |
UINTN Index2; | |
EFI_IPv6_ADDRESS *OldGateway; | |
EFI_IPv6_ADDRESS *NewGateway; | |
UINTN OldGatewayCount; | |
UINTN NewGatewayCount; | |
IP6_CONFIG_DATA_ITEM *Item; | |
BOOLEAN OneRemoved; | |
BOOLEAN OneAdded; | |
IP6_SERVICE *IpSb; | |
IP6_DEFAULT_ROUTER *DefaultRouter; | |
VOID *Tmp; | |
OldGateway = NULL; | |
NewGateway = NULL; | |
Item = NULL; | |
DefaultRouter = NULL; | |
Tmp = NULL; | |
OneRemoved = FALSE; | |
OneAdded = FALSE; | |
if ((DataSize != 0) && (DataSize % sizeof (EFI_IPv6_ADDRESS) != 0)) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Instance->Policy != Ip6ConfigPolicyManual) { | |
return EFI_WRITE_PROTECTED; | |
} | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
Item = &Instance->DataItem[Ip6ConfigDataTypeGateway]; | |
OldGateway = Item->Data.Gateway; | |
OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS); | |
for (Index1 = 0; Index1 < OldGatewayCount; Index1++) { | |
// | |
// Remove this default router. | |
// | |
DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1); | |
if (DefaultRouter != NULL) { | |
Ip6DestroyDefaultRouter (IpSb, DefaultRouter); | |
OneRemoved = TRUE; | |
} | |
} | |
if ((Data != NULL) && (DataSize != 0)) { | |
NewGateway = (EFI_IPv6_ADDRESS *)Data; | |
NewGatewayCount = DataSize / sizeof (EFI_IPv6_ADDRESS); | |
for (Index1 = 0; Index1 < NewGatewayCount; Index1++) { | |
if (!NetIp6IsValidUnicast (NewGateway + Index1)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) { | |
if (EFI_IP6_EQUAL (NewGateway + Index1, NewGateway + Index2)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
} | |
if (NewGatewayCount != OldGatewayCount) { | |
Tmp = AllocatePool (DataSize); | |
if (Tmp == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} else { | |
Tmp = NULL; | |
} | |
for (Index1 = 0; Index1 < NewGatewayCount; Index1++) { | |
DefaultRouter = Ip6FindDefaultRouter (IpSb, NewGateway + Index1); | |
if (DefaultRouter == NULL) { | |
Ip6CreateDefaultRouter (IpSb, NewGateway + Index1, IP6_INF_ROUTER_LIFETIME); | |
OneAdded = TRUE; | |
} | |
} | |
if (!OneRemoved && !OneAdded) { | |
Item->Status = EFI_SUCCESS; | |
return EFI_ABORTED; | |
} else { | |
if (Tmp != NULL) { | |
if (Item->Data.Ptr != NULL) { | |
FreePool (Item->Data.Ptr); | |
} | |
Item->Data.Ptr = Tmp; | |
} | |
CopyMem (Item->Data.Ptr, Data, DataSize); | |
Item->DataSize = DataSize; | |
Item->Status = EFI_SUCCESS; | |
return EFI_SUCCESS; | |
} | |
} else { | |
// | |
// DataSize is 0 and Data is NULL, clean up the Gateway address. | |
// | |
if (Item->Data.Ptr != NULL) { | |
FreePool (Item->Data.Ptr); | |
} | |
Item->Data.Ptr = NULL; | |
Item->DataSize = 0; | |
Item->Status = EFI_NOT_FOUND; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
The work function for EfiIp6ConfigSetData() to set the DNS server list for the | |
EFI IPv6 network stack running on the communication device that this EFI IPv6 | |
Configuration Protocol manages. It is not configurable when the policy is | |
Ip6ConfigPolicyAutomatic. The DNS server addresses must be unicast IPv6 addresses. | |
@param[in] Instance The pointer to the IP6 config instance data. | |
@param[in] DataSize The size of the buffer pointed to by Data in bytes. | |
@param[in] Data The data buffer to set, points to an array of | |
EFI_IPv6_ADDRESS instances. | |
@retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. | |
@retval EFI_WRITE_PROTECTED The specified configuration data cannot be set | |
under the current policy. | |
@retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. | |
@retval EFI_ABORTED The DNS server addresses to be set equal the current | |
configuration. | |
@retval EFI_SUCCESS The specified configuration data for the EFI IPv6 | |
network stack was set. | |
**/ | |
EFI_STATUS | |
Ip6ConfigSetDnsServer ( | |
IN IP6_CONFIG_INSTANCE *Instance, | |
IN UINTN DataSize, | |
IN VOID *Data | |
) | |
{ | |
UINTN OldIndex; | |
UINTN NewIndex; | |
EFI_IPv6_ADDRESS *OldDns; | |
EFI_IPv6_ADDRESS *NewDns; | |
UINTN OldDnsCount; | |
UINTN NewDnsCount; | |
IP6_CONFIG_DATA_ITEM *Item; | |
BOOLEAN OneAdded; | |
VOID *Tmp; | |
OldDns = NULL; | |
NewDns = NULL; | |
Item = NULL; | |
Tmp = NULL; | |
if ((DataSize != 0) && (DataSize % sizeof (EFI_IPv6_ADDRESS) != 0)) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Instance->Policy != Ip6ConfigPolicyManual) { | |
return EFI_WRITE_PROTECTED; | |
} | |
Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; | |
if ((Data != NULL) && (DataSize != 0)) { | |
NewDns = (EFI_IPv6_ADDRESS *)Data; | |
OldDns = Item->Data.DnsServers; | |
NewDnsCount = DataSize / sizeof (EFI_IPv6_ADDRESS); | |
OldDnsCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS); | |
OneAdded = FALSE; | |
if (NewDnsCount != OldDnsCount) { | |
Tmp = AllocatePool (DataSize); | |
if (Tmp == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} else { | |
Tmp = NULL; | |
} | |
for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) { | |
if (!NetIp6IsValidUnicast (NewDns + NewIndex)) { | |
// | |
// The dns server address must be unicast. | |
// | |
if (Tmp != NULL) { | |
FreePool (Tmp); | |
} | |
return EFI_INVALID_PARAMETER; | |
} | |
if (OneAdded) { | |
// | |
// If any address in the new setting is not in the old settings, skip the | |
// comparision below. | |
// | |
continue; | |
} | |
for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) { | |
if (EFI_IP6_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) { | |
// | |
// If found break out. | |
// | |
break; | |
} | |
} | |
if (OldIndex == OldDnsCount) { | |
OneAdded = TRUE; | |
} | |
} | |
if (!OneAdded && (DataSize == Item->DataSize)) { | |
// | |
// No new item is added and the size is the same. | |
// | |
Item->Status = EFI_SUCCESS; | |
return EFI_ABORTED; | |
} else { | |
if (Tmp != NULL) { | |
if (Item->Data.Ptr != NULL) { | |
FreePool (Item->Data.Ptr); | |
} | |
Item->Data.Ptr = Tmp; | |
} | |
CopyMem (Item->Data.Ptr, Data, DataSize); | |
Item->DataSize = DataSize; | |
Item->Status = EFI_SUCCESS; | |
} | |
} else { | |
// | |
// DataSize is 0 and Data is NULL, clean up the DnsServer address. | |
// | |
if (Item->Data.Ptr != NULL) { | |
FreePool (Item->Data.Ptr); | |
} | |
Item->Data.Ptr = NULL; | |
Item->DataSize = 0; | |
Item->Status = EFI_NOT_FOUND; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Generate the operational state of the interface this IP6 config instance manages | |
and output in EFI_IP6_CONFIG_INTERFACE_INFO. | |
@param[in] IpSb The pointer to the IP6 service binding instance. | |
@param[out] IfInfo The pointer to the IP6 configuration interface information structure. | |
**/ | |
VOID | |
Ip6ConfigInitIfInfo ( | |
IN IP6_SERVICE *IpSb, | |
OUT EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo | |
) | |
{ | |
UnicodeSPrint ( | |
IfInfo->Name, | |
sizeof (IfInfo->Name), | |
L"eth%d", | |
IpSb->Ip6ConfigInstance.IfIndex | |
); | |
IfInfo->IfType = IpSb->SnpMode.IfType; | |
IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize; | |
CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize); | |
} | |
/** | |
Parse DHCPv6 reply packet to get the DNS server list. | |
It is the work function for Ip6ConfigOnDhcp6Reply and Ip6ConfigOnDhcp6Event. | |
@param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL instance. | |
@param[in, out] Instance The pointer to the IP6 configuration instance data. | |
@param[in] Reply The pointer to the DHCPv6 reply packet. | |
@retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. | |
@retval EFI_NOT_READY The reply packet does not contain the DNS server option, or | |
the DNS server address is not valid. | |
**/ | |
EFI_STATUS | |
Ip6ConfigParseDhcpReply ( | |
IN EFI_DHCP6_PROTOCOL *Dhcp6, | |
IN OUT IP6_CONFIG_INSTANCE *Instance, | |
IN EFI_DHCP6_PACKET *Reply | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 OptCount; | |
EFI_DHCP6_PACKET_OPTION **OptList; | |
UINT16 OpCode; | |
UINT16 Length; | |
UINTN Index; | |
UINTN Index2; | |
EFI_IPv6_ADDRESS *DnsServer; | |
IP6_CONFIG_DATA_ITEM *Item; | |
// | |
// A DHCPv6 reply packet is received as the response to our InfoRequest | |
// packet. | |
// | |
OptCount = 0; | |
Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, NULL); | |
if (Status != EFI_BUFFER_TOO_SMALL) { | |
return EFI_NOT_READY; | |
} | |
OptList = AllocatePool (OptCount * sizeof (EFI_DHCP6_PACKET_OPTION *)); | |
if (OptList == NULL) { | |
return EFI_NOT_READY; | |
} | |
Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, OptList); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_NOT_READY; | |
goto ON_EXIT; | |
} | |
Status = EFI_SUCCESS; | |
for (Index = 0; Index < OptCount; Index++) { | |
// | |
// Go through all the options to check the ones we are interested in. | |
// The OpCode and Length are in network byte-order and may not be naturally | |
// aligned. | |
// | |
CopyMem (&OpCode, &OptList[Index]->OpCode, sizeof (OpCode)); | |
OpCode = NTOHS (OpCode); | |
if (OpCode == DHCP6_OPT_DNS_SERVERS) { | |
CopyMem (&Length, &OptList[Index]->OpLen, sizeof (Length)); | |
Length = NTOHS (Length); | |
if ((Length == 0) || ((Length % sizeof (EFI_IPv6_ADDRESS)) != 0)) { | |
// | |
// The length should be a multiple of 16 bytes. | |
// | |
Status = EFI_NOT_READY; | |
break; | |
} | |
// | |
// Validate the DnsServers: whether they are unicast addresses. | |
// | |
DnsServer = (EFI_IPv6_ADDRESS *)OptList[Index]->Data; | |
for (Index2 = 0; Index2 < Length / sizeof (EFI_IPv6_ADDRESS); Index2++) { | |
if (!NetIp6IsValidUnicast (DnsServer)) { | |
Status = EFI_NOT_READY; | |
goto ON_EXIT; | |
} | |
DnsServer++; | |
} | |
Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; | |
if (Item->DataSize != Length) { | |
if (Item->Data.Ptr != NULL) { | |
FreePool (Item->Data.Ptr); | |
} | |
Item->Data.Ptr = AllocatePool (Length); | |
ASSERT (Item->Data.Ptr != NULL); | |
} | |
CopyMem (Item->Data.Ptr, OptList[Index]->Data, Length); | |
Item->DataSize = Length; | |
Item->Status = EFI_SUCCESS; | |
// | |
// Signal the waiting events. | |
// | |
NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL); | |
break; | |
} | |
} | |
ON_EXIT: | |
FreePool (OptList); | |
return Status; | |
} | |
/** | |
The callback function for Ip6SetAddr. The prototype is defined | |
as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed | |
on the tentative address by DHCPv6 in Ip6ConfigOnDhcp6Event(). | |
@param[in] IsDadPassed If TRUE, Duplicate Address Detection passes. | |
@param[in] TargetAddress The tentative IPv6 address to be checked. | |
@param[in] Context Pointer to the IP6 configuration instance data. | |
**/ | |
VOID | |
Ip6ConfigSetStatefulAddrCallback ( | |
IN BOOLEAN IsDadPassed, | |
IN EFI_IPv6_ADDRESS *TargetAddress, | |
IN VOID *Context | |
) | |
{ | |
IP6_CONFIG_INSTANCE *Instance; | |
Instance = (IP6_CONFIG_INSTANCE *)Context; | |
NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); | |
// | |
// We should record the addresses that fail the DAD, and DECLINE them. | |
// | |
if (IsDadPassed) { | |
// | |
// Decrease the count, no interests in those passed DAD. | |
// | |
if (Instance->FailedIaAddressCount > 0 ) { | |
Instance->FailedIaAddressCount--; | |
} | |
} else { | |
// | |
// Record it. | |
// | |
IP6_COPY_ADDRESS (Instance->DeclineAddress + Instance->DeclineAddressCount, TargetAddress); | |
Instance->DeclineAddressCount++; | |
} | |
if (Instance->FailedIaAddressCount == Instance->DeclineAddressCount) { | |
// | |
// The checking on all addresses are finished. | |
// | |
if (Instance->DeclineAddressCount != 0) { | |
// | |
// Decline those duplicates. | |
// | |
if (Instance->Dhcp6 != NULL) { | |
Instance->Dhcp6->Decline ( | |
Instance->Dhcp6, | |
Instance->DeclineAddressCount, | |
Instance->DeclineAddress | |
); | |
} | |
} | |
if (Instance->DeclineAddress != NULL) { | |
FreePool (Instance->DeclineAddress); | |
} | |
Instance->DeclineAddress = NULL; | |
Instance->DeclineAddressCount = 0; | |
} | |
} | |
/** | |
The event handle routine when DHCPv6 process is finished or is updated. | |
@param[in] Event Not used. | |
@param[in] Context The pointer to the IP6 configuration instance data. | |
**/ | |
VOID | |
EFIAPI | |
Ip6ConfigOnDhcp6Event ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
IP6_CONFIG_INSTANCE *Instance; | |
EFI_DHCP6_PROTOCOL *Dhcp6; | |
EFI_STATUS Status; | |
EFI_DHCP6_MODE_DATA Dhcp6ModeData; | |
EFI_DHCP6_IA *Ia; | |
EFI_DHCP6_IA_ADDRESS *IaAddr; | |
UINT32 Index; | |
IP6_SERVICE *IpSb; | |
IP6_ADDRESS_INFO *AddrInfo; | |
IP6_INTERFACE *IpIf; | |
Instance = (IP6_CONFIG_INSTANCE *)Context; | |
if ((Instance->Policy != Ip6ConfigPolicyAutomatic) || Instance->OtherInfoOnly) { | |
// | |
// IPv6 is not operating in the automatic policy now or | |
// the DHCPv6 information request message exchange is aborted. | |
// | |
return; | |
} | |
// | |
// The stateful address autoconfiguration is done or updated. | |
// | |
Dhcp6 = Instance->Dhcp6; | |
Status = Dhcp6->GetModeData (Dhcp6, &Dhcp6ModeData, NULL); | |
if (EFI_ERROR (Status)) { | |
return; | |
} | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
IpIf = IpSb->DefaultInterface; | |
Ia = Dhcp6ModeData.Ia; | |
IaAddr = Ia->IaAddress; | |
if (Instance->DeclineAddress != NULL) { | |
FreePool (Instance->DeclineAddress); | |
} | |
Instance->DeclineAddress = (EFI_IPv6_ADDRESS *)AllocatePool (Ia->IaAddressCount * sizeof (EFI_IPv6_ADDRESS)); | |
if (Instance->DeclineAddress == NULL) { | |
goto ON_EXIT; | |
} | |
Instance->FailedIaAddressCount = Ia->IaAddressCount; | |
Instance->DeclineAddressCount = 0; | |
for (Index = 0; Index < Ia->IaAddressCount; Index++, IaAddr++) { | |
if ((Ia->IaAddress[Index].ValidLifetime != 0) && (Ia->State == Dhcp6Bound)) { | |
// | |
// Set this address, either it's a new address or with updated lifetimes. | |
// An appropriate prefix length will be set. | |
// | |
Ip6SetAddress ( | |
IpIf, | |
&IaAddr->IpAddress, | |
FALSE, | |
0, | |
IaAddr->ValidLifetime, | |
IaAddr->PreferredLifetime, | |
Ip6ConfigSetStatefulAddrCallback, | |
Instance | |
); | |
} else { | |
// | |
// discard this address, artificially decrease the count as if this address | |
// passed DAD. | |
// | |
if (Ip6IsOneOfSetAddress (IpSb, &IaAddr->IpAddress, NULL, &AddrInfo)) { | |
ASSERT (AddrInfo != NULL); | |
Ip6RemoveAddr ( | |
IpSb, | |
&IpIf->AddressList, | |
&IpIf->AddressCount, | |
&AddrInfo->Address, | |
AddrInfo->PrefixLength | |
); | |
} | |
if (Instance->FailedIaAddressCount > 0) { | |
Instance->FailedIaAddressCount--; | |
} | |
} | |
} | |
// | |
// Parse the Reply packet to get the options we need. | |
// | |
if (Dhcp6ModeData.Ia->ReplyPacket != NULL) { | |
Ip6ConfigParseDhcpReply (Dhcp6, Instance, Dhcp6ModeData.Ia->ReplyPacket); | |
} | |
ON_EXIT: | |
FreePool (Dhcp6ModeData.ClientId); | |
FreePool (Dhcp6ModeData.Ia); | |
} | |
/** | |
The event process routine when the DHCPv6 server is answered with a reply packet | |
for an information request. | |
@param[in] This Points to the EFI_DHCP6_PROTOCOL. | |
@param[in] Context The pointer to the IP6 configuration instance data. | |
@param[in] Packet The DHCPv6 reply packet. | |
@retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. | |
@retval EFI_NOT_READY The reply packet does not contain the DNS server option, or | |
the DNS server address is not valid. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Ip6ConfigOnDhcp6Reply ( | |
IN EFI_DHCP6_PROTOCOL *This, | |
IN VOID *Context, | |
IN EFI_DHCP6_PACKET *Packet | |
) | |
{ | |
return Ip6ConfigParseDhcpReply (This, (IP6_CONFIG_INSTANCE *)Context, Packet); | |
} | |
/** | |
The event process routine when the DHCPv6 service binding protocol is installed | |
in the system. | |
@param[in] Event Not used. | |
@param[in] Context The pointer to the IP6 config instance data. | |
**/ | |
VOID | |
EFIAPI | |
Ip6ConfigOnDhcp6SbInstalled ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
IP6_CONFIG_INSTANCE *Instance; | |
Instance = (IP6_CONFIG_INSTANCE *)Context; | |
if ((Instance->Dhcp6Handle != NULL) || (Instance->Policy != Ip6ConfigPolicyAutomatic)) { | |
// | |
// The DHCP6 child is already created or the policy is no longer AUTOMATIC. | |
// | |
return; | |
} | |
Ip6ConfigStartStatefulAutoConfig (Instance, Instance->OtherInfoOnly); | |
} | |
/** | |
Set the configuration for the EFI IPv6 network stack running on the communication | |
device this EFI IPv6 Configuration Protocol instance manages. | |
This function is used to set the configuration data of type DataType for the EFI | |
IPv6 network stack that is running on the communication device that this EFI IPv6 | |
Configuration Protocol instance manages. | |
DataSize is used to calculate the count of structure instances in the Data for | |
a DataType in which multiple structure instances are allowed. | |
This function is always non-blocking. When setting some type of configuration data, | |
an asynchronous process is invoked to check the correctness of the data, such as | |
performing Duplicate Address Detection on the manually set local IPv6 addresses. | |
EFI_NOT_READY is returned immediately to indicate that such an asynchronous process | |
is invoked, and the process is not finished yet. The caller wanting to get the result | |
of the asynchronous process is required to call RegisterDataNotify() to register an | |
event on the specified configuration data. Once the event is signaled, the caller | |
can call GetData() to obtain the configuration data and know the result. | |
For other types of configuration data that do not require an asynchronous configuration | |
process, the result of the operation is immediately returned. | |
@param[in] This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance. | |
@param[in] DataType The type of data to set. | |
@param[in] DataSize Size of the buffer pointed to by Data in bytes. | |
@param[in] Data The data buffer to set. The type of the data buffer is | |
associated with the DataType. | |
@retval EFI_SUCCESS The specified configuration data for the EFI IPv6 | |
network stack was set successfully. | |
@retval EFI_INVALID_PARAMETER One or more of the following are TRUE: | |
- This is NULL. | |
- One or more fields in Data and DataSizedo not match the | |
requirement of the data type indicated by DataType. | |
@retval EFI_WRITE_PROTECTED The specified configuration data is read-only or the specified | |
configuration data cannot be set under the current policy. | |
@retval EFI_ACCESS_DENIED Another set operation on the specified configuration | |
data is already in process. | |
@retval EFI_NOT_READY An asynchronous process was invoked to set the specified | |
configuration data, and the process is not finished yet. | |
@retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type | |
indicated by DataType. | |
@retval EFI_UNSUPPORTED This DataType is not supported. | |
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. | |
@retval EFI_DEVICE_ERROR An unexpected system error or network error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiIp6ConfigSetData ( | |
IN EFI_IP6_CONFIG_PROTOCOL *This, | |
IN EFI_IP6_CONFIG_DATA_TYPE DataType, | |
IN UINTN DataSize, | |
IN VOID *Data | |
) | |
{ | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
IP6_CONFIG_INSTANCE *Instance; | |
IP6_SERVICE *IpSb; | |
if ((This == NULL) || ((Data == NULL) && (DataSize != 0)) || ((Data != NULL) && (DataSize == 0))) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (DataType >= Ip6ConfigDataTypeMaximum) { | |
return EFI_UNSUPPORTED; | |
} | |
Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); | |
if (IpSb->LinkLocalDadFail) { | |
return EFI_DEVICE_ERROR; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
Status = Instance->DataItem[DataType].Status; | |
if (Status != EFI_NOT_READY) { | |
if (Instance->DataItem[DataType].SetData == NULL) { | |
// | |
// This type of data is readonly. | |
// | |
Status = EFI_WRITE_PROTECTED; | |
} else { | |
Status = Instance->DataItem[DataType].SetData (Instance, DataSize, Data); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Fire up the events registered with this type of data. | |
// | |
NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL); | |
Ip6ConfigWriteConfigData (IpSb->MacString, Instance); | |
} else if (Status == EFI_ABORTED) { | |
// | |
// The SetData is aborted because the data to set is the same with | |
// the one maintained. | |
// | |
Status = EFI_SUCCESS; | |
NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL); | |
} | |
} | |
} else { | |
// | |
// Another asynchronous process is on the way. | |
// | |
Status = EFI_ACCESS_DENIED; | |
} | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Get the configuration data for the EFI IPv6 network stack running on the communication | |
device that this EFI IPv6 Configuration Protocol instance manages. | |
This function returns the configuration data of type DataType for the EFI IPv6 network | |
stack running on the communication device that this EFI IPv6 Configuration Protocol instance | |
manages. | |
The caller is responsible for allocating the buffer used to return the specified | |
configuration data. The required size will be returned to the caller if the size of | |
the buffer is too small. | |
EFI_NOT_READY is returned if the specified configuration data is not ready due to an | |
asynchronous configuration process already in progress. The caller can call RegisterDataNotify() | |
to register an event on the specified configuration data. Once the asynchronous configuration | |
process is finished, the event will be signaled, and a subsequent GetData() call will return | |
the specified configuration data. | |
@param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance. | |
@param[in] DataType The type of data to get. | |
@param[in, out] DataSize On input, in bytes, the size of Data. On output, in bytes, the | |
size of buffer required to store the specified configuration data. | |
@param[in] Data The data buffer in which the configuration data is returned. The | |
type of the data buffer is associated with the DataType. | |
This is an optional parameter that may be NULL. | |
@retval EFI_SUCCESS The specified configuration data was obtained successfully. | |
@retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: | |
- This is NULL. | |
- DataSize is NULL. | |
- Data is NULL if *DataSize is not zero. | |
@retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified configuration data, | |
and the required size is returned in DataSize. | |
@retval EFI_NOT_READY The specified configuration data is not ready due to an | |
asynchronous configuration process already in progress. | |
@retval EFI_NOT_FOUND The specified configuration data is not found. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiIp6ConfigGetData ( | |
IN EFI_IP6_CONFIG_PROTOCOL *This, | |
IN EFI_IP6_CONFIG_DATA_TYPE DataType, | |
IN OUT UINTN *DataSize, | |
IN VOID *Data OPTIONAL | |
) | |
{ | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
IP6_CONFIG_INSTANCE *Instance; | |
IP6_CONFIG_DATA_ITEM *DataItem; | |
if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (DataType >= Ip6ConfigDataTypeMaximum) { | |
return EFI_NOT_FOUND; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); | |
DataItem = &Instance->DataItem[DataType]; | |
Status = Instance->DataItem[DataType].Status; | |
if (!EFI_ERROR (Status)) { | |
if (DataItem->GetData != NULL) { | |
Status = DataItem->GetData (Instance, DataSize, Data); | |
} else if (*DataSize < Instance->DataItem[DataType].DataSize) { | |
// | |
// Update the buffer length. | |
// | |
*DataSize = Instance->DataItem[DataType].DataSize; | |
Status = EFI_BUFFER_TOO_SMALL; | |
} else { | |
*DataSize = Instance->DataItem[DataType].DataSize; | |
CopyMem (Data, Instance->DataItem[DataType].Data.Ptr, *DataSize); | |
} | |
} | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Register an event that is signaled whenever a configuration process on the specified | |
configuration data is done. | |
This function registers an event that is to be signaled whenever a configuration | |
process on the specified configuration data is performed. An event can be registered | |
for a different DataType simultaneously. The caller is responsible for determining | |
which type of configuration data causes the signaling of the event in such an event. | |
@param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance. | |
@param[in] DataType The type of data to unregister the event for. | |
@param[in] Event The event to register. | |
@retval EFI_SUCCESS The notification event for the specified configuration data is | |
registered. | |
@retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. | |
@retval EFI_UNSUPPORTED The configuration data type specified by DataType is not | |
supported. | |
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. | |
@retval EFI_ACCESS_DENIED The Event is already registered for the DataType. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiIp6ConfigRegisterDataNotify ( | |
IN EFI_IP6_CONFIG_PROTOCOL *This, | |
IN EFI_IP6_CONFIG_DATA_TYPE DataType, | |
IN EFI_EVENT Event | |
) | |
{ | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
IP6_CONFIG_INSTANCE *Instance; | |
NET_MAP *EventMap; | |
NET_MAP_ITEM *Item; | |
if ((This == NULL) || (Event == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (DataType >= Ip6ConfigDataTypeMaximum) { | |
return EFI_UNSUPPORTED; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); | |
EventMap = &Instance->DataItem[DataType].EventMap; | |
// | |
// Check whether this event is already registered for this DataType. | |
// | |
Item = NetMapFindKey (EventMap, Event); | |
if (Item == NULL) { | |
Status = NetMapInsertTail (EventMap, Event, NULL); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_OUT_OF_RESOURCES; | |
} | |
} else { | |
Status = EFI_ACCESS_DENIED; | |
} | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Remove a previously registered event for the specified configuration data. | |
@param This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance. | |
@param DataType The type of data to remove from the previously | |
registered event. | |
@param Event The event to be unregistered. | |
@retval EFI_SUCCESS The event registered for the specified | |
configuration data was removed. | |
@retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. | |
@retval EFI_NOT_FOUND The Event has not been registered for the | |
specified DataType. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiIp6ConfigUnregisterDataNotify ( | |
IN EFI_IP6_CONFIG_PROTOCOL *This, | |
IN EFI_IP6_CONFIG_DATA_TYPE DataType, | |
IN EFI_EVENT Event | |
) | |
{ | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
IP6_CONFIG_INSTANCE *Instance; | |
NET_MAP_ITEM *Item; | |
if ((This == NULL) || (Event == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (DataType >= Ip6ConfigDataTypeMaximum) { | |
return EFI_NOT_FOUND; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); | |
Item = NetMapFindKey (&Instance->DataItem[DataType].EventMap, Event); | |
if (Item != NULL) { | |
NetMapRemoveItem (&Instance->DataItem[DataType].EventMap, Item, NULL); | |
Status = EFI_SUCCESS; | |
} else { | |
Status = EFI_NOT_FOUND; | |
} | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Initialize an IP6_CONFIG_INSTANCE. | |
@param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. | |
@retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully. | |
**/ | |
EFI_STATUS | |
Ip6ConfigInitInstance ( | |
OUT IP6_CONFIG_INSTANCE *Instance | |
) | |
{ | |
IP6_SERVICE *IpSb; | |
IP6_CONFIG_INSTANCE *TmpInstance; | |
LIST_ENTRY *Entry; | |
EFI_STATUS Status; | |
UINTN Index; | |
UINT16 IfIndex; | |
IP6_CONFIG_DATA_ITEM *DataItem; | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
Instance->Signature = IP6_CONFIG_INSTANCE_SIGNATURE; | |
// | |
// Determine the index of this interface. | |
// | |
IfIndex = 0; | |
NET_LIST_FOR_EACH (Entry, &mIp6ConfigInstanceList) { | |
TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_CONFIG_INSTANCE, Link, IP6_CONFIG_INSTANCE_SIGNATURE); | |
if (TmpInstance->IfIndex > IfIndex) { | |
// | |
// There is a sequence hole because some interface is down. | |
// | |
break; | |
} | |
IfIndex++; | |
} | |
Instance->IfIndex = IfIndex; | |
NetListInsertBefore (Entry, &Instance->Link); | |
for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { | |
// | |
// Initialize the event map for each data item. | |
// | |
NetMapInit (&Instance->DataItem[Index].EventMap); | |
} | |
// | |
// Initialize the NET_MAPs used for DAD on manually configured source addresses. | |
// | |
NetMapInit (&Instance->DadFailedMap); | |
NetMapInit (&Instance->DadPassedMap); | |
// | |
// Initialize each data type: associate storage and set data size for the | |
// fixed size data types, hook the SetData function, set the data attribute. | |
// | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo]; | |
DataItem->GetData = Ip6ConfigGetIfInfo; | |
DataItem->Data.Ptr = &Instance->InterfaceInfo; | |
DataItem->DataSize = sizeof (Instance->InterfaceInfo); | |
SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE); | |
Ip6ConfigInitIfInfo (IpSb, &Instance->InterfaceInfo); | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId]; | |
DataItem->SetData = Ip6ConfigSetAltIfId; | |
DataItem->Data.Ptr = &Instance->AltIfId; | |
DataItem->DataSize = sizeof (Instance->AltIfId); | |
DataItem->Status = EFI_NOT_FOUND; | |
SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypePolicy]; | |
DataItem->SetData = Ip6ConfigSetPolicy; | |
DataItem->Data.Ptr = &Instance->Policy; | |
DataItem->DataSize = sizeof (Instance->Policy); | |
Instance->Policy = Ip6ConfigPolicyManual; | |
SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits]; | |
DataItem->SetData = Ip6ConfigSetDadXmits; | |
DataItem->Data.Ptr = &Instance->DadXmits; | |
DataItem->DataSize = sizeof (Instance->DadXmits); | |
Instance->DadXmits.DupAddrDetectTransmits = IP6_CONFIG_DEFAULT_DAD_XMITS; | |
SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; | |
DataItem->SetData = Ip6ConfigSetManualAddress; | |
DataItem->Status = EFI_NOT_FOUND; | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway]; | |
DataItem->SetData = Ip6ConfigSetGateway; | |
DataItem->Status = EFI_NOT_FOUND; | |
DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; | |
DataItem->SetData = Ip6ConfigSetDnsServer; | |
DataItem->Status = EFI_NOT_FOUND; | |
// | |
// Create the event used for DHCP. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
Ip6ConfigOnDhcp6Event, | |
Instance, | |
&Instance->Dhcp6Event | |
); | |
ASSERT_EFI_ERROR (Status); | |
Instance->Configured = TRUE; | |
// | |
// Try to read the config data from NV variable. | |
// | |
Status = Ip6ConfigReadConfigData (IpSb->MacString, Instance); | |
if (Status == EFI_NOT_FOUND) { | |
// | |
// The NV variable is not set, so generate a random IAID, and write down the | |
// fresh new configuration as the NV variable now. | |
// | |
Instance->IaId = NET_RANDOM (NetRandomInitSeed ()); | |
for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) { | |
Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31)); | |
} | |
Ip6ConfigWriteConfigData (IpSb->MacString, Instance); | |
} else if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Instance->Ip6Config.SetData = EfiIp6ConfigSetData; | |
Instance->Ip6Config.GetData = EfiIp6ConfigGetData; | |
Instance->Ip6Config.RegisterDataNotify = EfiIp6ConfigRegisterDataNotify; | |
Instance->Ip6Config.UnregisterDataNotify = EfiIp6ConfigUnregisterDataNotify; | |
// | |
// Publish the IP6 configuration form | |
// | |
return Ip6ConfigFormInit (Instance); | |
} | |
/** | |
Release an IP6_CONFIG_INSTANCE. | |
@param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. | |
**/ | |
VOID | |
Ip6ConfigCleanInstance ( | |
IN OUT IP6_CONFIG_INSTANCE *Instance | |
) | |
{ | |
UINTN Index; | |
IP6_CONFIG_DATA_ITEM *DataItem; | |
if (Instance->DeclineAddress != NULL) { | |
FreePool (Instance->DeclineAddress); | |
} | |
if (!Instance->Configured) { | |
return; | |
} | |
if (Instance->Dhcp6Handle != NULL) { | |
Ip6ConfigDestroyDhcp6 (Instance); | |
} | |
// | |
// Close the event. | |
// | |
if (Instance->Dhcp6Event != NULL) { | |
gBS->CloseEvent (Instance->Dhcp6Event); | |
} | |
NetMapClean (&Instance->DadPassedMap); | |
NetMapClean (&Instance->DadFailedMap); | |
for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { | |
DataItem = &Instance->DataItem[Index]; | |
if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) { | |
if (DataItem->Data.Ptr != NULL) { | |
FreePool (DataItem->Data.Ptr); | |
} | |
DataItem->Data.Ptr = NULL; | |
DataItem->DataSize = 0; | |
} | |
NetMapClean (&Instance->DataItem[Index].EventMap); | |
} | |
Ip6ConfigFormUnload (Instance); | |
RemoveEntryList (&Instance->Link); | |
} | |
/** | |
Destroy the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources. | |
@param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. | |
@retval EFI_SUCCESS The child was successfully destroyed. | |
@retval Others Failed to destroy the child. | |
**/ | |
EFI_STATUS | |
Ip6ConfigDestroyDhcp6 ( | |
IN OUT IP6_CONFIG_INSTANCE *Instance | |
) | |
{ | |
IP6_SERVICE *IpSb; | |
EFI_STATUS Status; | |
EFI_DHCP6_PROTOCOL *Dhcp6; | |
Dhcp6 = Instance->Dhcp6; | |
ASSERT (Dhcp6 != NULL); | |
Dhcp6->Stop (Dhcp6); | |
Dhcp6->Configure (Dhcp6, NULL); | |
Instance->Dhcp6 = NULL; | |
IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); | |
// | |
// Close DHCPv6 protocol and destroy the child. | |
// | |
Status = gBS->CloseProtocol ( | |
Instance->Dhcp6Handle, | |
&gEfiDhcp6ProtocolGuid, | |
IpSb->Image, | |
IpSb->Controller | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = NetLibDestroyServiceChild ( | |
IpSb->Controller, | |
IpSb->Image, | |
&gEfiDhcp6ServiceBindingProtocolGuid, | |
Instance->Dhcp6Handle | |
); | |
Instance->Dhcp6Handle = NULL; | |
return Status; | |
} |