The default policy for Ip6Config is Ip6ConfigPolicyAutomatic, which results in all NIC ports starting SARR process when it receives RA message with M flag from IPv6 router. So, this patch is used to changes the the default IPv6 config policy to Ip6ConfigPolicyManualand also defer the SetData operation after Ip6ConfigProtocol installed. This update let the other platform drivers have chance to change the default config data by consume Ip6ConfigProtocol. Cc: Subramanian Sriram <sriram-s@hpe.com> Cc: El-Haj-Mahmoud Samer <samer.el-haj-mahmoud@hpe.com> Cc: Ye Ting <ting.ye@intel.com> Cc: Fu Siyuan <siyuan.fu@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jiaxin Wu <jiaxin.wu@intel.com> Reviewed-by: Subramanian Sriram <sriram-s@hpe.com> Reviewed-by: Fu Siyuan <siyuan.fu@intel.com> Reviewed-by: Ye Ting <ting.ye@intel.com>
		
			
				
	
	
		
			2389 lines
		
	
	
		
			75 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2389 lines
		
	
	
		
			75 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   The implementation of EFI IPv6 Configuration Protocol.
 | |
| 
 | |
|   Copyright (c) 2009 - 2016, 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 "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;
 | |
|   
 | |
|   //
 | |
|   // 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
 | |
|       );
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // All IPv6 children that use global unicast address as it's source address
 | |
|   // should be destryoed 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
 | |
|     );
 | |
| 
 | |
|   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 paramters 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 (IP6_CONFIG_DHCP6_OPTION_ORO);
 | |
|   Oro->OpLen                  = HTONS (2);
 | |
|   *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_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. Othewise, 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 still error or the variable is corrupted.
 | |
|       // Fall back to the default value.
 | |
|       //
 | |
|       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;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // 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.
 | |
|         //
 | |
|         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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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 inteface 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 {
 | |
| 
 | |
|     if (NewPolicy == Ip6ConfigPolicyAutomatic) {
 | |
|       //
 | |
|       // 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);
 | |
|     } else {
 | |
|       //
 | |
|       // 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 Ip6ConfigSetMaunualAddress.
 | |
| 
 | |
|   @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;
 | |
| 
 | |
|   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 asynchrous 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
 | |
| Ip6ConfigSetMaunualAddress (
 | |
|   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;
 | |
| 
 | |
|   ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY);
 | |
| 
 | |
|   if (((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0) || (DataSize == 0)) {
 | |
|     return EFI_BAD_BUFFER_SIZE;
 | |
|   }
 | |
| 
 | |
|   if (Instance->Policy != Ip6ConfigPolicyManual) {
 | |
|     return EFI_WRITE_PROTECTED;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
 | |
| 
 | |
|   //
 | |
|   // 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.
 | |
|   //
 | |
|   DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
| 
 | |
|   if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {
 | |
|     return EFI_BAD_BUFFER_SIZE;
 | |
|   }
 | |
| 
 | |
|   if (Instance->Policy != Ip6ConfigPolicyManual) {
 | |
|     return EFI_WRITE_PROTECTED;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   IpSb            = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
 | |
|   Item            = &Instance->DataItem[Ip6ConfigDataTypeGateway];
 | |
|   OldGateway      = Item->Data.Gateway;
 | |
|   OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);
 | |
|   OneRemoved      = FALSE;
 | |
|   OneAdded        = FALSE;
 | |
| 
 | |
|   if (NewGatewayCount != OldGatewayCount) {
 | |
|     Tmp = AllocatePool (DataSize);
 | |
|     if (Tmp == NULL) {
 | |
|       return EFI_OUT_OF_RESOURCES;
 | |
|     }
 | |
|   } else {
 | |
|     Tmp = NULL;
 | |
|   }
 | |
| 
 | |
|   for (Index1 = 0; Index1 < OldGatewayCount; Index1++) {
 | |
|     //
 | |
|     // Find the gateways that are no long in the new setting and remove them.
 | |
|     //
 | |
|     for (Index2 = 0; Index2 < NewGatewayCount; Index2++) {
 | |
|       if (EFI_IP6_EQUAL (OldGateway + Index1, NewGateway + Index2)) {
 | |
|         OneRemoved = TRUE;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Index2 == NewGatewayCount) {
 | |
|       //
 | |
|       // Remove this default router.
 | |
|       //
 | |
|       DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1);
 | |
|       if (DefaultRouter != NULL) {
 | |
|         Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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;
 | |
|   UINTN                 Index1;
 | |
|   EFI_IPv6_ADDRESS      *OldDns;
 | |
|   EFI_IPv6_ADDRESS      *NewDns;
 | |
|   UINTN                 OldDnsCount;
 | |
|   UINTN                 NewDnsCount;
 | |
|   IP6_CONFIG_DATA_ITEM  *Item;
 | |
|   BOOLEAN               OneAdded;
 | |
|   VOID                  *Tmp;
 | |
| 
 | |
|   if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {
 | |
|     return EFI_BAD_BUFFER_SIZE;
 | |
|   }
 | |
| 
 | |
|   if (Instance->Policy != Ip6ConfigPolicyManual) {
 | |
|     return EFI_WRITE_PROTECTED;
 | |
|   }
 | |
| 
 | |
|   Item        = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];
 | |
|   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.
 | |
|       //
 | |
|       FreePool (Tmp);
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) {
 | |
|       if (EFI_IP6_EQUAL (NewDns + NewIndex, NewDns + Index1)) {
 | |
|         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;
 | |
|     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
 | |
|   )
 | |
| {
 | |
|   IfInfo->Name[0] = L'e';
 | |
|   IfInfo->Name[1] = L't';
 | |
|   IfInfo->Name[2] = L'h';
 | |
|   IfInfo->Name[3] = (CHAR16) (L'0' + IpSb->Ip6ConfigInstance.IfIndex);
 | |
|   IfInfo->Name[4] = 0;
 | |
| 
 | |
|   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 == IP6_CONFIG_DHCP6_OPTION_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.
 | |
|                                 - Data is NULL.
 | |
|                                 - One or more fields in Data do 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)) {
 | |
|     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 asynchornous 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  = Ip6ConfigSetMaunualAddress;
 | |
|   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;
 | |
| }
 | |
| 
 |