/** @file
  The implementation of IPSEC_CONFIG_PROTOCOL.

  Copyright (c) 2009 - 2017, 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 "IpSecConfigImpl.h"
#include "IpSecDebug.h"

LIST_ENTRY                mConfigData[IPsecConfigDataTypeMaximum];
BOOLEAN                   mSetBySelf = FALSE;

//
// Common CompareSelector routine entry for SPD/SAD/PAD.
//
IPSEC_COMPARE_SELECTOR    mCompareSelector[] = {
  (IPSEC_COMPARE_SELECTOR) CompareSpdSelector,
  (IPSEC_COMPARE_SELECTOR) CompareSaId,
  (IPSEC_COMPARE_SELECTOR) ComparePadId
};

//
// Common IsZeroSelector routine entry for SPD/SAD/PAD.
//
IPSEC_IS_ZERO_SELECTOR    mIsZeroSelector[] = {
  (IPSEC_IS_ZERO_SELECTOR) IsZeroSpdSelector,
  (IPSEC_IS_ZERO_SELECTOR) IsZeroSaId,
  (IPSEC_IS_ZERO_SELECTOR) IsZeroPadId
};

//
// Common DuplicateSelector routine entry for SPD/SAD/PAD.
//
IPSEC_DUPLICATE_SELECTOR  mDuplicateSelector[] = {
  (IPSEC_DUPLICATE_SELECTOR) DuplicateSpdSelector,
  (IPSEC_DUPLICATE_SELECTOR) DuplicateSaId,
  (IPSEC_DUPLICATE_SELECTOR) DuplicatePadId
};

//
// Common FixPolicyEntry routine entry for SPD/SAD/PAD.
//
IPSEC_FIX_POLICY_ENTRY    mFixPolicyEntry[] = {
  (IPSEC_FIX_POLICY_ENTRY) FixSpdEntry,
  (IPSEC_FIX_POLICY_ENTRY) FixSadEntry,
  (IPSEC_FIX_POLICY_ENTRY) FixPadEntry
};

//
// Common UnfixPolicyEntry routine entry for SPD/SAD/PAD.
//
IPSEC_FIX_POLICY_ENTRY    mUnfixPolicyEntry[] = {
  (IPSEC_FIX_POLICY_ENTRY) UnfixSpdEntry,
  (IPSEC_FIX_POLICY_ENTRY) UnfixSadEntry,
  (IPSEC_FIX_POLICY_ENTRY) UnfixPadEntry
};

//
// Common SetPolicyEntry routine entry for SPD/SAD/PAD.
//
IPSEC_SET_POLICY_ENTRY    mSetPolicyEntry[] = {
  (IPSEC_SET_POLICY_ENTRY) SetSpdEntry,
  (IPSEC_SET_POLICY_ENTRY) SetSadEntry,
  (IPSEC_SET_POLICY_ENTRY) SetPadEntry
};

//
// Common GetPolicyEntry routine entry for SPD/SAD/PAD.
//
IPSEC_GET_POLICY_ENTRY    mGetPolicyEntry[] = {
  (IPSEC_GET_POLICY_ENTRY) GetSpdEntry,
  (IPSEC_GET_POLICY_ENTRY) GetSadEntry,
  (IPSEC_GET_POLICY_ENTRY) GetPadEntry
};

//
// Routine entry for IpSecConfig protocol.
//
EFI_IPSEC_CONFIG_PROTOCOL mIpSecConfigInstance = {
  EfiIpSecConfigSetData,
  EfiIpSecConfigGetData,
  EfiIpSecConfigGetNextSelector,
  EfiIpSecConfigRegisterNotify,
  EfiIpSecConfigUnregisterNotify
};

/**
  Get the all IPSec configuration variables and store those variables
  to the internal data structure.

  This founction is called by IpSecConfigInitialize() that is to intialize the 
  IPsecConfiguration Protocol.

  @param[in]  Private            Point to IPSEC_PRIVATE_DATA.

  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.
  @retval EFI_SUCCESS            Restore the IPsec Configuration successfully.
  @retval  others                Other errors is found during the variable getting.

**/
EFI_STATUS
IpSecConfigRestore (
  IN IPSEC_PRIVATE_DATA               *Private
  );

/**
  Check if the specified EFI_IP_ADDRESS_INFO is in EFI_IP_ADDRESS_INFO list.

  @param[in]   AddressInfo         Pointer of IP_ADDRESS_INFO to be search in AddressInfo list.
  @param[in]   AddressInfoList     A list that contains IP_ADDRESS_INFOs.
  @param[in]   AddressCount        Point out how many IP_ADDRESS_INFO in the list.

  @retval  TRUE    The specified AddressInfo is in the AddressInfoList.
  @retval  FALSE   The specified AddressInfo is not in the AddressInfoList.
  
**/
BOOLEAN
IsInAddressInfoList(
  IN EFI_IP_ADDRESS_INFO              *AddressInfo,
  IN EFI_IP_ADDRESS_INFO              *AddressInfoList,
  IN UINT32                           AddressCount
  )
{
  UINT8           Index;
  EFI_IP_ADDRESS  ZeroAddress;

  ZeroMem(&ZeroAddress, sizeof (EFI_IP_ADDRESS));

  //
  // Zero Address means any address is matched.
  //
  if (AddressCount == 1) {
    if (CompareMem (
          &AddressInfoList[0].Address,
          &ZeroAddress,
          sizeof (EFI_IP_ADDRESS)
          ) == 0) {
      return TRUE;
    }
  }
  for (Index = 0; Index < AddressCount ; Index++) {
    if (CompareMem (
          AddressInfo,
          &AddressInfoList[Index].Address,
          sizeof (EFI_IP_ADDRESS)
          ) == 0 && 
          AddressInfo->PrefixLength == AddressInfoList[Index].PrefixLength
          ) { 
       return TRUE;
     }
  }
  return FALSE;
}
 
/**
  Compare two SPD Selectors.

  Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/
  NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the 
  Local Addresses and remote Addresses.

  @param[in]   Selector1           Pointer of first SPD Selector.
  @param[in]   Selector2           Pointer of second SPD Selector.

  @retval  TRUE    This two Selector have the same value in above fields.
  @retval  FALSE   Not all above fields have the same value in these two Selectors.
  
**/
BOOLEAN
CompareSpdSelector (
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2
  )
{
  EFI_IPSEC_SPD_SELECTOR  *SpdSel1;
  EFI_IPSEC_SPD_SELECTOR  *SpdSel2;
  BOOLEAN                 IsMatch;
  UINTN                   Index;

  SpdSel1 = &Selector1->SpdSelector;
  SpdSel2 = &Selector2->SpdSelector;
  IsMatch = TRUE;

  //
  // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/
  // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the
  // two Spdselectors. Since the SPD supports two directions, it needs to 
  // compare two directions.
  //
  if ((SpdSel1->LocalAddressCount != SpdSel2->LocalAddressCount &&
       SpdSel1->LocalAddressCount != SpdSel2->RemoteAddressCount) ||
      (SpdSel1->RemoteAddressCount != SpdSel2->RemoteAddressCount &&
       SpdSel1->RemoteAddressCount != SpdSel2->LocalAddressCount) ||
       SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol ||
       SpdSel1->LocalPort != SpdSel2->LocalPort ||
       SpdSel1->LocalPortRange != SpdSel2->LocalPortRange ||
       SpdSel1->RemotePort != SpdSel2->RemotePort ||
       SpdSel1->RemotePortRange != SpdSel2->RemotePortRange
       ) {
    IsMatch = FALSE;
    return IsMatch;
  }
  
  //
  // Compare the all LocalAddress and RemoteAddress fields in the two Spdselectors.
  // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare 
  // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return
  // TRUE.
  //
  for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {
    if (!IsInAddressInfoList (
          &SpdSel1->LocalAddress[Index],
          SpdSel2->LocalAddress,
          SpdSel2->LocalAddressCount
          )) {
      IsMatch = FALSE;
      break;
    }
  }
  if (IsMatch) {
    for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {
      if (!IsInAddressInfoList (
            &SpdSel2->LocalAddress[Index],
            SpdSel1->LocalAddress,
            SpdSel1->LocalAddressCount
            )) {
        IsMatch = FALSE;
        break;
      }
    }
  }
  if (IsMatch) {
    for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {
      if (!IsInAddressInfoList (
            &SpdSel1->RemoteAddress[Index],
            SpdSel2->RemoteAddress,
            SpdSel2->RemoteAddressCount
            )) {
        IsMatch = FALSE;
        break;
      }
    }
  }
  if (IsMatch) {
    for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {
      if (!IsInAddressInfoList (
            &SpdSel2->RemoteAddress[Index],
            SpdSel1->RemoteAddress,
            SpdSel1->RemoteAddressCount
            )) {
        IsMatch = FALSE;
        break;
      }
    }
  }
  //
  // Finish the one direction compare. If it is matched, return; otherwise, 
  // compare the other direction.
  //
  if (IsMatch) {
    return IsMatch;
  }
  //
  // Secondly, the SpdSel1->LocalAddress doesn't equal to  SpdSel2->LocalAddress and 
  // SpdSel1->RemoteAddress doesn't equal to SpdSel2->RemoteAddress. Try to compare
  // the RemoteAddress to LocalAddress.
  //
  IsMatch = TRUE;
  for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {
    if (!IsInAddressInfoList (
          &SpdSel1->RemoteAddress[Index],
          SpdSel2->LocalAddress,
          SpdSel2->LocalAddressCount
          )) {
      IsMatch = FALSE;
      break;
    }
  }
  if (IsMatch) {
    for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {
      if (!IsInAddressInfoList (
            &SpdSel2->RemoteAddress[Index],
            SpdSel1->LocalAddress,
            SpdSel1->LocalAddressCount
            )) {
        IsMatch = FALSE;
        break;
      }
    }
  }
  if (IsMatch) {
    for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {
      if (!IsInAddressInfoList (
            &SpdSel1->LocalAddress[Index],
            SpdSel2->RemoteAddress,
            SpdSel2->RemoteAddressCount
            )) {
        IsMatch = FALSE;
        break;
      }
    }
  }
  if (IsMatch) {
    for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {
      if (!IsInAddressInfoList (
            &SpdSel2->LocalAddress[Index],
            SpdSel1->RemoteAddress,
            SpdSel1->RemoteAddressCount
            )) {
        IsMatch = FALSE;
        break;
      }
    }
  }
  return IsMatch;
}

/**
  Find if the two SPD Selectors has subordinative.

  Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/
  NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the 
  Local Addresses and remote Addresses.

  @param[in]   Selector1           Pointer of first SPD Selector.
  @param[in]   Selector2           Pointer of second SPD Selector.

  @retval  TRUE    The first SPD Selector is subordinate Selector of second SPD Selector.
  @retval  FALSE   The first SPD Selector is not subordinate Selector of second 
                   SPD Selector.
  
**/
BOOLEAN
IsSubSpdSelector (
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2
  )
{
  EFI_IPSEC_SPD_SELECTOR  *SpdSel1;
  EFI_IPSEC_SPD_SELECTOR  *SpdSel2;
  BOOLEAN                 IsMatch;
  UINTN                   Index;

  SpdSel1 = &Selector1->SpdSelector;
  SpdSel2 = &Selector2->SpdSelector;
  IsMatch = TRUE;

  //
  // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/
  // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the
  // two Spdselectors. Since the SPD supports two directions, it needs to 
  // compare two directions.
  //
  if (SpdSel1->LocalAddressCount > SpdSel2->LocalAddressCount ||
      SpdSel1->RemoteAddressCount > SpdSel2->RemoteAddressCount ||
      (SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol && SpdSel2->NextLayerProtocol != 0xffff) ||
      (SpdSel1->LocalPort > SpdSel2->LocalPort && SpdSel2->LocalPort != 0)||
      (SpdSel1->LocalPortRange > SpdSel2->LocalPortRange && SpdSel1->LocalPort != 0)||
      (SpdSel1->RemotePort > SpdSel2->RemotePort && SpdSel2->RemotePort != 0) ||
      (SpdSel1->RemotePortRange > SpdSel2->RemotePortRange && SpdSel2->RemotePort != 0)
      ) {
    IsMatch = FALSE;
  }
  
  //
  // Compare the all LocalAddress and RemoteAddress fields in the two Spdselectors.
  // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare 
  // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return
  // TRUE.
  //
  if (IsMatch) {
    for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {
      if (!IsInAddressInfoList (
            &SpdSel1->LocalAddress[Index],
            SpdSel2->LocalAddress,
            SpdSel2->LocalAddressCount
            )) {
        IsMatch = FALSE;
        break;
      }
    }

    if (IsMatch) {
      for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {
        if (!IsInAddressInfoList (
              &SpdSel1->RemoteAddress[Index],
              SpdSel2->RemoteAddress,
              SpdSel2->RemoteAddressCount
              )) {
          IsMatch = FALSE;
          break;
        }
      }
    }
  }
  if (IsMatch) {
    return IsMatch;
  }
  
  //
  //
  // The SPD selector in SPD entry is two way.
  //
  // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/
  // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the
  // two Spdselectors. Since the SPD supports two directions, it needs to 
  // compare two directions.
  //
  IsMatch = TRUE;
  if (SpdSel1->LocalAddressCount > SpdSel2->RemoteAddressCount ||
      SpdSel1->RemoteAddressCount > SpdSel2->LocalAddressCount ||
      (SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol && SpdSel2->NextLayerProtocol != 0xffff) ||
      (SpdSel1->LocalPort > SpdSel2->RemotePort && SpdSel2->RemotePort != 0)||
      (SpdSel1->LocalPortRange > SpdSel2->RemotePortRange && SpdSel1->RemotePort != 0)||
      (SpdSel1->RemotePort > SpdSel2->LocalPort && SpdSel2->LocalPort != 0) ||
      (SpdSel1->RemotePortRange > SpdSel2->LocalPortRange && SpdSel2->LocalPort != 0)
      ) {
    IsMatch = FALSE;
    return IsMatch;
  }
  
  //
  // Compare the all LocalAddress and RemoteAddress fields in the two Spdselectors.
  // First, SpdSel1->LocalAddress to SpdSel2->RemoteAddress && Compare 
  // SpdSel1->RemoteAddress to SpdSel2->LocalAddress. If all match, return
  // TRUE.
  //
  for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {
    if (!IsInAddressInfoList (
          &SpdSel1->LocalAddress[Index],
          SpdSel2->RemoteAddress,
          SpdSel2->RemoteAddressCount
          )) {
      IsMatch = FALSE;
      break;
    }
  }

  if (IsMatch) {
    for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {
      if (!IsInAddressInfoList (
            &SpdSel1->RemoteAddress[Index],
            SpdSel2->LocalAddress,
            SpdSel2->LocalAddressCount
            )) {
        IsMatch = FALSE;
        break;
      }
    }
  }
  return IsMatch;
  
}

/**
  Compare two SA IDs.

  @param[in]   Selector1           Pointer of first SA ID.
  @param[in]   Selector2           Pointer of second SA ID.

  @retval  TRUE    This two Selectors have the same SA ID.
  @retval  FALSE   This two Selecotrs don't have the same SA ID.
  
**/
BOOLEAN
CompareSaId (
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2
  )
{
  EFI_IPSEC_SA_ID *SaId1;
  EFI_IPSEC_SA_ID *SaId2;
  BOOLEAN         IsMatch;

  SaId1   = &Selector1->SaId;
  SaId2   = &Selector2->SaId;
  IsMatch = TRUE;

  if (CompareMem (SaId1, SaId2, sizeof (EFI_IPSEC_SA_ID)) != 0) {
    IsMatch = FALSE;
  }

  return IsMatch;
}

/**
  Compare two PAD IDs.

  @param[in]   Selector1           Pointer of first PAD ID.
  @param[in]   Selector2           Pointer of second PAD ID.

  @retval  TRUE    This two Selectors have the same PAD ID.
  @retval  FALSE   This two Selecotrs don't have the same PAD ID.
  
**/
BOOLEAN
ComparePadId (
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2
  )
{
  EFI_IPSEC_PAD_ID  *PadId1;
  EFI_IPSEC_PAD_ID  *PadId2;
  BOOLEAN           IsMatch;

  PadId1  = &Selector1->PadId;
  PadId2  = &Selector2->PadId;
  IsMatch = TRUE;

  //
  // Compare the PeerIdValid fields in PadId.
  //
  if (PadId1->PeerIdValid != PadId2->PeerIdValid) {
    IsMatch = FALSE;
  }
  //
  // Compare the PeerId fields in PadId if PeerIdValid is true.
  //
  if (IsMatch &&
      PadId1->PeerIdValid &&
      AsciiStriCmp ((CONST CHAR8 *) PadId1->Id.PeerId, (CONST CHAR8 *) PadId2->Id.PeerId) != 0
      ) {
    IsMatch = FALSE;
  }
  //
  // Compare the IpAddress fields in PadId if PeerIdValid is false.
  //
  if (IsMatch &&
      !PadId1->PeerIdValid &&
      (PadId1->Id.IpAddress.PrefixLength != PadId2->Id.IpAddress.PrefixLength ||
       CompareMem (&PadId1->Id.IpAddress.Address, &PadId2->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0)
      ) {
    IsMatch = FALSE;
  }

  return IsMatch;
}

/**
  Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount
  fields.

  @param[in]  Selector      Pointer of the SPD Selector.

  @retval     TRUE          If the SPD Selector is Zero.
  @retval     FALSE         If the SPD Selector is not Zero.

**/
BOOLEAN
IsZeroSpdSelector (
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector
  )
{
  EFI_IPSEC_SPD_SELECTOR  *SpdSel;
  BOOLEAN                 IsZero;

  SpdSel  = &Selector->SpdSelector;
  IsZero  = FALSE;

  if (SpdSel->LocalAddressCount == 0 && SpdSel->RemoteAddressCount == 0) {
    IsZero = TRUE;
  }

  return IsZero;
}

/**
  Check if the SA ID is Zero by its DestAddress.

  @param[in]  Selector      Pointer of the SA ID.

  @retval     TRUE          If the SA ID is Zero.
  @retval     FALSE         If the SA ID is not Zero.

**/
BOOLEAN
IsZeroSaId (
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector
  )
{
  BOOLEAN                   IsZero;
  EFI_IPSEC_CONFIG_SELECTOR ZeroSelector;
  
  IsZero    = FALSE;

  ZeroMem (&ZeroSelector, sizeof (EFI_IPSEC_CONFIG_SELECTOR));

  if (CompareMem (&ZeroSelector, Selector, sizeof (EFI_IPSEC_CONFIG_SELECTOR)) == 0) {
    IsZero = TRUE;
  }

  return IsZero;
}

/**
  Check if the PAD ID is Zero.

  @param[in]  Selector      Pointer of the PAD ID.

  @retval     TRUE          If the PAD ID is Zero.
  @retval     FALSE         If the PAD ID is not Zero.

**/
BOOLEAN
IsZeroPadId (
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector
  )
{
  EFI_IPSEC_PAD_ID  *PadId;
  EFI_IPSEC_PAD_ID  ZeroId;
  BOOLEAN           IsZero;

  PadId   = &Selector->PadId;
  IsZero  = FALSE;

  ZeroMem (&ZeroId, sizeof (EFI_IPSEC_PAD_ID));

  if (CompareMem (PadId, &ZeroId, sizeof (EFI_IPSEC_PAD_ID)) == 0) {
    IsZero = TRUE;
  }

  return IsZero;
}

/**
  Copy Source SPD Selector to the Destination SPD Selector.

  @param[in, out] DstSel             Pointer of Destination SPD Selector.
  @param[in]      SrcSel             Pointer of Source SPD Selector.
  @param[in, out] Size               The size of the Destination SPD Selector. If it 
                                     not NULL and its value less than the size of 
                                     Source SPD Selector, the value of Source SPD 
                                     Selector's size will be passed to caller by this
                                     parameter.

  @retval EFI_INVALID_PARAMETER  If the Destination or Source SPD Selector is NULL
  @retval EFI_BUFFER_TOO_SMALL   If the input Size is less than size of the Source SPD Selector. 
  @retval EFI_SUCCESS            Copy Source SPD Selector to the Destination SPD
                                 Selector successfully.

**/
EFI_STATUS
DuplicateSpdSelector (
  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,
  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,
  IN OUT UINTN                        *Size
  )
{
  EFI_IPSEC_SPD_SELECTOR  *Dst;
  EFI_IPSEC_SPD_SELECTOR  *Src;

  Dst = &DstSel->SpdSelector;
  Src = &SrcSel->SpdSelector;

  if (Dst == NULL || Src == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (Size != NULL && (*Size) < SIZE_OF_SPD_SELECTOR (Src)) {
    *Size = SIZE_OF_SPD_SELECTOR (Src);
    return EFI_BUFFER_TOO_SMALL;
  }
  //
  // Copy the base structure of SPD selector.
  //
  CopyMem (Dst, Src, sizeof (EFI_IPSEC_SPD_SELECTOR));

  //
  // Copy the local address array of SPD selector.
  //
  Dst->LocalAddress = (EFI_IP_ADDRESS_INFO *) (Dst + 1);
  CopyMem (
    Dst->LocalAddress,
    Src->LocalAddress,
    sizeof (EFI_IP_ADDRESS_INFO) * Dst->LocalAddressCount
    );

  //
  // Copy the remote address array of SPD selector.
  //
  Dst->RemoteAddress = Dst->LocalAddress + Dst->LocalAddressCount;
  CopyMem (
    Dst->RemoteAddress,
    Src->RemoteAddress,
    sizeof (EFI_IP_ADDRESS_INFO) * Dst->RemoteAddressCount
    );

  return EFI_SUCCESS;
}

/**
  Copy Source SA ID to the Destination SA ID.

  @param[in, out] DstSel             Pointer of Destination SA ID.
  @param[in]      SrcSel             Pointer of Source SA ID.
  @param[in, out] Size               The size of the Destination SA ID. If it 
                                     not NULL and its value less than the size of 
                                     Source SA ID, the value of Source SA ID's size 
                                     will be passed to caller by this parameter.

  @retval EFI_INVALID_PARAMETER  If the Destination or Source SA ID is NULL.
  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source SA ID. 
  @retval EFI_SUCCESS            Copy Source SA ID  to the Destination SA ID successfully.

**/
EFI_STATUS
DuplicateSaId (
  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,
  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,
  IN OUT UINTN                        *Size
  )
{
  EFI_IPSEC_SA_ID *Dst;
  EFI_IPSEC_SA_ID *Src;

  Dst = &DstSel->SaId;
  Src = &SrcSel->SaId;

  if (Dst == NULL || Src == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (Size != NULL && *Size < sizeof (EFI_IPSEC_SA_ID)) {
    *Size = sizeof (EFI_IPSEC_SA_ID);
    return EFI_BUFFER_TOO_SMALL;
  }

  CopyMem (Dst, Src, sizeof (EFI_IPSEC_SA_ID));

  return EFI_SUCCESS;
}

/**
  Copy Source PAD ID to the Destination PAD ID.

  @param[in, out] DstSel             Pointer of Destination PAD ID.
  @param[in]      SrcSel             Pointer of Source PAD ID.
  @param[in, out] Size               The size of the Destination PAD ID. If it 
                                     not NULL and its value less than the size of 
                                     Source PAD ID, the value of Source PAD ID's size 
                                     will be passed to caller by this parameter.

  @retval EFI_INVALID_PARAMETER  If the Destination or Source PAD ID is NULL.
  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source PAD ID .
  @retval EFI_SUCCESS            Copy Source PAD ID  to the Destination PAD ID successfully.

**/
EFI_STATUS
DuplicatePadId (
  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,
  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,
  IN OUT UINTN                        *Size
  )
{
  EFI_IPSEC_PAD_ID  *Dst;
  EFI_IPSEC_PAD_ID  *Src;

  Dst = &DstSel->PadId;
  Src = &SrcSel->PadId;

  if (Dst == NULL || Src == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (Size != NULL && *Size < sizeof (EFI_IPSEC_PAD_ID)) {
    *Size = sizeof (EFI_IPSEC_PAD_ID);
    return EFI_BUFFER_TOO_SMALL;
  }

  CopyMem (Dst, Src, sizeof (EFI_IPSEC_PAD_ID));

  return EFI_SUCCESS;
}

/**
  Fix the value of some members of SPD Selector. 

  This function is called by IpSecCopyPolicyEntry()which copy the Policy 
  Entry into the Variable. Since some members in SPD Selector are pointers, 
  a physical address to relative address convertion is required before copying 
  this SPD entry into the variable.

  @param[in]       Selector              Pointer of SPD Selector.
  @param[in, out]  Data                  Pointer of SPD Data.

**/
VOID
FixSpdEntry (
  IN     EFI_IPSEC_SPD_SELECTOR            *Selector,
  IN OUT EFI_IPSEC_SPD_DATA                *Data
  )
{
  //
  // It assumes that all ref buffers in SPD selector and data are
  // stored in the continous memory and close to the base structure.
  //
  FIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);
  FIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);

  if (Data->ProcessingPolicy != NULL) {
    if (Data->ProcessingPolicy->TunnelOption != NULL) {
      FIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);
    }

    FIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);
  }

}

/**
  Fix the value of some members of SA ID. 

  This function is called by IpSecCopyPolicyEntry()which copy the Policy 
  Entry into the Variable. Since some members in SA ID are pointers, 
  a physical address to relative address conversion is required before copying 
  this SAD into the variable.

  @param[in]       SaId                  Pointer of SA ID
  @param[in, out]  Data                  Pointer of SA Data.

**/
VOID
FixSadEntry (
  IN     EFI_IPSEC_SA_ID                  *SaId,
  IN OUT EFI_IPSEC_SA_DATA2                *Data
  )
{
  //
  // It assumes that all ref buffers in SAD selector and data are
  // stored in the continous memory and close to the base structure.
  //
  if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
    FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);
  }

  if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
    FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);
  }

  if (Data->SpdSelector != NULL) {
    if (Data->SpdSelector->LocalAddress != NULL) {
      FIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);
    }

    FIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);
    FIX_REF_BUF_ADDR (Data->SpdSelector, Data);
  }

}

/**
  Fix the value of some members of PAD ID. 

  This function is called by IpSecCopyPolicyEntry()which copy the Policy 
  Entry into the Variable. Since some members in PAD ID are pointers, 
  a physical address to relative address conversion is required before copying
  this PAD into the variable.

  @param[in]       PadId              Pointer of PAD ID.
  @param[in, out]  Data               Pointer of PAD Data.

**/
VOID
FixPadEntry (
  IN     EFI_IPSEC_PAD_ID                  *PadId,
  IN OUT EFI_IPSEC_PAD_DATA                *Data
  )
{
  //
  // It assumes that all ref buffers in pad selector and data are
  // stored in the continous memory and close to the base structure.
  //
  if (Data->AuthData != NULL) {
    FIX_REF_BUF_ADDR (Data->AuthData, Data);
  }

  if (Data->RevocationData != NULL) {
    FIX_REF_BUF_ADDR (Data->RevocationData, Data);
  }

}

/**
  Recover the value of some members of SPD Selector. 

  This function is corresponding to FixSpdEntry(). It recovers the value of members
  of SPD Selector that are fixed by FixSpdEntry().

  @param[in, out]  Selector              Pointer of SPD Selector.
  @param[in, out]  Data                  Pointer of SPD Data.

**/
VOID
UnfixSpdEntry (
  IN OUT EFI_IPSEC_SPD_SELECTOR           *Selector,
  IN OUT EFI_IPSEC_SPD_DATA               *Data
  )
{
  //
  // It assumes that all ref buffers in SPD selector and data are
  // stored in the continous memory and close to the base structure.
  //
  UNFIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);
  UNFIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);

  if (Data->ProcessingPolicy != NULL) {
    UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);
    if (Data->ProcessingPolicy->TunnelOption != NULL) {
      UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);
    }
  }
  
}

/**
  Recover the value of some members of SA ID. 

  This function is corresponding to FixSadEntry(). It recovers the value of members
  of SAD ID that are fixed by FixSadEntry().

  @param[in, out]  SaId              Pointer of SAD ID.
  @param[in, out]  Data              Pointer of SAD Data.

**/
VOID
UnfixSadEntry (
  IN OUT EFI_IPSEC_SA_ID                     *SaId,
  IN OUT EFI_IPSEC_SA_DATA2                   *Data
  )
{
  //
  // It assumes that all ref buffers in SAD selector and data are
  // stored in the continous memory and close to the base structure.
  //
  if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
    UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);
  }

  if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
    UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);
  }

  if (Data->SpdSelector != NULL) {
    UNFIX_REF_BUF_ADDR (Data->SpdSelector, Data);
    if (Data->SpdSelector->LocalAddress != NULL) {
      UNFIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);
    }

    UNFIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);
  }

}

/**
  Recover the value of some members of PAD ID. 

  This function is corresponding to FixPadEntry(). It recovers the value of members
  of PAD ID that are fixed by FixPadEntry().

  @param[in]       PadId              Pointer of PAD ID.
  @param[in, out]  Data               Pointer of PAD Data.

**/
VOID
UnfixPadEntry (
  IN     EFI_IPSEC_PAD_ID                 *PadId,
  IN OUT EFI_IPSEC_PAD_DATA               *Data
  )
{
  //
  // It assumes that all ref buffers in pad selector and data are
  // stored in the continous memory and close to the base structure.
  //
  if (Data->AuthData != NULL) {
    UNFIX_REF_BUF_ADDR (Data->AuthData, Data);
  }

  if (Data->RevocationData != NULL) {
    UNFIX_REF_BUF_ADDR (Data->RevocationData, Data);
  }

}

/**
  Set the security policy information for the EFI IPsec driver.

  The IPsec configuration data has a unique selector/identifier separately to 
  identify a data entry.

  @param[in]  Selector           Pointer to an entry selector on operated 
                                 configuration data specified by DataType. 
                                 A NULL Selector causes the entire specified-type 
                                 configuration information to be flushed.
  @param[in]  Data               The data buffer to be set. The structure 
                                 of the data buffer should be EFI_IPSEC_SPD_DATA.
  @param[in]  Context            Pointer to one entry selector that describes 
                                 the expected position the new data entry will 
                                 be added. If Context is NULL, the new entry will
                                 be appended the end of database.

  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:
                                   - Selector is not NULL and its LocalAddress 
                                     is NULL or its RemoteAddress is NULL.
                                   - Data is not NULL and its Action is Protected 
                                     and its plolicy is NULL.
                                   - Data is not NULL, its Action is not protected,
                                     and its policy is not NULL.
                                   - The Action of Data is Protected, its policy 
                                     mode is Tunnel, and its tunnel option is NULL.
                                   - The Action of Data is protected and its policy 
                                     mode is not Tunnel and it tunnel option is not NULL.
                                   - SadEntry requied to be set into new SpdEntry's Sas has 
                                     been found but it is invalid.
  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.
  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

**/
EFI_STATUS
SetSpdEntry (
  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,
  IN VOID                            *Data,
  IN VOID                            *Context OPTIONAL
  )
{
  EFI_IPSEC_SPD_SELECTOR  *SpdSel;
  EFI_IPSEC_SPD_DATA      *SpdData;
  EFI_IPSEC_SPD_SELECTOR  *InsertBefore;
  LIST_ENTRY              *SpdList;
  LIST_ENTRY              *SadList;
  LIST_ENTRY              *SpdSas;
  LIST_ENTRY              *EntryInsertBefore;
  LIST_ENTRY              *Entry;
  LIST_ENTRY              *Entry2;
  LIST_ENTRY              *NextEntry;
  LIST_ENTRY              *NextEntry2;
  IPSEC_SPD_ENTRY         *SpdEntry;
  IPSEC_SAD_ENTRY         *SadEntry;
  UINTN                   SpdEntrySize;
  UINTN                   Index;

  SpdSel        = (Selector == NULL) ? NULL : &Selector->SpdSelector;
  SpdData       = (Data == NULL) ? NULL : (EFI_IPSEC_SPD_DATA *) Data;
  InsertBefore  = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SpdSelector;
  SpdList       = &mConfigData[IPsecConfigDataTypeSpd];

  if (SpdSel != NULL) {
    if (SpdSel->LocalAddress == NULL || SpdSel->RemoteAddress == NULL) {
      return EFI_INVALID_PARAMETER;
    }
  }

  if (SpdData != NULL) {
    if ((SpdData->Action == EfiIPsecActionProtect && SpdData->ProcessingPolicy == NULL) ||
        (SpdData->Action != EfiIPsecActionProtect && SpdData->ProcessingPolicy != NULL)
        ) {
      return EFI_INVALID_PARAMETER;
    }

    if (SpdData->Action == EfiIPsecActionProtect) {
      if ((SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption == NULL) ||
          (SpdData->ProcessingPolicy->Mode != EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption != NULL)
          ) {
        return EFI_INVALID_PARAMETER;
      }
    }
  }
  //
  // The default behavior is to insert the node ahead of the header.
  //
  EntryInsertBefore = SpdList;

  //
  // Remove the existed SPD entry.
  //
  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SpdList) {

    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);

    if (SpdSel == NULL || 
        CompareSpdSelector ((EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel)
        ) {
      //
      // Record the existed entry position to keep the original order.
      //
      EntryInsertBefore = SpdEntry->List.ForwardLink;
      RemoveEntryList (&SpdEntry->List);

      //
      // Update the reverse ref of SAD entry in the SPD.sas list.
      //
      SpdSas = &SpdEntry->Data->Sas;
      
      //
      // Remove the related SAs from Sas(SadEntry->BySpd). If the SA entry is established by 
      // IKE, remove from mConfigData list(SadEntry->List) and then free it directly since its 
      // SpdEntry will be freed later.
      //
      NET_LIST_FOR_EACH_SAFE (Entry2, NextEntry2, SpdSas) {
        SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry2);
        
        if (SadEntry->Data->SpdEntry != NULL) {
          RemoveEntryList (&SadEntry->BySpd);
          SadEntry->Data->SpdEntry = NULL;
        }
        
        if (!(SadEntry->Data->ManualSet)) {
          RemoveEntryList (&SadEntry->List);
          FreePool (SadEntry);
        }
      }
      
      //
      // Free the existed SPD entry
      //
      FreePool (SpdEntry);
    }
  }
  //
  // Return success here if only want to remove the SPD entry.
  //
  if (SpdData == NULL || SpdSel == NULL) {
    return EFI_SUCCESS;
  }
  //
  // Search the appointed entry position if InsertBefore is not NULL.
  //
  if (InsertBefore != NULL) {

    NET_LIST_FOR_EACH (Entry, SpdList) {
      SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);

      if (CompareSpdSelector (
            (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,
            (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore
            )) {
        EntryInsertBefore = Entry;
        break;
      }
    }
  }

  //
  // Do Padding for the different Arch.
  //
  SpdEntrySize  = ALIGN_VARIABLE (sizeof (IPSEC_SPD_ENTRY));
  SpdEntrySize  = ALIGN_VARIABLE (SpdEntrySize + SIZE_OF_SPD_SELECTOR (SpdSel));
  SpdEntrySize += IpSecGetSizeOfEfiSpdData (SpdData);

  SpdEntry = AllocateZeroPool (SpdEntrySize);

  if (SpdEntry == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // Fix the address of Selector and Data buffer and copy them, which is
  // continous memory and close to the base structure of SPD entry.
  //
  SpdEntry->Selector  = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER ((SpdEntry + 1), sizeof (UINTN));
  SpdEntry->Data      = (IPSEC_SPD_DATA *) ALIGN_POINTER (
                                            ((UINT8 *) SpdEntry->Selector + SIZE_OF_SPD_SELECTOR (SpdSel)),
                                            sizeof (UINTN)
                                            );

  DuplicateSpdSelector (
    (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,
    (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,
    NULL
    );

  CopyMem (
    SpdEntry->Data->Name,
    SpdData->Name,
    sizeof (SpdData->Name)
    );
  SpdEntry->Data->PackageFlag      = SpdData->PackageFlag;
  SpdEntry->Data->TrafficDirection = SpdData->TrafficDirection;
  SpdEntry->Data->Action           = SpdData->Action;
  
  //
  // Fix the address of ProcessingPolicy and copy it if need, which is continous
  // memory and close to the base structure of SAD data.
  //
  if (SpdData->Action != EfiIPsecActionProtect) {
    SpdEntry->Data->ProcessingPolicy = NULL;
  } else {
    SpdEntry->Data->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (
                                                                      SpdEntry->Data + 1,
                                                                      sizeof (UINTN)
                                                                      );
    IpSecDuplicateProcessPolicy (SpdEntry->Data->ProcessingPolicy, SpdData->ProcessingPolicy);
  }
  //
  // Update the sas list of the new SPD entry.
  //
  InitializeListHead (&SpdEntry->Data->Sas);

  SadList = &mConfigData[IPsecConfigDataTypeSad];

  NET_LIST_FOR_EACH (Entry, SadList) {
    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);

      for (Index = 0; Index < SpdData->SaIdCount; Index++) {
        if (CompareSaId (
              (EFI_IPSEC_CONFIG_SELECTOR *) &SpdData->SaId[Index],
              (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id
              )) {
          //
          // Check whether the found SadEntry is vaild.
          //
          if (IsSubSpdSelector (
                (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdSelector,
                (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector
                )) {
            if (SadEntry->Data->SpdEntry != NULL) {
              RemoveEntryList (&SadEntry->BySpd);
            }
            InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);
            SadEntry->Data->SpdEntry = SpdEntry;
          } else {
            return EFI_INVALID_PARAMETER;
          }
        }
      }      
  }
  
  //
  // Insert the new SPD entry.
  //
  InsertTailList (EntryInsertBefore, &SpdEntry->List);

  return EFI_SUCCESS;
}

/**
  Set the security association information for the EFI IPsec driver.

  The IPsec configuration data has a unique selector/identifier separately to 
  identify a data entry.

  @param[in]  Selector           Pointer to an entry selector on operated 
                                 configuration data specified by DataType. 
                                 A NULL Selector causes the entire specified-type 
                                 configuration information to be flushed.
  @param[in]  Data               The data buffer to be set. The structure 
                                 of the data buffer should be EFI_IPSEC_SA_DATA.
  @param[in]  Context            Pointer to one entry selector which describes 
                                 the expected position the new data entry will 
                                 be added. If Context is NULL,the new entry will
                                 be appended the end of database.

  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.
  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

**/
EFI_STATUS
SetSadEntry (
  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,
  IN VOID                            *Data,
  IN VOID                            *Context OPTIONAL
  )
{
  IPSEC_SAD_ENTRY   *SadEntry;
  IPSEC_SPD_ENTRY   *SpdEntry;
  LIST_ENTRY        *Entry;
  LIST_ENTRY        *NextEntry;
  LIST_ENTRY        *SadList;
  LIST_ENTRY        *SpdList;
  EFI_IPSEC_SA_ID   *SaId;
  EFI_IPSEC_SA_DATA2 *SaData;
  EFI_IPSEC_SA_ID   *InsertBefore;
  LIST_ENTRY        *EntryInsertBefore;
  UINTN             SadEntrySize;
  
  SaId          = (Selector == NULL) ? NULL : &Selector->SaId;
  SaData        = (Data == NULL) ? NULL : (EFI_IPSEC_SA_DATA2 *) Data;
  InsertBefore  = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SaId;
  SadList       = &mConfigData[IPsecConfigDataTypeSad];

  //
  // The default behavior is to insert the node ahead of the header.
  //
  EntryInsertBefore = SadList;

  //
  // Remove the existed SAD entry.
  //
  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SadList) {

    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);

    if (SaId == NULL || 
        CompareSaId (
          (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,
          (EFI_IPSEC_CONFIG_SELECTOR *) SaId
          )) {
      //
      // Record the existed entry position to keep the original order.
      //
      EntryInsertBefore = SadEntry->List.ForwardLink;

      //
      // Update the related SAD.byspd field.
      //
      if (SadEntry->Data->SpdEntry != NULL) {
        RemoveEntryList (&SadEntry->BySpd);
      }

      RemoveEntryList (&SadEntry->List);
      FreePool (SadEntry);
    }
  }
  //
  // Return success here if only want to remove the SAD entry
  //
  if (SaData == NULL || SaId == NULL) {
    return EFI_SUCCESS;
  }
  //
  // Search the appointed entry position if InsertBefore is not NULL.
  //
  if (InsertBefore != NULL) {

    NET_LIST_FOR_EACH (Entry, SadList) {
      SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);

      if (CompareSaId (
           (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,
           (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore
           )) {
        EntryInsertBefore = Entry;
        break;
      }
    }
  }

  //
  // Do Padding for different Arch.
  //
  SadEntrySize  = ALIGN_VARIABLE (sizeof (IPSEC_SAD_ENTRY));
  SadEntrySize  = ALIGN_VARIABLE (SadEntrySize + sizeof (EFI_IPSEC_SA_ID));
  SadEntrySize  = ALIGN_VARIABLE (SadEntrySize + sizeof (IPSEC_SAD_DATA));
  
  if (SaId->Proto == EfiIPsecAH) {
    SadEntrySize += SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength;
  } else {
    SadEntrySize  = ALIGN_VARIABLE (SadEntrySize + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength);
    SadEntrySize += ALIGN_VARIABLE (SaData->AlgoInfo.EspAlgoInfo.EncKeyLength);
  }

  if (SaData->SpdSelector != NULL) {
    SadEntrySize += SadEntrySize + SIZE_OF_SPD_SELECTOR (SaData->SpdSelector);
  }
  SadEntry      = AllocateZeroPool (SadEntrySize);

  if (SadEntry == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // Fix the address of Id and Data buffer and copy them, which is
  // continous memory and close to the base structure of SAD entry.
  //
  SadEntry->Id    = (EFI_IPSEC_SA_ID *) ALIGN_POINTER ((SadEntry + 1), sizeof (UINTN));
  SadEntry->Data  = (IPSEC_SAD_DATA *) ALIGN_POINTER ((SadEntry->Id + 1), sizeof (UINTN));

  CopyMem (SadEntry->Id, SaId, sizeof (EFI_IPSEC_SA_ID));

  SadEntry->Data->Mode                  = SaData->Mode;
  SadEntry->Data->SequenceNumber        = SaData->SNCount;
  SadEntry->Data->AntiReplayWindowSize  = SaData->AntiReplayWindows;

  ZeroMem (
    &SadEntry->Data->AntiReplayBitmap,
    sizeof (SadEntry->Data->AntiReplayBitmap)
    );

  ZeroMem (
    &SadEntry->Data->AlgoInfo,
    sizeof (EFI_IPSEC_ALGO_INFO)
    );

  SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId     = SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId;
  SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength  = SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength;

  if (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {
    SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SadEntry->Data + 1), sizeof (UINTN));
    CopyMem (
      SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
      SaData->AlgoInfo.EspAlgoInfo.AuthKey,
      SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength
      );
  }

  if (SaId->Proto == EfiIPsecESP) {
    SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId    = SaData->AlgoInfo.EspAlgoInfo.EncAlgoId;
    SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength = SaData->AlgoInfo.EspAlgoInfo.EncKeyLength;

    if (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {
      SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (
                                                               ((UINT8 *) (SadEntry->Data + 1) + 
                                                                 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength),
                                                                 sizeof (UINTN)
                                                                 );
      CopyMem (
        SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
        SaData->AlgoInfo.EspAlgoInfo.EncKey,
        SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength
        );
    }
  }

  CopyMem (
    &SadEntry->Data->SaLifetime,
    &SaData->SaLifetime,
    sizeof (EFI_IPSEC_SA_LIFETIME)
    );

  SadEntry->Data->PathMTU     = SaData->PathMTU;
  SadEntry->Data->SpdSelector = NULL;
  SadEntry->Data->ESNEnabled  = FALSE;
  SadEntry->Data->ManualSet   = SaData->ManualSet;

  //
  // Copy Tunnel Source/Destination Address
  //
  if (SaData->Mode == EfiIPsecTunnel) {
    CopyMem (
      &SadEntry->Data->TunnelDestAddress,
      &SaData->TunnelDestinationAddress,
      sizeof (EFI_IP_ADDRESS)
      );
    CopyMem (
      &SadEntry->Data->TunnelSourceAddress,
      &SaData->TunnelSourceAddress,
      sizeof (EFI_IP_ADDRESS)
      );
  }
  //
  // Update the spd.sas list of the spd entry specified by SAD selector
  //
  SpdList = &mConfigData[IPsecConfigDataTypeSpd];

  for (Entry = SpdList->ForwardLink; Entry != SpdList && SaData->SpdSelector != NULL; Entry = Entry->ForwardLink) {

    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);
    if (IsSubSpdSelector (
          (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector,
          (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector
          ) && SpdEntry->Data->Action == EfiIPsecActionProtect) {
      SadEntry->Data->SpdEntry = SpdEntry;
      SadEntry->Data->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *)((UINT8 *)SadEntry +
                                                                SadEntrySize -
                                                                SIZE_OF_SPD_SELECTOR (SaData->SpdSelector)
                                                                );
      DuplicateSpdSelector (
       (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdSelector,
       (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector,
       NULL
       );
      InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);
    }
  }
  //
  // Insert the new SAD entry.
  //
  InsertTailList (EntryInsertBefore, &SadEntry->List);

  return EFI_SUCCESS;
}

/**
  Set the peer authorization configuration information for the EFI IPsec driver.

  The IPsec configuration data has a unique selector/identifier separately to 
  identify a data entry.

  @param[in]  Selector           Pointer to an entry selector on operated 
                                 configuration data specified by DataType. 
                                 A NULL Selector causes the entire specified-type 
                                 configuration information to be flushed.
  @param[in]  Data               The data buffer to be set. The structure 
                                 of the data buffer should be EFI_IPSEC_PAD_DATA.
  @param[in]  Context            Pointer to one entry selector that describes 
                                 the expected position the new data entry will 
                                 be added. If Context is NULL, the new entry will
                                 be appended the end of database.

  @retval EFI_OUT_OF_RESOURCES  The required system resources could not be allocated.
  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

**/
EFI_STATUS
SetPadEntry (
  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,
  IN VOID                            *Data,
  IN VOID                            *Context OPTIONAL
  )
{
  IPSEC_PAD_ENTRY     *PadEntry;
  EFI_IPSEC_PAD_ID    *PadId;
  EFI_IPSEC_PAD_DATA  *PadData;
  LIST_ENTRY          *PadList;
  LIST_ENTRY          *Entry;
  LIST_ENTRY          *NextEntry;
  EFI_IPSEC_PAD_ID    *InsertBefore;
  LIST_ENTRY          *EntryInsertBefore;
  UINTN               PadEntrySize;
   
  PadId         = (Selector == NULL) ? NULL : &Selector->PadId;
  PadData       = (Data == NULL) ? NULL : (EFI_IPSEC_PAD_DATA *) Data;
  InsertBefore  = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->PadId;
  PadList       = &mConfigData[IPsecConfigDataTypePad];

  //
  // The default behavior is to insert the node ahead of the header.
  //
  EntryInsertBefore = PadList;

  //
  // Remove the existed pad entry.
  //
  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, PadList) {

    PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);

    if (PadId == NULL || 
        ComparePadId ((EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id, (EFI_IPSEC_CONFIG_SELECTOR *) PadId)
        ) {
      //
      // Record the existed entry position to keep the original order.
      //
      EntryInsertBefore = PadEntry->List.ForwardLink;
      RemoveEntryList (&PadEntry->List);

      FreePool (PadEntry);
    }
  }
  //
  // Return success here if only want to remove the pad entry
  //
  if (PadData == NULL || PadId == NULL) {
    return EFI_SUCCESS;
  }
  //
  // Search the appointed entry position if InsertBefore is not NULL.
  //
  if (InsertBefore != NULL) {

    NET_LIST_FOR_EACH (Entry, PadList) {
      PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);

      if (ComparePadId (
            (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id,
            (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore
            )) {
        EntryInsertBefore = Entry;
        break;
      }
    }
  }

  //
  // Do PADDING for different arch.
  //
  PadEntrySize  = ALIGN_VARIABLE (sizeof (IPSEC_PAD_ENTRY));
  PadEntrySize  = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_ID));
  PadEntrySize  = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_DATA));
  PadEntrySize  = ALIGN_VARIABLE (PadEntrySize + (PadData->AuthData != NULL ? PadData->AuthDataSize : 0));
  PadEntrySize += PadData->RevocationData != NULL ? PadData->RevocationDataSize : 0;

  PadEntry      = AllocateZeroPool (PadEntrySize);

  if (PadEntry == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // Fix the address of Id and Data buffer and copy them, which is
  // continous memory and close to the base structure of pad entry.
  //
  PadEntry->Id    = (EFI_IPSEC_PAD_ID *) ALIGN_POINTER ((PadEntry + 1), sizeof (UINTN));
  PadEntry->Data  = (EFI_IPSEC_PAD_DATA *) ALIGN_POINTER ((PadEntry->Id + 1), sizeof (UINTN));

  CopyMem (PadEntry->Id, PadId, sizeof (EFI_IPSEC_PAD_ID));

  PadEntry->Data->AuthProtocol  = PadData->AuthProtocol;
  PadEntry->Data->AuthMethod    = PadData->AuthMethod;
  PadEntry->Data->IkeIdFlag     = PadData->IkeIdFlag;

  if (PadData->AuthData != NULL) {
    PadEntry->Data->AuthDataSize  = PadData->AuthDataSize;
    PadEntry->Data->AuthData      = (VOID *) ALIGN_POINTER (PadEntry->Data + 1, sizeof (UINTN));
    CopyMem (
      PadEntry->Data->AuthData,
      PadData->AuthData,
      PadData->AuthDataSize
      );
  } else {
    PadEntry->Data->AuthDataSize  = 0;
    PadEntry->Data->AuthData      = NULL;
  }

  if (PadData->RevocationData != NULL) {
    PadEntry->Data->RevocationDataSize  = PadData->RevocationDataSize;
    PadEntry->Data->RevocationData      = (VOID *) ALIGN_POINTER (
                                                    ((UINT8 *) (PadEntry->Data + 1) + PadData->AuthDataSize),
                                                    sizeof (UINTN)
                                                    );
    CopyMem (
      PadEntry->Data->RevocationData,
      PadData->RevocationData,
      PadData->RevocationDataSize
      );
  } else {
    PadEntry->Data->RevocationDataSize  = 0;
    PadEntry->Data->RevocationData      = NULL;
  }
  //
  // Insert the new pad entry.
  //
  InsertTailList (EntryInsertBefore, &PadEntry->List);

  return EFI_SUCCESS;
}

/**
  This function lookup the data entry from IPsec SPD. Return the configuration 
  value of the specified SPD Entry.

  @param[in]      Selector      Pointer to an entry selector which is an identifier 
                                of the SPD entry.
  @param[in, out] DataSize      On output the size of data returned in Data.
  @param[out]     Data          The buffer to return the contents of the IPsec 
                                configuration data. The type of the data buffer 
                                is associated with the DataType. 
 
  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.
  @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.
  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.
  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been
                                updated with the size needed to complete the request.

**/
EFI_STATUS
GetSpdEntry (
  IN     EFI_IPSEC_CONFIG_SELECTOR       *Selector,
  IN OUT UINTN                           *DataSize,
     OUT VOID                            *Data
  )
{
  IPSEC_SPD_ENTRY         *SpdEntry;
  IPSEC_SAD_ENTRY         *SadEntry;
  EFI_IPSEC_SPD_SELECTOR  *SpdSel;
  EFI_IPSEC_SPD_DATA      *SpdData;
  LIST_ENTRY              *SpdList;
  LIST_ENTRY              *SpdSas;
  LIST_ENTRY              *Entry;
  UINTN                   RequiredSize;

  SpdSel  = &Selector->SpdSelector;
  SpdData = (EFI_IPSEC_SPD_DATA *) Data;
  SpdList = &mConfigData[IPsecConfigDataTypeSpd];

  NET_LIST_FOR_EACH (Entry, SpdList) {
    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);

    //
    // Find the required SPD entry
    //
    if (CompareSpdSelector (
          (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,
          (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector
          )) {

      RequiredSize = IpSecGetSizeOfSpdData (SpdEntry->Data);
      if (*DataSize < RequiredSize) {
        *DataSize = RequiredSize;
        return EFI_BUFFER_TOO_SMALL;
      }

      if (SpdData == NULL) {
        return EFI_INVALID_PARAMETER;
      }

      *DataSize = RequiredSize;

      //
      // Extract and fill all SaId array from the SPD.sas list
      //
      SpdSas              = &SpdEntry->Data->Sas;
      SpdData->SaIdCount  = 0;

      NET_LIST_FOR_EACH (Entry, SpdSas) {
        SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);
        CopyMem (
          &SpdData->SaId[SpdData->SaIdCount++],
          SadEntry->Id,
          sizeof (EFI_IPSEC_SA_ID)
          );
      }
      //
      // Fill the other fields in SPD data.
      //
      CopyMem (SpdData->Name, SpdEntry->Data->Name, sizeof (SpdData->Name));

      SpdData->PackageFlag      = SpdEntry->Data->PackageFlag;
      SpdData->TrafficDirection = SpdEntry->Data->TrafficDirection;
      SpdData->Action           = SpdEntry->Data->Action;
      
      if (SpdData->Action != EfiIPsecActionProtect) {
        SpdData->ProcessingPolicy = NULL;
      } else {
        SpdData->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ((UINT8 *) SpdData + sizeof (EFI_IPSEC_SPD_DATA) + (SpdData->SaIdCount - 1) * sizeof (EFI_IPSEC_SA_ID));

        IpSecDuplicateProcessPolicy (
          SpdData->ProcessingPolicy,
          SpdEntry->Data->ProcessingPolicy
          );
      }

      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}

/**
  This function lookup the data entry from IPsec SAD. Return the configuration 
  value of the specified SAD Entry.

  @param[in]      Selector      Pointer to an entry selector which is an identifier 
                                of the SAD entry.
  @param[in, out] DataSize      On output, the size of data returned in Data.
  @param[out]     Data          The buffer to return the contents of the IPsec 
                                configuration data. The type of the data buffer 
                                is associated with the DataType. 
 
  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.
  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.
  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been
                                updated with the size needed to complete the request.

**/
EFI_STATUS
GetSadEntry (
  IN     EFI_IPSEC_CONFIG_SELECTOR     *Selector,
  IN OUT UINTN                         *DataSize,
     OUT VOID                          *Data
  )
{
  IPSEC_SAD_ENTRY   *SadEntry;
  LIST_ENTRY        *Entry;
  LIST_ENTRY        *SadList;
  EFI_IPSEC_SA_ID   *SaId;
  EFI_IPSEC_SA_DATA2 *SaData;
  UINTN             RequiredSize;

  SaId    = &Selector->SaId;
  SaData  = (EFI_IPSEC_SA_DATA2 *) Data;
  SadList = &mConfigData[IPsecConfigDataTypeSad];

  NET_LIST_FOR_EACH (Entry, SadList) {
    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);

    //
    // Find the required SAD entry.
    //
    if (CompareSaId (
         (EFI_IPSEC_CONFIG_SELECTOR *) SaId,
         (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id
         )) {
      //
      // Calculate the required size of the SAD entry.
      // Data Layout is follows:
      // |EFI_IPSEC_SA_DATA
      // |AuthKey
      // |EncryptKey  (Optional)
      // |SpdSelector (Optional)     
      // 
      RequiredSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA2));

      if (SaId->Proto == EfiIPsecAH) {
        RequiredSize  = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength);
      } else {
        RequiredSize  = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength);
        RequiredSize  = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength);
      }

      if (SadEntry->Data->SpdSelector != NULL) {
        RequiredSize += SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdSelector);
      }
      
      if (*DataSize < RequiredSize) {
        *DataSize = RequiredSize;
        return EFI_BUFFER_TOO_SMALL;
      }
      
      //
      // Fill the data fields of SAD entry.
      //
      *DataSize                 = RequiredSize;
      SaData->Mode              = SadEntry->Data->Mode;
      SaData->SNCount           = SadEntry->Data->SequenceNumber;
      SaData->AntiReplayWindows = SadEntry->Data->AntiReplayWindowSize;

      CopyMem (
        &SaData->SaLifetime,
        &SadEntry->Data->SaLifetime,
        sizeof (EFI_IPSEC_SA_LIFETIME)
        );

      ZeroMem (
        &SaData->AlgoInfo,
        sizeof (EFI_IPSEC_ALGO_INFO)
        );

      if (SaId->Proto == EfiIPsecAH) {
        //
        // Copy AH alogrithm INFO to SaData
        //
        SaData->AlgoInfo.AhAlgoInfo.AuthAlgoId    = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthAlgoId;
        SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength;
        if (SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength != 0) {
          SaData->AlgoInfo.AhAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));
          CopyMem (
            SaData->AlgoInfo.AhAlgoInfo.AuthKey,
            SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKey,
            SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength
            );
        }
      } else if (SaId->Proto == EfiIPsecESP) {
        //
        // Copy ESP alogrithem INFO to SaData
        //
        SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId     = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId;
        SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength  = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength;
        if (SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {
          SaData->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));
          CopyMem (
            SaData->AlgoInfo.EspAlgoInfo.AuthKey,
            SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
            SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength
            );
        }

        SaData->AlgoInfo.EspAlgoInfo.EncAlgoId    = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId;
        SaData->AlgoInfo.EspAlgoInfo.EncKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength;

        if (SaData->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {
          SaData->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (
                                                          ((UINT8 *) (SaData + 1) +
                                                            SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength),
                                                            sizeof (UINTN)
                                                            );
          CopyMem (
            SaData->AlgoInfo.EspAlgoInfo.EncKey,
            SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
            SaData->AlgoInfo.EspAlgoInfo.EncKeyLength
            );
        }
      }

      SaData->PathMTU = SadEntry->Data->PathMTU;

      //
      // Fill Tunnel Address if it is Tunnel Mode
      //
      if (SadEntry->Data->Mode == EfiIPsecTunnel) {
        CopyMem (
          &SaData->TunnelDestinationAddress,
          &SadEntry->Data->TunnelDestAddress,
          sizeof (EFI_IP_ADDRESS)
          );
        CopyMem (
          &SaData->TunnelSourceAddress,
          &SadEntry->Data->TunnelSourceAddress,
          sizeof (EFI_IP_ADDRESS)
          );
      }
      //
      // Fill the spd selector field of SAD data
      //
      if (SadEntry->Data->SpdSelector != NULL) {

        SaData->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) (
                                (UINT8 *)SaData +
                                RequiredSize -
                                SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdSelector)
                                );
       
        DuplicateSpdSelector (
          (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector,
          (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdSelector,
          NULL
          );

      } else {

        SaData->SpdSelector = NULL;
      }

      SaData->ManualSet = SadEntry->Data->ManualSet;

      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}

/**
  This function lookup the data entry from IPsec PAD. Return the configuration 
  value of the specified PAD Entry.

  @param[in]      Selector      Pointer to an entry selector which is an identifier 
                                of the PAD entry.
  @param[in, out] DataSize      On output the size of data returned in Data.
  @param[out]     Data          The buffer to return the contents of the IPsec 
                                configuration data. The type of the data buffer 
                                is associated with the DataType. 
 
  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.
  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.
  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been
                                updated with the size needed to complete the request.

**/
EFI_STATUS
GetPadEntry (
  IN     EFI_IPSEC_CONFIG_SELECTOR   *Selector,
  IN OUT UINTN                       *DataSize,
     OUT VOID                        *Data
  )
{
  IPSEC_PAD_ENTRY     *PadEntry;
  LIST_ENTRY          *PadList;
  LIST_ENTRY          *Entry;
  EFI_IPSEC_PAD_ID    *PadId;
  EFI_IPSEC_PAD_DATA  *PadData;
  UINTN               RequiredSize;

  PadId   = &Selector->PadId;
  PadData = (EFI_IPSEC_PAD_DATA *) Data;
  PadList = &mConfigData[IPsecConfigDataTypePad];

  NET_LIST_FOR_EACH (Entry, PadList) {
    PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);

    //
    // Find the required pad entry.
    //
    if (ComparePadId (
          (EFI_IPSEC_CONFIG_SELECTOR *) PadId,
          (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id
          )) {
      //
      // Calculate the required size of the pad entry.
      //
      RequiredSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));
      RequiredSize  = ALIGN_VARIABLE (RequiredSize + PadEntry->Data->AuthDataSize);
      RequiredSize += PadEntry->Data->RevocationDataSize;

      if (*DataSize < RequiredSize) {
        *DataSize = RequiredSize;
        return EFI_BUFFER_TOO_SMALL;
      }
      //
      // Fill the data fields of pad entry
      //
      *DataSize             = RequiredSize;
      PadData->AuthProtocol = PadEntry->Data->AuthProtocol;
      PadData->AuthMethod   = PadEntry->Data->AuthMethod;
      PadData->IkeIdFlag    = PadEntry->Data->IkeIdFlag;

      //
      // Copy Authentication data.
      //
      if (PadEntry->Data->AuthData != NULL) {

        PadData->AuthDataSize = PadEntry->Data->AuthDataSize;
        PadData->AuthData     = (VOID *) ALIGN_POINTER ((PadData + 1), sizeof (UINTN));
        CopyMem (
          PadData->AuthData,
          PadEntry->Data->AuthData,
          PadData->AuthDataSize
          );
      } else {

        PadData->AuthDataSize = 0;
        PadData->AuthData     = NULL;
      }
      //
      // Copy Revocation Data.
      //
      if (PadEntry->Data->RevocationData != NULL) {

        PadData->RevocationDataSize = PadEntry->Data->RevocationDataSize;
        PadData->RevocationData     = (VOID *) ALIGN_POINTER (
                                                 ((UINT8 *) (PadData + 1) + PadData->AuthDataSize),
                                                  sizeof (UINTN)
                                                  );
        CopyMem (
          PadData->RevocationData,
          PadEntry->Data->RevocationData,
          PadData->RevocationDataSize
          );
      } else {

        PadData->RevocationDataSize = 0;
        PadData->RevocationData     = NULL;
      }

      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}

/**
  Copy Source Process Policy to the Destination Process Policy.

  @param[in]  Dst                  Pointer to the Source Process Policy.
  @param[in]  Src                  Pointer to the Destination Process Policy.

**/
VOID
IpSecDuplicateProcessPolicy (
  IN EFI_IPSEC_PROCESS_POLICY            *Dst,
  IN EFI_IPSEC_PROCESS_POLICY            *Src
  )
{
  //
  // Firstly copy the structure content itself.
  //
  CopyMem (Dst, Src, sizeof (EFI_IPSEC_PROCESS_POLICY));

  //
  // Recursively copy the tunnel option if needed.
  //
  if (Dst->Mode != EfiIPsecTunnel) {
    ASSERT (Dst->TunnelOption == NULL);
  } else {
    Dst->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER ((Dst + 1), sizeof (UINTN));
    CopyMem (
      Dst->TunnelOption,
      Src->TunnelOption,
      sizeof (EFI_IPSEC_TUNNEL_OPTION)
      );
  }
}

/**
  Calculate the a whole size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed
  to by the pointer members.

  @param[in]  SpdData             Pointer to a specified EFI_IPSEC_SPD_DATA.

  @return the whole size the specified EFI_IPSEC_SPD_DATA.

**/
UINTN
IpSecGetSizeOfEfiSpdData (
  IN EFI_IPSEC_SPD_DATA               *SpdData
  )
{
  UINTN Size;

  Size = ALIGN_VARIABLE (sizeof (IPSEC_SPD_DATA));

  if (SpdData->Action == EfiIPsecActionProtect) {
    Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_PROCESS_POLICY));

    if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {
      Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_TUNNEL_OPTION));
    }
  }

  return Size;
}

/**
  Calculate the a whole size of IPSEC_SPD_DATA which includes the buffer size pointed
  to by the pointer members and the buffer size used by the Sa List. 

  @param[in]  SpdData       Pointer to the specified IPSEC_SPD_DATA.

  @return the whole size of IPSEC_SPD_DATA.

**/
UINTN
IpSecGetSizeOfSpdData (
  IN IPSEC_SPD_DATA                   *SpdData
  )
{
  UINTN       Size;
  LIST_ENTRY  *Link;

  Size = sizeof (EFI_IPSEC_SPD_DATA) - sizeof (EFI_IPSEC_SA_ID);

  if (SpdData->Action == EfiIPsecActionProtect) {
    Size += sizeof (EFI_IPSEC_PROCESS_POLICY);

    if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {
      Size += sizeof (EFI_IPSEC_TUNNEL_OPTION);
    }
  }

  NET_LIST_FOR_EACH (Link, &SpdData->Sas) {
    Size += sizeof (EFI_IPSEC_SA_ID);
  }

  return Size;
}

/**
  Get the IPsec Variable.

  Get the all variables which start with the string contained in VaraiableName.
  Since all IPsec related variable store in continual space, those kinds of 
  variable can be searched by the EfiGetNextVariableName. Those variables also are 
  returned in a continual buffer.
  
  @param[in]      VariableName          Pointer to a specified Variable Name.
  @param[in]      VendorGuid            Pointer to a specified Vendor Guid.
  @param[in]      Attributes            Point to memory location to return the attributes 
                                        of variable. If the point is NULL, the parameter 
                                        would be ignored.
  @param[in, out] DataSize              As input, point to the maximum size of return 
                                        Data-Buffer. As output, point to the actual 
                                        size of the returned Data-Buffer.
  @param[in]      Data                  Point to return Data-Buffer.
        
  @retval  EFI_ABORTED           If the Variable size which contained in the variable
                                 structure doesn't match the variable size obtained 
                                 from the EFIGetVariable.
  @retval  EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has
                                 been updated with the size needed to complete the request.   
  @retval  EFI_SUCCESS           The function completed successfully.
  @retval  others                Other errors found during the variable getting.
**/
EFI_STATUS
IpSecGetVariable (
  IN     CHAR16                       *VariableName,
  IN     EFI_GUID                     *VendorGuid,
  IN     UINT32                       *Attributes, OPTIONAL
  IN OUT UINTN                        *DataSize,
  IN     VOID                         *Data
  )
{
  EFI_STATUS            Status;
  EFI_GUID              VendorGuidI;
  UINTN                 VariableNameLength;
  CHAR16                *VariableNameI;
  UINTN                 VariableNameISize;
  UINTN                 VariableNameISizeNew;
  UINTN                 VariableIndex;
  UINTN                 VariableCount;
  IP_SEC_VARIABLE_INFO  IpSecVariableInfo;
  UINTN                 DataSizeI;

  //
  // The variable name constructor is "VariableName + Info/0001/0002/... + NULL".
  // So the varialbe name is like "VariableNameInfo", "VariableName0001", ...
  // "VariableNameNULL".
  //
  VariableNameLength  = StrLen (VariableName);
  VariableNameISize   = (VariableNameLength + 5) * sizeof (CHAR16);
  VariableNameI       = AllocateZeroPool (VariableNameISize);
  if (VariableNameI == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }
  
  //
  // Construct the varible name of ipsecconfig meta data.
  //
  UnicodeSPrint (VariableNameI, VariableNameISize, L"%s%s", VariableName, L"Info");

  DataSizeI = sizeof (IpSecVariableInfo);

  Status = gRT->GetVariable (
                  VariableNameI,
                  VendorGuid,
                  Attributes,
                  &DataSizeI,
                  &IpSecVariableInfo
                  );
  if (EFI_ERROR (Status)) {
    goto ON_EXIT;
  }

  if (*DataSize < IpSecVariableInfo.VariableSize) {
    *DataSize = IpSecVariableInfo.VariableSize;
    Status    = EFI_BUFFER_TOO_SMALL;
    goto ON_EXIT;
  }

  VariableCount     = IpSecVariableInfo.VariableCount;
  VariableNameI[0]  = L'\0';

  while (VariableCount != 0) {
    //
    // Get the variable name one by one in the variable database.
    //
    VariableNameISizeNew = VariableNameISize;
    Status = gRT->GetNextVariableName (
                    &VariableNameISizeNew,
                    VariableNameI,
                    &VendorGuidI
                    );
    if (Status == EFI_BUFFER_TOO_SMALL) {
      VariableNameI = ReallocatePool (
                        VariableNameISize,
                        VariableNameISizeNew,
                        VariableNameI
                        );
      if (VariableNameI == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        break;
      }
      VariableNameISize = VariableNameISizeNew;

      Status = gRT->GetNextVariableName (
                      &VariableNameISizeNew,
                      VariableNameI,
                      &VendorGuidI
                      );
    }

    if (EFI_ERROR (Status)) {
      break;
    }
    //
    // Check whether the current variable is the required "ipsecconfig".
    //
    if (StrnCmp (VariableNameI, VariableName, VariableNameLength) == 0 ||
        CompareGuid (VendorGuid, &VendorGuidI)
        ) {
      //
      // Parse the variable count of the current ipsecconfig data.
      //
      VariableIndex = StrDecimalToUintn (VariableNameI + VariableNameLength);
      if (VariableIndex!= 0 && VariableIndex <= IpSecVariableInfo.VariableCount) {
        //
        // Get the variable size of the current ipsecconfig data.
        //
        DataSizeI = 0;
        Status = gRT->GetVariable (
                        VariableNameI,
                        VendorGuid,
                        Attributes,
                        &DataSizeI,
                        NULL
                        );
        ASSERT (Status == EFI_BUFFER_TOO_SMALL);
        //
        // Validate the variable count and variable size.
        //
        if (VariableIndex != IpSecVariableInfo.VariableCount) {
          //
          // If the varaibe is not the last one, its size should be the max
          // size of the single variable.
          //
          if (DataSizeI != IpSecVariableInfo.SingleVariableSize) {
            return EFI_ABORTED;
          }
        } else {
          if (DataSizeI != IpSecVariableInfo.VariableSize % IpSecVariableInfo.SingleVariableSize) {
            return EFI_ABORTED;
          }
        }
        //
        // Get the variable data of the current ipsecconfig data and
        // store it into user buffer continously.
        //
        Status = gRT->GetVariable (
                        VariableNameI,
                        VendorGuid,
                        Attributes,
                        &DataSizeI,
                        (UINT8 *) Data + (VariableIndex - 1) * IpSecVariableInfo.SingleVariableSize
                        );
        ASSERT_EFI_ERROR (Status);
        VariableCount--;
      }
    }
  }
  //
  // The VariableCount in "VariableNameInfo" varaible should have the correct
  // numbers of variables which name starts with VariableName.
  //
  if (VariableCount != 0) {
    Status = EFI_ABORTED;
  }

ON_EXIT:
  if (VariableNameI != NULL) {
    FreePool (VariableNameI);
  }
  return Status;
}

/**
  Set the IPsec variables.

  Set all IPsec variables which start with the specified variable name. Those variables
  are set one by one.

  @param[in]  VariableName  The name of the vendor's variable. It is a
                            Null-Terminated Unicode String.
  @param[in]  VendorGuid    Unify identifier for vendor.
  @param[in]  Attributes    Point to memory location to return the attributes of 
                            variable. If the point is NULL, the parameter would be ignored.
  @param[in]  DataSize      The size in bytes of Data-Buffer.
  @param[in]  Data          Points to the content of the variable.

  @retval  EFI_SUCCESS      The firmware successfully stored the variable and its data, as
                            defined by the Attributes.
  @retval  others           Storing the variables failed.      

**/
EFI_STATUS
IpSecSetVariable (
  IN CHAR16                           *VariableName,
  IN EFI_GUID                         *VendorGuid,
  IN UINT32                           Attributes,
  IN UINTN                            DataSize,
  IN VOID                             *Data
  )
{
  EFI_STATUS            Status;
  CHAR16                *VariableNameI;
  UINTN                 VariableNameSize;
  UINTN                 VariableIndex;
  IP_SEC_VARIABLE_INFO  IpSecVariableInfo;
  UINT64                MaximumVariableStorageSize;
  UINT64                RemainingVariableStorageSize;
  UINT64                MaximumVariableSize;

  Status = gRT->QueryVariableInfo (
                  Attributes,
                  &MaximumVariableStorageSize,
                  &RemainingVariableStorageSize,
                  &MaximumVariableSize
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  
  //
  // "VariableName + Info/0001/0002/... + NULL"
  //
  VariableNameSize  = (StrLen (VariableName) + 5) * sizeof (CHAR16);
  VariableNameI     = AllocateZeroPool (VariableNameSize);

  if (VariableNameI == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }
  //
  // Construct the variable of ipsecconfig general information. Like the total
  // numbers of the Ipsecconfig variables, the total size of all ipsecconfig variables.
  //
  UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%s", VariableName, L"Info");
  MaximumVariableSize -= VariableNameSize;
  
  IpSecVariableInfo.VariableCount       = (UINT32) ((DataSize + (UINTN) MaximumVariableSize - 1) / (UINTN) MaximumVariableSize);
  IpSecVariableInfo.VariableSize        = (UINT32) DataSize;
  IpSecVariableInfo.SingleVariableSize  = (UINT32) MaximumVariableSize;

  //
  // Set the variable of ipsecconfig general information.
  //
  Status = gRT->SetVariable (
                  VariableNameI,
                  VendorGuid,
                  Attributes,
                  sizeof (IpSecVariableInfo),
                  &IpSecVariableInfo
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Error set ipsecconfig meta data with %r\n", Status));
    goto ON_EXIT;
  }

  for (VariableIndex = 0; VariableIndex < IpSecVariableInfo.VariableCount; VariableIndex++) {
    //
    // Construct and set the variable of ipsecconfig data one by one.
    // The index of variable name begin from 0001, and the varaible name
    // likes "VariableName0001", "VaraiableName0002"....
    // 
    UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%04d", VariableName, VariableIndex + 1);
    Status = gRT->SetVariable (
                    VariableNameI,
                    VendorGuid,
                    Attributes,
                    (VariableIndex == IpSecVariableInfo.VariableCount - 1) ?
                    (DataSize % (UINTN) MaximumVariableSize) :
                    (UINTN) MaximumVariableSize,
                    (UINT8 *) Data + VariableIndex * (UINTN) MaximumVariableSize
                    );

    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "Error set ipsecconfig variable data with %r\n", Status));
      goto ON_EXIT;
    }
  }

ON_EXIT:
  if (VariableNameI != NULL) {
    FreePool (VariableNameI);
  }

  return Status;
}

/**
  Return the configuration value for the EFI IPsec driver. 

  This function lookup the data entry from IPsec database or IKEv2 configuration
  information. The expected data type and unique identification are described in
  DataType and Selector parameters.        

  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
  @param[in]      DataType      The type of data to retrieve.
  @param[in]      Selector      Pointer to an entry selector that is an identifier of the IPsec 
                                configuration data entry.
  @param[in, out] DataSize      On output the size of data returned in Data.
  @param[out]     Data          The buffer to return the contents of the IPsec configuration data. 
                                The type of the data buffer associated with the DataType. 
 
  @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.
                                - Selector is NULL.
                                - DataSize is NULL.
                                - Data is NULL and *DataSize is not zero
  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.
  @retval EFI_UNSUPPORTED       The specified DataType is not supported.
  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been
                                updated with the size needed to complete the request.

**/
EFI_STATUS
EFIAPI
EfiIpSecConfigGetData (
  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,
  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,
  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,
  IN OUT UINTN                        *DataSize,
     OUT VOID                         *Data
  )
{
  if (This == NULL || Selector == NULL || DataSize == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (*DataSize != 0 && Data == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (DataType >= IPsecConfigDataTypeMaximum) {
    return EFI_UNSUPPORTED;
  }

  return mGetPolicyEntry[DataType](Selector, DataSize, Data);
}

/**
  Set the security association, security policy and peer authorization configuration
  information for the EFI IPsec driver. 

  This function is used to set the IPsec configuration information of type DataType for
  the EFI IPsec driver.
  The IPsec configuration data has a unique selector/identifier separately to identify
  a data entry. The selector structure depends on DataType's definition.
  Using SetData() with a Data of NULL causes the IPsec configuration data entry identified
  by DataType and Selector to be deleted.        

  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
  @param[in] DataType           The type of data to be set.
  @param[in] Selector           Pointer to an entry selector on operated configuration data 
                                specified by DataType. A NULL Selector causes the entire 
                                specified-type configuration information to be flushed.
  @param[in] Data               The data buffer to be set. The structure of the data buffer is 
                                associated with the DataType.
  @param[in] InsertBefore       Pointer to one entry selector which describes the expected
                                position the new data entry will be added. If InsertBefore is NULL,
                                the new entry will be appended to the end of the database.
 
  @retval EFI_SUCCESS           The specified configuration entry data was set successfully.
  @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
                                - This is NULL.
  @retval EFI_UNSUPPORTED       The specified DataType is not supported.
  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.

**/
EFI_STATUS
EFIAPI
EfiIpSecConfigSetData (
  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,
  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,
  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector,
  IN VOID                             *Data,
  IN EFI_IPSEC_CONFIG_SELECTOR        *InsertBefore OPTIONAL
  )
{
  EFI_STATUS  Status;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (DataType >= IPsecConfigDataTypeMaximum) {
    return EFI_UNSUPPORTED;
  }
  
  Status = mSetPolicyEntry[DataType](Selector, Data, InsertBefore);

  if (!EFI_ERROR (Status) && !mSetBySelf) {
    //
    // Save the updated config data into variable.
    //
    IpSecConfigSave ();
  }

  return Status;
}

/**
  Enumerates the current selector for IPsec configuration data entry. 

  This function is called multiple times to retrieve the entry Selector in IPsec
  configuration database. On each call to GetNextSelector(), the next entry 
  Selector are retrieved into the output interface.
 
  If the entire IPsec configuration database has been iterated, the error 
  EFI_NOT_FOUND is returned.
  If the Selector buffer is too small for the next Selector copy, an 
  EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect 
  the size of buffer needed.

  On the initial call to GetNextSelector() to start the IPsec configuration database
  search, a pointer to the buffer with all zero value is passed in Selector. Calls 
  to SetData() between calls to GetNextSelector may produce unpredictable results.         

  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
  @param[in]      DataType      The type of IPsec configuration data to retrieve.
  @param[in, out] SelectorSize  The size of the Selector buffer.
  @param[in, out] Selector      On input, supplies the pointer to last Selector that was 
                                returned by GetNextSelector().
                                On output, returns one copy of the current entry Selector
                                of a given DataType. 
 
  @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.
                                - SelectorSize is NULL.
                                - Selector is NULL.
  @retval EFI_NOT_FOUND         The next configuration data entry was not found.
  @retval EFI_UNSUPPORTED       The specified DataType is not supported.
  @retval EFI_BUFFER_TOO_SMALL  The SelectorSize is too small for the result. This parameter
                                has been updated with the size needed to complete the search 
                                request.

**/
EFI_STATUS
EFIAPI
EfiIpSecConfigGetNextSelector (
  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,
  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,
  IN OUT UINTN                        *SelectorSize,
  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *Selector
  )
{
  LIST_ENTRY                *Link;
  IPSEC_COMMON_POLICY_ENTRY *CommonEntry;
  BOOLEAN                   IsFound;

  if (This == NULL || Selector == NULL || SelectorSize == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (DataType >= IPsecConfigDataTypeMaximum) {
    return EFI_UNSUPPORTED;
  }

  IsFound = FALSE;

  NET_LIST_FOR_EACH (Link, &mConfigData[DataType]) {
    CommonEntry = BASE_CR (Link, IPSEC_COMMON_POLICY_ENTRY, List);

    if (IsFound || (BOOLEAN)(mIsZeroSelector[DataType](Selector))) {
      //
      // If found the appointed entry, then duplicate the next one and return,
      // or if the appointed entry is zero, then return the first one directly.
      //
      return mDuplicateSelector[DataType](Selector, CommonEntry->Selector, SelectorSize);
    } else {
      //
      // Set the flag if find the appointed entry.
      //
      IsFound = mCompareSelector[DataType](Selector, CommonEntry->Selector);
    }
  }

  return EFI_NOT_FOUND;
}

/**
  Register an event that is to be signaled whenever a configuration process on the
  specified IPsec configuration information is done. 

  The register function is not surpport now and always returns EFI_UNSUPPORTED.
  
  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
  @param[in] DataType           The type of data to be registered the event for.
  @param[in] Event              The event to be registered.
 
  @retval EFI_SUCCESS           The event is registered successfully.
  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.
  @retval EFI_ACCESS_DENIED     The Event is already registered for the DataType.
  @retval EFI_UNSUPPORTED       The notify registration is unsupported, or the specified
                                DataType is not supported.

**/
EFI_STATUS
EFIAPI
EfiIpSecConfigRegisterNotify (
  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,
  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,
  IN EFI_EVENT                        Event
  )
{
  return EFI_UNSUPPORTED;
}

/**
  Remove the specified event that was previously registered on the specified IPsec
  configuration data. 

  This function is not support now and alwasy return EFI_UNSUPPORTED.

  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
  @param[in] DataType           The configuration data type to remove the registered event for.
  @param[in] Event              The event to be unregistered.
 
  @retval EFI_SUCCESS           The event was removed successfully.
  @retval EFI_NOT_FOUND         The Event specified by DataType could not be found in the 
                                database.
  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.
  @retval EFI_UNSUPPORTED       The notify registration is unsupported, or the specified
                                DataType is not supported.

**/
EFI_STATUS
EFIAPI
EfiIpSecConfigUnregisterNotify (
  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,
  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,
  IN EFI_EVENT                        Event
  )
{
  return EFI_UNSUPPORTED;
}

/**
  Copy whole data in specified EFI_SIPEC_CONFIG_SELECTOR and the Data to a buffer.

  This function is a caller defined function, and it is called by the IpSecVisitConfigData().
  The orignal caller is IpSecConfigSave(), which calls the IpsecVisitConfigData() to 
  copy all types of IPsec Config datas into one buffer and store this buffer into firmware in
  the form of several variables.
  
  @param[in]      Type              A specified IPSEC_CONFIG_DATA_TYPE.
  @param[in]      Selector          Points to a EFI_IPSEC_CONFIG_SELECTOR to be copied
                                    to the buffer.
  @param[in]      Data              Points to data to be copied to the buffer. The
                                    Data type is related to the Type.
  @param[in]      SelectorSize      The size of the Selector.
  @param[in]      DataSize          The size of the Data.
  @param[in, out] Buffer            The buffer to store the Selector and Data.

  @retval EFI_SUCCESS            Copy the Selector and Data to a buffer successfully.
  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.

**/
EFI_STATUS
IpSecCopyPolicyEntry (
  IN     EFI_IPSEC_CONFIG_DATA_TYPE   Type,
  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,
  IN     VOID                         *Data,
  IN     UINTN                        SelectorSize,
  IN     UINTN                        DataSize,
  IN OUT IPSEC_VARIABLE_BUFFER        *Buffer
  )
{
  IPSEC_VAR_ITEM_HEADER SelectorHeader;
  IPSEC_VAR_ITEM_HEADER DataHeader;
  UINTN                 EntrySize;
  UINT8                 *TempPoint;
  
  if (Type == IPsecConfigDataTypeSad) {
    //
    // Don't save automatically-generated SA entry into variable.
    //
    if (((EFI_IPSEC_SA_DATA2 *) Data)->ManualSet == FALSE) {
      return EFI_SUCCESS;
    }
  }
  //
  // Increase the capacity size of the buffer if needed.
  //
  EntrySize  = ALIGN_VARIABLE (sizeof (SelectorHeader));
  EntrySize  = ALIGN_VARIABLE (EntrySize + SelectorSize);
  EntrySize  = ALIGN_VARIABLE (EntrySize + sizeof (SelectorHeader));
  EntrySize  = ALIGN_VARIABLE (EntrySize + DataSize);
  
  //EntrySize = SelectorSize + DataSize + 2 * sizeof (SelectorHeader);
  if (Buffer->Capacity - Buffer->Size < EntrySize) {
    //
    // Calculate the required buffer
    //
    Buffer->Capacity += EntrySize;
    TempPoint         = AllocatePool (Buffer->Capacity);
    
    if (TempPoint == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    //
    // Copy the old Buffer to new buffer and free the old one.
    //
    CopyMem (TempPoint, Buffer->Ptr, Buffer->Size);
    FreePool (Buffer->Ptr);
    
    Buffer->Ptr       =  TempPoint;    
  }

  mFixPolicyEntry[Type](Selector, Data);

  //
  // Fill the selector header and copy it into buffer.
  //
  SelectorHeader.Type = (UINT8) (Type | IPSEC_VAR_ITEM_HEADER_LOGO_BIT);
  SelectorHeader.Size = (UINT16) SelectorSize;

  CopyMem (
    Buffer->Ptr + Buffer->Size,
    &SelectorHeader,
    sizeof (SelectorHeader)
    );
  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + sizeof (SelectorHeader));
  
  //
  // Copy the selector into buffer.
  //
  CopyMem (
    Buffer->Ptr + Buffer->Size,
    Selector,
    SelectorSize
    );
  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + SelectorSize);

  //
  // Fill the data header and copy it into buffer.
  //
  DataHeader.Type = (UINT8) Type;
  DataHeader.Size = (UINT16) DataSize;

  CopyMem (
    Buffer->Ptr + Buffer->Size,
    &DataHeader,
    sizeof (DataHeader)
    );
  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + sizeof (DataHeader));
  //
  // Copy the data into buffer.
  //
  CopyMem (
    Buffer->Ptr + Buffer->Size,
    Data,
    DataSize
    );
  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + DataSize);
  
  mUnfixPolicyEntry[Type](Selector, Data);

  return EFI_SUCCESS;
}

/**
  Visit all IPsec Configurations of specified Type and call the caller defined
  interface.

  @param[in]  DataType          The specified IPsec Config Data Type.
  @param[in]  Routine           The function defined by the caller.
  @param[in]  Context           The data passed to the Routine.

  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated
  @retval EFI_SUCCESS            This function completed successfully.

**/
EFI_STATUS
IpSecVisitConfigData (
  IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
  IN IPSEC_COPY_POLICY_ENTRY    Routine,
  IN VOID                       *Context
  )
{
  EFI_STATUS                GetNextStatus;
  EFI_STATUS                GetDataStatus;
  EFI_STATUS                RoutineStatus;
  EFI_IPSEC_CONFIG_SELECTOR *Selector;
  VOID                      *Data;
  UINTN                     SelectorSize;
  UINTN                     DataSize;
  UINTN                     SelectorBufferSize;
  UINTN                     DataBufferSize;
  BOOLEAN                   FirstGetNext;

  FirstGetNext        = TRUE;
  DataBufferSize      = 0;
  Data                = NULL;
  SelectorBufferSize  = sizeof (EFI_IPSEC_CONFIG_SELECTOR);
  Selector            = AllocateZeroPool (SelectorBufferSize);

  if (Selector == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  while (TRUE) {
    //
    // Get the real size of the selector.
    //
    SelectorSize = SelectorBufferSize;
    GetNextStatus = EfiIpSecConfigGetNextSelector (
                      &mIpSecConfigInstance,
                      DataType,
                      &SelectorSize,
                      Selector
                      );
    if (GetNextStatus == EFI_BUFFER_TOO_SMALL) {
      FreePool (Selector);
      SelectorBufferSize = SelectorSize;
      //
      // Allocate zero pool for the first selector, while store the last
      // selector content for the other selectors.
      //
      if (FirstGetNext) {
        Selector = AllocateZeroPool (SelectorBufferSize);
      } else {
        Selector = AllocateCopyPool (SelectorBufferSize, Selector);
      }

      if (Selector == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }
      //
      // Get the content of the selector.
      //
      GetNextStatus = EfiIpSecConfigGetNextSelector (
                        &mIpSecConfigInstance,
                        DataType,
                        &SelectorSize,
                        Selector
                        );
    }

    if (EFI_ERROR (GetNextStatus)) {
      break;
    }

    FirstGetNext = FALSE;

    //
    // Get the real size of the policy entry according to the selector.
    //
    DataSize = DataBufferSize;
    GetDataStatus = EfiIpSecConfigGetData (
                      &mIpSecConfigInstance,
                      DataType,
                      Selector,
                      &DataSize,
                      Data
                      );
    if (GetDataStatus == EFI_BUFFER_TOO_SMALL) {
      if (Data != NULL) {
        FreePool (Data);
      }

      DataBufferSize  = DataSize;
      Data            = AllocateZeroPool (DataBufferSize);

      if (Data == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }
      //
      // Get the content of the policy entry according to the selector.
      //
      GetDataStatus = EfiIpSecConfigGetData (
                        &mIpSecConfigInstance,
                        DataType,
                        Selector,
                        &DataSize,
                        Data
                        );
    }

    if (EFI_ERROR (GetDataStatus)) {
      break;
    }
    //
    // Prepare the buffer of updated policy entry, which is stored in
    // the continous memory, and then save into variable later.
    //
    RoutineStatus = Routine (
                      DataType,
                      Selector,
                      Data,
                      SelectorSize,
                      DataSize,
                      Context
                      );
    if (EFI_ERROR (RoutineStatus)) {
      break;
    }
  }

  if (Data != NULL) {
    FreePool (Data);
  }

  if (Selector != NULL) {
    FreePool (Selector);
  }

  return EFI_SUCCESS;
}

/**
  This function is the subfunction of  EFIIpSecConfigSetData.

  This function call IpSecSetVaraible to set the IPsec Configuration into the firmware.

  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.
  @retval EFI_SUCCESS            Saved the configration successfully.
  @retval Others                 Other errors were found while obtaining the variable.

**/
EFI_STATUS
IpSecConfigSave (
  VOID
  )
{
  IPSEC_VARIABLE_BUFFER       Buffer;
  EFI_STATUS                  Status;
  EFI_IPSEC_CONFIG_DATA_TYPE  Type;

  Buffer.Size     = 0;
  Buffer.Capacity = IPSEC_DEFAULT_VARIABLE_SIZE;
  Buffer.Ptr      = AllocateZeroPool (Buffer.Capacity);

  if (Buffer.Ptr == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // For each policy database, prepare the contious buffer to save into variable.
  //
  for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {
    IpSecVisitConfigData (
      Type,
      (IPSEC_COPY_POLICY_ENTRY) IpSecCopyPolicyEntry,
      &Buffer
      );
  }
  //
  // Save the updated policy database into variable.
  //
  Status = IpSecSetVariable (
             IPSECCONFIG_VARIABLE_NAME,
             &gEfiIpSecConfigProtocolGuid,
             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
             Buffer.Size,
             Buffer.Ptr
             );

  FreePool (Buffer.Ptr);

  return Status;
}

/**
  Get the all IPSec configuration variables and store those variables
  to the internal data structure.

  This founction is called by IpSecConfigInitialize() which is to intialize the 
  IPsecConfiguration Protocol.

  @param[in]  Private            Point to IPSEC_PRIVATE_DATA.

  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated
  @retval EFI_SUCCESS            Restore the IPsec Configuration successfully.
  @retval  others                Other errors is found while obtaining the variable.

**/
EFI_STATUS
IpSecConfigRestore (
  IN IPSEC_PRIVATE_DATA           *Private
  )
{
  EFI_STATUS                  Status;
  UINTN                       BufferSize;
  UINT8                       *Buffer;
  IPSEC_VAR_ITEM_HEADER       *Header;
  UINT8                       *Ptr;
  EFI_IPSEC_CONFIG_SELECTOR   *Selector;
  EFI_IPSEC_CONFIG_DATA_TYPE  Type;
  VOID                        *Data;
  UINT8                       Value;
  UINTN                       Size;

  Value       = 0;
  Size        = sizeof (Value);
  BufferSize  = 0;
  Buffer      = NULL;

  Status = gRT->GetVariable (
                  IPSECCONFIG_STATUS_NAME,
                  &gEfiIpSecConfigProtocolGuid,
                  NULL,
                  &Size,
                  &Value
             );

  if (!EFI_ERROR (Status) && Value == IPSEC_STATUS_ENABLED) {
    Private->IpSec.DisabledFlag = FALSE;
  }
  //
  // Get the real size of policy database in variable.
  //
  Status = IpSecGetVariable (
             IPSECCONFIG_VARIABLE_NAME,
             &gEfiIpSecConfigProtocolGuid,
             NULL,
             &BufferSize,
             Buffer
             );
  if (Status == EFI_BUFFER_TOO_SMALL) {

    Buffer = AllocateZeroPool (BufferSize);
    if (Buffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    //
    // Get the content of policy database in variable.
    //
    Status = IpSecGetVariable (
               IPSECCONFIG_VARIABLE_NAME,
               &gEfiIpSecConfigProtocolGuid,
               NULL,
               &BufferSize,
               Buffer
               );
    if (EFI_ERROR (Status)) {
      FreePool (Buffer);
      return Status;
    }

    for (Ptr = Buffer; Ptr < Buffer + BufferSize;) {

      Header  = (IPSEC_VAR_ITEM_HEADER *) Ptr;
      Type    = (EFI_IPSEC_CONFIG_DATA_TYPE) (Header->Type & IPSEC_VAR_ITEM_HEADER_CONTENT_BIT);
      ASSERT (((Header->Type & 0x80) == IPSEC_VAR_ITEM_HEADER_LOGO_BIT) && (Type < IPsecConfigDataTypeMaximum));
      
      Selector  = (EFI_IPSEC_CONFIG_SELECTOR *) ALIGN_POINTER (Header + 1, sizeof (UINTN));
      Header    = (IPSEC_VAR_ITEM_HEADER *) ALIGN_POINTER (
                                              (UINT8 *) Selector + Header->Size, 
                                              sizeof (UINTN)
                                              );
      ASSERT (Header->Type == Type);

      Data = ALIGN_POINTER (Header + 1, sizeof (UINTN));

      mUnfixPolicyEntry[Type](Selector, Data);

      //
      // Update each policy entry according to the content in variable.
      //
      mSetBySelf = TRUE;
      Status = EfiIpSecConfigSetData (
                 &Private->IpSecConfig,
                 Type,
                 Selector,
                 Data,
                 NULL
                 );
      mSetBySelf = FALSE;

      if (EFI_ERROR (Status)) {
        FreePool (Buffer);
        return Status;
      }

      Ptr =  ALIGN_POINTER ((UINT8 *) Data + Header->Size, sizeof (UINTN));
    }

    FreePool (Buffer);
  }

  return EFI_SUCCESS;
}

/**
  Install and Initialize IPsecConfig protocol

  @param[in, out]  Private   Pointer to IPSEC_PRIVATE_DATA. After this function finish,
                             the pointer of IPsecConfig Protocol implementation will copy
                             into its IPsecConfig member.

  @retval     EFI_SUCCESS    Initialized the IPsecConfig Protocol successfully.
  @retval     Others         Initializing the IPsecConfig Protocol failed.
**/
EFI_STATUS
IpSecConfigInitialize (
  IN OUT IPSEC_PRIVATE_DATA        *Private
  )
{
  EFI_IPSEC_CONFIG_DATA_TYPE  Type;

  CopyMem (
    &Private->IpSecConfig,
    &mIpSecConfigInstance,
    sizeof (EFI_IPSEC_CONFIG_PROTOCOL)
    );

  //
  // Initialize the list head of policy database.
  //
  for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {
    InitializeListHead (&mConfigData[Type]);
  }
  //
  // Restore the content of policy database according to the variable.
  //
  IpSecConfigRestore (Private);

  return gBS->InstallMultipleProtocolInterfaces (
                &Private->Handle,
                &gEfiIpSecConfigProtocolGuid,
                &Private->IpSecConfig,
                NULL
                );
}
