/** @file
  The implementation of policy entry operation function in IpSecConfig application.

  Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>

  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php.

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "IpSecConfig.h"
#include "Indexer.h"
#include "Match.h"
#include "Helper.h"
#include "ForEach.h"
#include "PolicyEntryOperation.h"

/**
  Fill in EFI_IPSEC_SPD_SELECTOR through ParamPackage list.

  @param[out]     Selector        The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
  @param[in]      ParamPackage    The pointer to the ParamPackage list.
  @param[in, out] Mask            The pointer to the Mask.

  @retval EFI_SUCCESS              Fill in EFI_IPSEC_SPD_SELECTOR successfully.
  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

**/
EFI_STATUS
CreateSpdSelector (
     OUT EFI_IPSEC_SPD_SELECTOR    *Selector,
  IN     LIST_ENTRY                *ParamPackage,
  IN OUT UINT32                    *Mask
  )
{
  EFI_STATUS      Status;
  EFI_STATUS      ReturnStatus;
  CONST CHAR16    *ValueStr;

  Status       = EFI_SUCCESS;
  ReturnStatus = EFI_SUCCESS;

  //
  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
  //
  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local");
  if (ValueStr != NULL) {
    Selector->LocalAddressCount = 1;
    Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->LocalAddress);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
        mHiiHandle,
        mAppName,
        L"--local",
        ValueStr
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      *Mask |= LOCAL;
    }
  }

  //
  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
  //
  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote");
  if (ValueStr != NULL) {
    Selector->RemoteAddressCount = 1;
    Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->RemoteAddress);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
        mHiiHandle,
        mAppName,
        L"--remote",
        ValueStr
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      *Mask |= REMOTE;
    }
  }

  Selector->NextLayerProtocol = EFI_IPSEC_ANY_PROTOCOL;

  //
  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
  //
  Status = GetNumber (
             L"--proto",
             (UINT16) -1,
             &Selector->NextLayerProtocol,
             sizeof (UINT16),
             mMapIpProtocol,
             ParamPackage,
             FORMAT_NUMBER | FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= PROTO;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Selector->LocalPort  = EFI_IPSEC_ANY_PORT;
  Selector->RemotePort = EFI_IPSEC_ANY_PORT;

  //
  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
  //
  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local-port");
  if (ValueStr != NULL) {
    Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->LocalPort, &Selector->LocalPortRange);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
        mHiiHandle,
        mAppName,
        L"--local-port",
        ValueStr
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      *Mask |= LOCAL_PORT;
    }
  }

  //
  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
  //
  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote-port");
  if (ValueStr != NULL) {
    Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->RemotePort, &Selector->RemotePortRange);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
        mHiiHandle,
        mAppName,
        L"--remote-port",
        ValueStr
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      *Mask |= REMOTE_PORT;
    }
  }

  //
  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
  //
  Status = GetNumber (
             L"--icmp-type",
             (UINT8) -1,
             &Selector->LocalPort,
             sizeof (UINT16),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= ICMP_TYPE;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  //
  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
  //
  Status = GetNumber (
             L"--icmp-code",
             (UINT8) -1,
             &Selector->RemotePort,
             sizeof (UINT16),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= ICMP_CODE;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  return ReturnStatus;
}

/**
  Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA through ParamPackage list.

  @param[out] Selector        The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
  @param[out] Data            The pointer to the EFI_IPSEC_SPD_DATA structure.
  @param[in]  ParamPackage    The pointer to the ParamPackage list.
  @param[out] Mask            The pointer to the Mask.
  @param[in]  CreateNew       The switch to create new.

  @retval EFI_SUCCESS              Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA successfully.
  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

**/
EFI_STATUS
CreateSpdEntry (
  OUT EFI_IPSEC_SPD_SELECTOR    **Selector,
  OUT EFI_IPSEC_SPD_DATA        **Data,
  IN  LIST_ENTRY                *ParamPackage,
  OUT UINT32                    *Mask,
  IN  BOOLEAN                   CreateNew
  )
{
  EFI_STATUS      Status;
  EFI_STATUS      ReturnStatus;
  CONST CHAR16    *ValueStr;
  UINTN           DataSize;

  Status    = EFI_SUCCESS;
  *Mask     = 0;

  *Selector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR) + 2 * sizeof (EFI_IP_ADDRESS_INFO));
  ASSERT (*Selector != NULL);

  (*Selector)->LocalAddress  = (EFI_IP_ADDRESS_INFO *) (*Selector + 1);
  (*Selector)->RemoteAddress = (*Selector)->LocalAddress + 1;

  ReturnStatus = CreateSpdSelector (*Selector, ParamPackage, Mask);

  //
  // SPD DATA
  // NOTE: Allocate enough memory and add padding for different arch.
  //
  DataSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SPD_DATA));
  DataSize  = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_PROCESS_POLICY));
  DataSize += sizeof (EFI_IPSEC_TUNNEL_OPTION);

  *Data = AllocateZeroPool (DataSize);
  ASSERT (*Data != NULL);

  (*Data)->ProcessingPolicy               = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (
                                                                           (*Data + 1),
                                                                           sizeof (UINTN)
                                                                           );
  (*Data)->ProcessingPolicy->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER (
                                                                          ((*Data)->ProcessingPolicy + 1),
                                                                          sizeof (UINTN)
                                                                          );


  //
  // Convert user imput from string to integer, and fill in the Name in EFI_IPSEC_SPD_DATA.
  //
  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--name");
  if (ValueStr != NULL) {
    UnicodeStrToAsciiStrS (ValueStr, (CHAR8 *) (*Data)->Name, sizeof ((*Data)->Name));
    *Mask |= NAME;
  }

  //
  // Convert user imput from string to integer, and fill in the PackageFlag in EFI_IPSEC_SPD_DATA.
  //
  Status = GetNumber (
             L"--packet-flag",
             (UINT8) -1,
             &(*Data)->PackageFlag,
             sizeof (UINT32),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= PACKET_FLAG;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  //
  // Convert user imput from string to integer, and fill in the Action in EFI_IPSEC_SPD_DATA.
  //
  Status = GetNumber (
             L"--action",
             (UINT8) -1,
             &(*Data)->Action,
             sizeof (UINT32),
             mMapIpSecAction,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= ACTION;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  //
  // Convert user imput from string to integer, and fill in the ExtSeqNum in EFI_IPSEC_SPD_DATA.
  //
  if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence")) {
    (*Data)->ProcessingPolicy->ExtSeqNum   = TRUE;
    *Mask |= EXT_SEQUENCE;
  } else if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence-")) {
    (*Data)->ProcessingPolicy->ExtSeqNum   = FALSE;
    *Mask |= EXT_SEQUENCE;
  }

  //
  // Convert user imput from string to integer, and fill in the SeqOverflow in EFI_IPSEC_SPD_DATA.
  //
  if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow")) {
    (*Data)->ProcessingPolicy->SeqOverflow = TRUE;
    *Mask |= SEQUENCE_OVERFLOW;
  } else if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow-")) {
    (*Data)->ProcessingPolicy->SeqOverflow = FALSE;
    *Mask |= SEQUENCE_OVERFLOW;
  }

  //
  // Convert user imput from string to integer, and fill in the FragCheck in EFI_IPSEC_SPD_DATA.
  //
  if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check")) {
    (*Data)->ProcessingPolicy->FragCheck   = TRUE;
    *Mask |= FRAGMENT_CHECK;
  } else if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check-")) {
    (*Data)->ProcessingPolicy->FragCheck   = FALSE;
    *Mask |= FRAGMENT_CHECK;
  }

  //
  // Convert user imput from string to integer, and fill in the ProcessingPolicy in EFI_IPSEC_SPD_DATA.
  //
  Status = GetNumber (
             L"--lifebyte",
             (UINT64) -1,
             &(*Data)->ProcessingPolicy->SaLifetime.ByteCount,
             sizeof (UINT64),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= LIFEBYTE;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Status = GetNumber (
             L"--lifetime",
             (UINT64) -1,
             &(*Data)->ProcessingPolicy->SaLifetime.HardLifetime,
             sizeof (UINT64),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= LIFETIME;
  }
  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Status = GetNumber (
             L"--lifetime-soft",
             (UINT64) -1,
             &(*Data)->ProcessingPolicy->SaLifetime.SoftLifetime,
             sizeof (UINT64),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= LIFETIME_SOFT;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  (*Data)->ProcessingPolicy->Mode = EfiIPsecTransport;
  Status = GetNumber (
             L"--mode",
             0,
             &(*Data)->ProcessingPolicy->Mode,
             sizeof (UINT32),
             mMapIpSecMode,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= MODE;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-local");
  if (ValueStr != NULL) {
    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->LocalTunnelAddress);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
        mHiiHandle,
        mAppName,
        L"--tunnel-local",
        ValueStr
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      *Mask |= TUNNEL_LOCAL;
    }
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-remote");
  if (ValueStr != NULL) {
    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
        mHiiHandle,
        mAppName,
        L"--tunnel-remote",
        ValueStr
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      *Mask |= TUNNEL_REMOTE;
    }
  }

  (*Data)->ProcessingPolicy->TunnelOption->DF = EfiIPsecTunnelCopyDf;
  Status = GetNumber (
             L"--dont-fragment",
             0,
             &(*Data)->ProcessingPolicy->TunnelOption->DF,
             sizeof (UINT32),
             mMapDfOption,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= DONT_FRAGMENT;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  (*Data)->ProcessingPolicy->Proto = EfiIPsecESP;
  Status = GetNumber (
             L"--ipsec-proto",
             0,
             &(*Data)->ProcessingPolicy->Proto,
             sizeof (UINT32),
             mMapIpSecProtocol,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= IPSEC_PROTO;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Status = GetNumber (
             L"--encrypt-algo",
             0,
             &(*Data)->ProcessingPolicy->EncAlgoId,
             sizeof (UINT8),
             mMapEncAlgo,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= ENCRYPT_ALGO;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Status = GetNumber (
             L"--auth-algo",
             0,
             &(*Data)->ProcessingPolicy->AuthAlgoId,
             sizeof (UINT8),
             mMapAuthAlgo,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= AUTH_ALGO;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  //
  // Cannot check Mode against EfiIPsecTunnel, because user may want to change tunnel_remote only so the Mode is not set.
  //
  if ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE | DONT_FRAGMENT)) == 0) {
    (*Data)->ProcessingPolicy->TunnelOption = NULL;
  }

  if ((*Mask & (EXT_SEQUENCE | SEQUENCE_OVERFLOW | FRAGMENT_CHECK | LIFEBYTE |
                LIFETIME_SOFT | LIFETIME | MODE | TUNNEL_LOCAL | TUNNEL_REMOTE |
                DONT_FRAGMENT | IPSEC_PROTO | AUTH_ALGO | ENCRYPT_ALGO)) == 0) {
    if ((*Data)->Action != EfiIPsecActionProtect) {
      //
      // User may not provide additional parameter for Protect action, so we cannot simply set ProcessingPolicy to NULL.
      //
      (*Data)->ProcessingPolicy = NULL;
    }
  }

  if (CreateNew) {
    if ((*Mask & (LOCAL | REMOTE | PROTO | ACTION)) != (LOCAL | REMOTE | PROTO | ACTION)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
        mHiiHandle,
        mAppName,
        L"--local --remote --proto --action"
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else if (((*Data)->Action == EfiIPsecActionProtect) &&
               ((*Data)->ProcessingPolicy->Mode == EfiIPsecTunnel) &&
               ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE))) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
        mHiiHandle,
        mAppName,
        L"--tunnel-local --tunnel-remote"
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    }
  }

  return ReturnStatus;
}

/**
  Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA2 through ParamPackage list.

  @param[out] SaId            The pointer to the EFI_IPSEC_SA_ID structure.
  @param[out] Data            The pointer to the EFI_IPSEC_SA_DATA2 structure.
  @param[in]  ParamPackage    The pointer to the ParamPackage list.
  @param[out] Mask            The pointer to the Mask.
  @param[in]  CreateNew       The switch to create new.

  @retval EFI_SUCCESS              Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA2 successfully.
  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

**/
EFI_STATUS
CreateSadEntry (
  OUT EFI_IPSEC_SA_ID      **SaId,
  OUT EFI_IPSEC_SA_DATA2   **Data,
  IN  LIST_ENTRY           *ParamPackage,
  OUT UINT32               *Mask,
  IN  BOOLEAN              CreateNew
  )
{
  EFI_STATUS      Status;
  EFI_STATUS      ReturnStatus;
  UINTN           AuthKeyLength;
  UINTN           EncKeyLength;
  CONST CHAR16    *ValueStr;
  CHAR8           *AsciiStr;
  UINTN           DataSize;

  Status        = EFI_SUCCESS;
  ReturnStatus  = EFI_SUCCESS;
  *Mask         = 0;
  AuthKeyLength = 0;
  EncKeyLength  = 0;

  *SaId = AllocateZeroPool (sizeof (EFI_IPSEC_SA_ID));
  ASSERT (*SaId != NULL);

  //
  // Convert user imput from string to integer, and fill in the Spi in EFI_IPSEC_SA_ID.
  //
  Status = GetNumber (L"--spi", (UINT32) -1, &(*SaId)->Spi, sizeof (UINT32), NULL, ParamPackage, FORMAT_NUMBER);
  if (!EFI_ERROR (Status)) {
    *Mask |= SPI;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  //
  // Convert user imput from string to integer, and fill in the Proto in EFI_IPSEC_SA_ID.
  //
  Status = GetNumber (
             L"--ipsec-proto",
             0,
             &(*SaId)->Proto,
             sizeof (EFI_IPSEC_PROTOCOL_TYPE),
             mMapIpSecProtocol,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= IPSEC_PROTO;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  //
  // Convert user imput from string to integer, and fill in EFI_IPSEC_SA_DATA2.
  //
  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");
  if (ValueStr != NULL) {
    AuthKeyLength = StrLen (ValueStr);
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");
  if (ValueStr != NULL) {
    EncKeyLength = StrLen (ValueStr);
  }

  //
  // EFI_IPSEC_SA_DATA2:
  //   +------------
  //   | EFI_IPSEC_SA_DATA2
  //   +-----------------------
  //   | AuthKey
  //   +-------------------------
  //   | EncKey
  //   +-------------------------
  //   | SpdSelector
  //
  // Notes: To make sure the address alignment add padding after each data if needed.
  //
  DataSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA2));
  DataSize  = ALIGN_VARIABLE (DataSize + AuthKeyLength);
  DataSize  = ALIGN_VARIABLE (DataSize + EncKeyLength);
  DataSize  = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_SPD_SELECTOR));
  DataSize  = ALIGN_VARIABLE (DataSize + sizeof (EFI_IP_ADDRESS_INFO));
  DataSize += sizeof (EFI_IP_ADDRESS_INFO);



  *Data = AllocateZeroPool (DataSize);
  ASSERT (*Data != NULL);

  (*Data)->ManualSet                    = TRUE;
  (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER (((*Data) + 1), sizeof (UINTN));
  (*Data)->AlgoInfo.EspAlgoInfo.EncKey  = (VOID *) ALIGN_POINTER (
                                                     ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.AuthKey + AuthKeyLength),
                                                     sizeof (UINTN)
                                                     );
  (*Data)->SpdSelector                  = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER (
                                                                       ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.EncKey + EncKeyLength),
                                                                       sizeof (UINTN)
                                                                       );
  (*Data)->SpdSelector->LocalAddress    = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (
                                                                    ((UINT8 *) (*Data)->SpdSelector + sizeof (EFI_IPSEC_SPD_SELECTOR)),
                                                                    sizeof (UINTN));
  (*Data)->SpdSelector->RemoteAddress   = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (
                                                                    (*Data)->SpdSelector->LocalAddress + 1,
                                                                    sizeof (UINTN)
                                                                    );

  (*Data)->Mode = EfiIPsecTransport;
  Status = GetNumber (
             L"--mode",
             0,
             &(*Data)->Mode,
             sizeof (EFI_IPSEC_MODE),
             mMapIpSecMode,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= MODE;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  //
  // According to RFC 4303-3.3.3. The first packet sent using a given SA
  // will contain a sequence number of 1.
  //
  (*Data)->SNCount = 1;
  Status = GetNumber (
             L"--sequence-number",
             (UINT64) -1,
             &(*Data)->SNCount,
             sizeof (UINT64),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= SEQUENCE_NUMBER;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  (*Data)->AntiReplayWindows = 0;
  Status = GetNumber (
             L"--antireplay-window",
             (UINT8) -1,
             &(*Data)->AntiReplayWindows,
             sizeof (UINT8),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= SEQUENCE_NUMBER;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Status = GetNumber (
             L"--encrypt-algo",
             0,
             &(*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId,
             sizeof (UINT8),
             mMapEncAlgo,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= ENCRYPT_ALGO;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");
  if (ValueStr != NULL ) {
    (*Data)->AlgoInfo.EspAlgoInfo.EncKeyLength = EncKeyLength;
    AsciiStr = AllocateZeroPool (EncKeyLength + 1);
    ASSERT (AsciiStr != NULL);
    UnicodeStrToAsciiStrS (ValueStr, AsciiStr, EncKeyLength + 1);
    CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.EncKey,  AsciiStr, EncKeyLength);
    FreePool (AsciiStr);
    *Mask |= ENCRYPT_KEY;
  } else {
    (*Data)->AlgoInfo.EspAlgoInfo.EncKey = NULL;
  }

  Status = GetNumber (
             L"--auth-algo",
             0,
             &(*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId,
             sizeof (UINT8),
             mMapAuthAlgo,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= AUTH_ALGO;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");
  if (ValueStr != NULL) {
    (*Data)->AlgoInfo.EspAlgoInfo.AuthKeyLength = AuthKeyLength;
    AsciiStr = AllocateZeroPool (AuthKeyLength + 1);
    ASSERT (AsciiStr != NULL);
    UnicodeStrToAsciiStrS (ValueStr, AsciiStr, AuthKeyLength + 1);
    CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.AuthKey, AsciiStr, AuthKeyLength);
    FreePool (AsciiStr);
    *Mask |= AUTH_KEY;
  } else {
    (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = NULL;
  }

  Status = GetNumber (
             L"--lifebyte",
             (UINT64) -1,
             &(*Data)->SaLifetime.ByteCount,
             sizeof (UINT64),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= LIFEBYTE;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Status = GetNumber (
             L"--lifetime",
             (UINT64) -1,
             &(*Data)->SaLifetime.HardLifetime,
             sizeof (UINT64),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= LIFETIME;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Status = GetNumber (
             L"--lifetime-soft",
             (UINT64) -1,
             &(*Data)->SaLifetime.SoftLifetime,
             sizeof (UINT64),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= LIFETIME_SOFT;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Status = GetNumber (
             L"--path-mtu",
             (UINT32) -1,
             &(*Data)->PathMTU,
             sizeof (UINT32),
             NULL,
             ParamPackage,
             FORMAT_NUMBER
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= PATH_MTU;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  //
  // Convert user imput from string to integer, and fill in the DestAddress in EFI_IPSEC_SA_ID.
  //
  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-dest");
  if (ValueStr != NULL) {
    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->TunnelDestinationAddress);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
        mHiiHandle,
        mAppName,
        L"--tunnel-dest",
        ValueStr
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      *Mask |= DEST;
    }
  }

  //
  // Convert user input from string to integer, and fill in the DestAddress in EFI_IPSEC_SA_ID.
  //
  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-source");
  if (ValueStr != NULL) {
    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->TunnelSourceAddress);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
        mHiiHandle,
        mAppName,
        L"--tunnel-source",
        ValueStr
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      *Mask |= SOURCE;
    }
  }

  //
  // If it is TunnelMode, then check if the tunnel-source and --tunnel-dest are set
  //
  if ((*Data)->Mode == EfiIPsecTunnel) {
    if ((*Mask & (DEST|SOURCE)) != (DEST|SOURCE)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
        mHiiHandle,
        mAppName,
        L"--tunnel-source --tunnel-dest"
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    }
  }
  ReturnStatus = CreateSpdSelector ((*Data)->SpdSelector, ParamPackage, Mask);

  if (CreateNew) {
    if ((*Mask & (SPI|IPSEC_PROTO|LOCAL|REMOTE)) != (SPI|IPSEC_PROTO|LOCAL|REMOTE)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
        mHiiHandle,
        mAppName,
        L"--spi --ipsec-proto --local --remote"
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      if ((*SaId)->Proto == EfiIPsecAH) {
        if ((*Mask & AUTH_ALGO) == 0) {
          ShellPrintHiiEx (
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
            mHiiHandle,
            mAppName,
            L"--auth-algo"
            );
          ReturnStatus = EFI_INVALID_PARAMETER;
        } else if ((*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId != IPSEC_AALG_NONE && (*Mask & AUTH_KEY) == 0) {
          ShellPrintHiiEx (
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
            mHiiHandle,
            mAppName,
            L"--auth-key"
            );
          ReturnStatus = EFI_INVALID_PARAMETER;
        }
      } else {
        if ((*Mask & (ENCRYPT_ALGO|AUTH_ALGO)) != (ENCRYPT_ALGO|AUTH_ALGO) ) {
          ShellPrintHiiEx (
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
            mHiiHandle,
            mAppName,
            L"--encrypt-algo --auth-algo"
            );
          ReturnStatus = EFI_INVALID_PARAMETER;
        } else if ((*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId != IPSEC_EALG_NONE && (*Mask & ENCRYPT_KEY) == 0) {
          ShellPrintHiiEx (
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
            mHiiHandle,
            mAppName,
            L"--encrypt-key"
            );
          ReturnStatus = EFI_INVALID_PARAMETER;
        } else if ((*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId != IPSEC_AALG_NONE && (*Mask & AUTH_KEY) == 0) {
          ShellPrintHiiEx (
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
            mHiiHandle,
            mAppName,
            L"--auth-key"
            );
          ReturnStatus = EFI_INVALID_PARAMETER;
        }
      }
    }
  }

  return ReturnStatus;
}

/**
  Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA through ParamPackage list.

  @param[out] PadId           The pointer to the EFI_IPSEC_PAD_ID structure.
  @param[out] Data            The pointer to the EFI_IPSEC_PAD_DATA structure.
  @param[in]  ParamPackage    The pointer to the ParamPackage list.
  @param[out] Mask            The pointer to the Mask.
  @param[in]  CreateNew       The switch to create new.

  @retval EFI_SUCCESS              Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA successfully.
  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

**/
EFI_STATUS
CreatePadEntry (
  OUT EFI_IPSEC_PAD_ID      **PadId,
  OUT EFI_IPSEC_PAD_DATA    **Data,
  IN  LIST_ENTRY            *ParamPackage,
  OUT UINT32                *Mask,
  IN  BOOLEAN               CreateNew
  )
{
  EFI_STATUS         Status;
  EFI_STATUS         ReturnStatus;
  SHELL_FILE_HANDLE  FileHandle;
  UINT64             FileSize;
  UINTN              AuthDataLength;
  UINTN              RevocationDataLength;
  UINTN              DataLength;
  UINTN              Index;
  CONST CHAR16       *ValueStr;
  UINTN              DataSize;

  Status               = EFI_SUCCESS;
  ReturnStatus         = EFI_SUCCESS;
  *Mask                = 0;
  AuthDataLength       = 0;
  RevocationDataLength = 0;

  *PadId = AllocateZeroPool (sizeof (EFI_IPSEC_PAD_ID));
  ASSERT (*PadId != NULL);

  //
  // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_ID.
  //
  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-address");
  if (ValueStr != NULL) {
    (*PadId)->PeerIdValid = FALSE;
    Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &(*PadId)->Id.IpAddress);
    if (EFI_ERROR (Status)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
        mHiiHandle,
        mAppName,
        L"--peer-address",
        ValueStr
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else {
      *Mask |= PEER_ADDRESS;
    }
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-id");
  if (ValueStr != NULL) {
    (*PadId)->PeerIdValid = TRUE;
    StrnCpyS ((CHAR16 *) (*PadId)->Id.PeerId, MAX_PEERID_LEN / sizeof (CHAR16), ValueStr, MAX_PEERID_LEN / sizeof (CHAR16) - 1);
    *Mask |= PEER_ID;
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");
  if (ValueStr != NULL) {
    if (ValueStr[0] == L'@') {
      //
      // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"
      //
      Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);
      if (EFI_ERROR (Status)) {
        ShellPrintHiiEx (
          -1,
          -1,
          NULL,
          STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),
          mHiiHandle,
          mAppName,
          &ValueStr[1]
          );
        ReturnStatus = EFI_INVALID_PARAMETER;
      } else {
        Status = ShellGetFileSize (FileHandle, &FileSize);
        ShellCloseFile (&FileHandle);
        if (EFI_ERROR (Status)) {
          ShellPrintHiiEx (
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),
            mHiiHandle,
            mAppName,
            &ValueStr[1]
            );
          ReturnStatus = EFI_INVALID_PARAMETER;
        } else {
          AuthDataLength = (UINTN) FileSize;
        }
      }
    } else {
      AuthDataLength = StrLen (ValueStr);
    }
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");
  if (ValueStr != NULL) {
    RevocationDataLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);
  }

  //
  // Allocate Buffer for Data. Add padding after each struct to make sure the alignment
  // in different Arch.
  //
  DataSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));
  DataSize  = ALIGN_VARIABLE (DataSize + AuthDataLength);
  DataSize += RevocationDataLength;

  *Data = AllocateZeroPool (DataSize);
  ASSERT (*Data != NULL);

  (*Data)->AuthData       = (VOID *) ALIGN_POINTER ((*Data + 1), sizeof (UINTN));
  (*Data)->RevocationData = (VOID *) ALIGN_POINTER (((UINT8 *) (*Data + 1) + AuthDataLength), sizeof (UINTN));
  (*Data)->AuthProtocol   = EfiIPsecAuthProtocolIKEv1;

  //
  // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_DATA.
  //
  Status = GetNumber (
             L"--auth-proto",
             0,
             &(*Data)->AuthProtocol,
             sizeof (EFI_IPSEC_AUTH_PROTOCOL_TYPE),
             mMapAuthProto,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= AUTH_PROTO;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  Status = GetNumber (
             L"--auth-method",
             0,
             &(*Data)->AuthMethod,
             sizeof (EFI_IPSEC_AUTH_METHOD),
             mMapAuthMethod,
             ParamPackage,
             FORMAT_STRING
             );
  if (!EFI_ERROR (Status)) {
    *Mask |= AUTH_METHOD;
  }

  if (Status == EFI_INVALID_PARAMETER) {
    ReturnStatus = EFI_INVALID_PARAMETER;
  }

  if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id")) {
    (*Data)->IkeIdFlag = TRUE;
    *Mask |= IKE_ID;
  }

  if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id-")) {
    (*Data)->IkeIdFlag = FALSE;
    *Mask |= IKE_ID;
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");
  if (ValueStr != NULL) {
    if (ValueStr[0] == L'@') {
      //
      // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"
      //

      Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);
      if (EFI_ERROR (Status)) {
        ShellPrintHiiEx (
          -1,
          -1,
          NULL,
          STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),
          mHiiHandle,
          mAppName,
          &ValueStr[1]
          );
        ReturnStatus = EFI_INVALID_PARAMETER;
        (*Data)->AuthData = NULL;
      } else {
        DataLength = AuthDataLength;
        Status     = ShellReadFile (FileHandle, &DataLength, (*Data)->AuthData);
        ShellCloseFile (&FileHandle);
        if (EFI_ERROR (Status)) {
          ShellPrintHiiEx (
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),
            mHiiHandle,
            mAppName,
            &ValueStr[1]
            );
          ReturnStatus = EFI_INVALID_PARAMETER;
          (*Data)->AuthData = NULL;
        } else {
          ASSERT (DataLength == AuthDataLength);
          *Mask |= AUTH_DATA;
        }
      }
    } else {
      for (Index = 0; Index < AuthDataLength; Index++) {
        ((CHAR8 *) (*Data)->AuthData)[Index] = (CHAR8) ValueStr[Index];
      }
      (*Data)->AuthDataSize = AuthDataLength;
      *Mask |= AUTH_DATA;
    }
  }

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");
  if (ValueStr != NULL) {
    CopyMem ((*Data)->RevocationData, ValueStr, RevocationDataLength);
    (*Data)->RevocationDataSize = RevocationDataLength;
    *Mask |= REVOCATION_DATA;
  } else {
    (*Data)->RevocationData = NULL;
  }

  if (CreateNew) {
    if ((*Mask & (PEER_ID | PEER_ADDRESS)) == 0) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
        mHiiHandle,
        mAppName,
        L"--peer-id --peer-address"
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    } else if ((*Mask & (AUTH_METHOD | AUTH_DATA)) != (AUTH_METHOD | AUTH_DATA)) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
        mHiiHandle,
        mAppName,
        L"--auth-method --auth-data"
        );
      ReturnStatus = EFI_INVALID_PARAMETER;
    }
  }

  return ReturnStatus;
}

CREATE_POLICY_ENTRY mCreatePolicyEntry[] = {
  (CREATE_POLICY_ENTRY) CreateSpdEntry,
  (CREATE_POLICY_ENTRY) CreateSadEntry,
  (CREATE_POLICY_ENTRY) CreatePadEntry
};

/**
  Combine old SPD entry with new SPD entry.

  @param[in, out] OldSelector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
  @param[in, out] OldData        The pointer to the EFI_IPSEC_SPD_DATA structure.
  @param[in]      NewSelector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
  @param[in]      NewData        The pointer to the EFI_IPSEC_SPD_DATA structure.
  @param[in]      Mask           The pointer to the Mask.
  @param[out]     CreateNew      The switch to create new.

  @retval EFI_SUCCESS              Combined successfully.
  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

**/
EFI_STATUS
CombineSpdEntry (
  IN OUT EFI_IPSEC_SPD_SELECTOR    *OldSelector,
  IN OUT EFI_IPSEC_SPD_DATA        *OldData,
  IN     EFI_IPSEC_SPD_SELECTOR    *NewSelector,
  IN     EFI_IPSEC_SPD_DATA        *NewData,
  IN     UINT32                    Mask,
     OUT BOOLEAN                   *CreateNew
  )
{

  //
  // Process Selector
  //
  *CreateNew = FALSE;
  if ((Mask & LOCAL) == 0) {
    NewSelector->LocalAddressCount = OldSelector->LocalAddressCount;
    NewSelector->LocalAddress      = OldSelector->LocalAddress;
  } else if ((NewSelector->LocalAddressCount != OldSelector->LocalAddressCount) ||
             (CompareMem (NewSelector->LocalAddress, OldSelector->LocalAddress, NewSelector->LocalAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {
    *CreateNew = TRUE;
  }

  if ((Mask & REMOTE) == 0) {
    NewSelector->RemoteAddressCount = OldSelector->RemoteAddressCount;
    NewSelector->RemoteAddress      = OldSelector->RemoteAddress;
  } else if ((NewSelector->RemoteAddressCount != OldSelector->RemoteAddressCount) ||
             (CompareMem (NewSelector->RemoteAddress, OldSelector->RemoteAddress, NewSelector->RemoteAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {
    *CreateNew = TRUE;
  }

  if ((Mask & PROTO) == 0) {
    NewSelector->NextLayerProtocol = OldSelector->NextLayerProtocol;
  } else if (NewSelector->NextLayerProtocol != OldSelector->NextLayerProtocol) {
    *CreateNew = TRUE;
  }

  switch (NewSelector->NextLayerProtocol) {
    case EFI_IP4_PROTO_TCP:
    case EFI_IP4_PROTO_UDP:
      if ((Mask & LOCAL_PORT) == 0) {
        NewSelector->LocalPort      = OldSelector->LocalPort;
        NewSelector->LocalPortRange = OldSelector->LocalPortRange;
      } else if ((NewSelector->LocalPort != OldSelector->LocalPort) ||
        (NewSelector->LocalPortRange != OldSelector->LocalPortRange)) {
        *CreateNew = TRUE;
      }

      if ((Mask & REMOTE_PORT) == 0) {
        NewSelector->RemotePort      = OldSelector->RemotePort;
        NewSelector->RemotePortRange = OldSelector->RemotePortRange;
      } else if ((NewSelector->RemotePort != OldSelector->RemotePort) ||
        (NewSelector->RemotePortRange != OldSelector->RemotePortRange)) {
        *CreateNew = TRUE;
      }
      break;

    case EFI_IP4_PROTO_ICMP:
      if ((Mask & ICMP_TYPE) == 0) {
        NewSelector->LocalPort = OldSelector->LocalPort;
      } else if (NewSelector->LocalPort != OldSelector->LocalPort) {
        *CreateNew = TRUE;
      }

      if ((Mask & ICMP_CODE) == 0) {
        NewSelector->RemotePort = OldSelector->RemotePort;
      } else if (NewSelector->RemotePort != OldSelector->RemotePort) {
        *CreateNew = TRUE;
      }
      break;
  }
  //
  // Process Data
  //
  OldData->SaIdCount = 0;

  if ((Mask & NAME) != 0) {
    AsciiStrCpyS ((CHAR8 *) OldData->Name, MAX_PEERID_LEN, (CHAR8 *) NewData->Name);
  }

  if ((Mask & PACKET_FLAG) != 0) {
    OldData->PackageFlag = NewData->PackageFlag;
  }

  if ((Mask & ACTION) != 0) {
    OldData->Action = NewData->Action;
  }

  if (OldData->Action != EfiIPsecActionProtect) {
    OldData->ProcessingPolicy = NULL;
  } else {
    //
    // Protect
    //
    if (OldData->ProcessingPolicy == NULL) {
      //
      // Just point to new data if originally NULL.
      //
      OldData->ProcessingPolicy = NewData->ProcessingPolicy;
      if (OldData->ProcessingPolicy->Mode == EfiIPsecTunnel &&
          (Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)
        ) {
        //
        // Change to Protect action and Tunnel mode, but without providing local/remote tunnel address.
        //
        ShellPrintHiiEx (
          -1,
          -1,
          NULL,
          STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
          mHiiHandle,
          mAppName,
          L"--tunnel-local --tunnel-remote"
          );
        return EFI_INVALID_PARAMETER;
      }
    } else {
      //
      // Modify some of the data.
      //
      if ((Mask & EXT_SEQUENCE) != 0) {
        OldData->ProcessingPolicy->ExtSeqNum = NewData->ProcessingPolicy->ExtSeqNum;
      }

      if ((Mask & SEQUENCE_OVERFLOW) != 0) {
        OldData->ProcessingPolicy->SeqOverflow = NewData->ProcessingPolicy->SeqOverflow;
      }

      if ((Mask & FRAGMENT_CHECK) != 0) {
        OldData->ProcessingPolicy->FragCheck = NewData->ProcessingPolicy->FragCheck;
      }

      if ((Mask & LIFEBYTE) != 0) {
        OldData->ProcessingPolicy->SaLifetime.ByteCount = NewData->ProcessingPolicy->SaLifetime.ByteCount;
      }

      if ((Mask & LIFETIME_SOFT) != 0) {
        OldData->ProcessingPolicy->SaLifetime.SoftLifetime = NewData->ProcessingPolicy->SaLifetime.SoftLifetime;
      }

      if ((Mask & LIFETIME) != 0) {
        OldData->ProcessingPolicy->SaLifetime.HardLifetime = NewData->ProcessingPolicy->SaLifetime.HardLifetime;
      }

      if ((Mask & MODE) != 0) {
        OldData->ProcessingPolicy->Mode = NewData->ProcessingPolicy->Mode;
      }

      if ((Mask & IPSEC_PROTO) != 0) {
        OldData->ProcessingPolicy->Proto = NewData->ProcessingPolicy->Proto;
      }

      if ((Mask & AUTH_ALGO) != 0) {
        OldData->ProcessingPolicy->AuthAlgoId = NewData->ProcessingPolicy->AuthAlgoId;
      }

      if ((Mask & ENCRYPT_ALGO) != 0) {
        OldData->ProcessingPolicy->EncAlgoId = NewData->ProcessingPolicy->EncAlgoId;
      }

      if (OldData->ProcessingPolicy->Mode != EfiIPsecTunnel) {
        OldData->ProcessingPolicy->TunnelOption = NULL;
      } else {
        if (OldData->ProcessingPolicy->TunnelOption == NULL) {
          //
          // Set from Transport mode to Tunnel mode, should ensure TUNNEL_LOCAL & TUNNEL_REMOTE both exists.
          //
          if ((Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)) {
            ShellPrintHiiEx (
              -1,
              -1,
              NULL,
              STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
              mHiiHandle,
              mAppName,
              L"--tunnel-local --tunnel-remote"
              );
            return EFI_INVALID_PARAMETER;
          }

          OldData->ProcessingPolicy->TunnelOption = NewData->ProcessingPolicy->TunnelOption;
        } else {
          if ((Mask & TUNNEL_LOCAL) != 0) {
            CopyMem (
              &OldData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,
              &NewData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,
              sizeof (EFI_IP_ADDRESS)
              );
          }

          if ((Mask & TUNNEL_REMOTE) != 0) {
            CopyMem (
              &OldData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,
              &NewData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,
              sizeof (EFI_IP_ADDRESS)
              );
          }

          if ((Mask & DONT_FRAGMENT) != 0) {
            OldData->ProcessingPolicy->TunnelOption->DF = NewData->ProcessingPolicy->TunnelOption->DF;
          }
        }
      }
    }
  }

  return EFI_SUCCESS;
}

/**
  Combine old SAD entry with new SAD entry.

  @param[in, out] OldSaId      The pointer to the EFI_IPSEC_SA_ID structure.
  @param[in, out] OldData      The pointer to the EFI_IPSEC_SA_DATA2 structure.
  @param[in]      NewSaId      The pointer to the EFI_IPSEC_SA_ID structure.
  @param[in]      NewData      The pointer to the EFI_IPSEC_SA_DATA2 structure.
  @param[in]      Mask         The pointer to the Mask.
  @param[out]     CreateNew    The switch to create new.

  @retval EFI_SUCCESS              Combined successfully.
  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

**/
EFI_STATUS
CombineSadEntry (
  IN OUT EFI_IPSEC_SA_ID      *OldSaId,
  IN OUT EFI_IPSEC_SA_DATA2   *OldData,
  IN     EFI_IPSEC_SA_ID      *NewSaId,
  IN     EFI_IPSEC_SA_DATA2   *NewData,
  IN     UINT32               Mask,
     OUT BOOLEAN              *CreateNew
  )
{

  *CreateNew = FALSE;

  if ((Mask & SPI) == 0) {
    NewSaId->Spi = OldSaId->Spi;
  } else if (NewSaId->Spi != OldSaId->Spi) {
    *CreateNew = TRUE;
  }

  if ((Mask & IPSEC_PROTO) == 0) {
    NewSaId->Proto = OldSaId->Proto;
  } else if (NewSaId->Proto != OldSaId->Proto) {
    *CreateNew = TRUE;
  }

  if ((Mask & DEST) == 0) {
    CopyMem (&NewData->TunnelDestinationAddress, &OldData->TunnelDestinationAddress, sizeof (EFI_IP_ADDRESS));
  } else if (CompareMem (&NewData->TunnelDestinationAddress, &OldData->TunnelDestinationAddress, sizeof (EFI_IP_ADDRESS)) != 0) {
    *CreateNew = TRUE;
  }

  if ((Mask & SOURCE) == 0) {
    CopyMem (&NewData->TunnelSourceAddress, &OldData->TunnelSourceAddress, sizeof (EFI_IP_ADDRESS));
  } else if (CompareMem (&NewData->TunnelSourceAddress, &OldData->TunnelSourceAddress, sizeof (EFI_IP_ADDRESS)) != 0) {
    *CreateNew = TRUE;
  }
  //
  // Process SA_DATA.
  //
  if ((Mask & MODE) != 0) {
    OldData->Mode = NewData->Mode;
  }

  if ((Mask & SEQUENCE_NUMBER) != 0) {
    OldData->SNCount = NewData->SNCount;
  }

  if ((Mask & ANTIREPLAY_WINDOW) != 0) {
    OldData->AntiReplayWindows = NewData->AntiReplayWindows;
  }

  if ((Mask & AUTH_ALGO) != 0) {
    OldData->AlgoInfo.EspAlgoInfo.AuthAlgoId    = NewData->AlgoInfo.EspAlgoInfo.AuthAlgoId;
  }

  if ((Mask & AUTH_KEY) != 0) {
    OldData->AlgoInfo.EspAlgoInfo.AuthKey       = NewData->AlgoInfo.EspAlgoInfo.AuthKey;
    OldData->AlgoInfo.EspAlgoInfo.AuthKeyLength = NewData->AlgoInfo.EspAlgoInfo.AuthKeyLength;
  }

  if ((Mask & ENCRYPT_ALGO) != 0) {
    OldData->AlgoInfo.EspAlgoInfo.EncAlgoId     = NewData->AlgoInfo.EspAlgoInfo.EncAlgoId;
  }

  if ((Mask & ENCRYPT_KEY) != 0) {
    OldData->AlgoInfo.EspAlgoInfo.EncKey        = NewData->AlgoInfo.EspAlgoInfo.EncKey;
    OldData->AlgoInfo.EspAlgoInfo.EncKeyLength  = NewData->AlgoInfo.EspAlgoInfo.EncKeyLength;
  }

  if (NewSaId->Proto == EfiIPsecAH) {
    if ((Mask & (ENCRYPT_ALGO | ENCRYPT_KEY)) != 0) {
      //
      // Should not provide encrypt_* if AH.
      //
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_UNWANTED_PARAMETER),
        mHiiHandle,
        mAppName,
        L"--encrypt-algo --encrypt-key"
        );
      return EFI_INVALID_PARAMETER;
    }
  }

  if (NewSaId->Proto == EfiIPsecESP && OldSaId->Proto == EfiIPsecAH) {
    //
    // AH -> ESP
    // Should provide encrypt_algo at least.
    //
    if ((Mask & ENCRYPT_ALGO) == 0) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
        mHiiHandle,
        mAppName,
        L"--encrypt-algo"
        );
      return EFI_INVALID_PARAMETER;
    }

    //
    // Encrypt_key should be provided if algorithm is not NONE.
    //
    if (NewData->AlgoInfo.EspAlgoInfo.EncAlgoId != IPSEC_EALG_NONE && (Mask & ENCRYPT_KEY) == 0) {
      ShellPrintHiiEx (
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
        mHiiHandle,
        mAppName,
        L"--encrypt-algo"
        );
      return EFI_INVALID_PARAMETER;
    }
  }

  if ((Mask & LIFEBYTE) != 0) {
    OldData->SaLifetime.ByteCount    = NewData->SaLifetime.ByteCount;
  }

  if ((Mask & LIFETIME_SOFT) != 0) {
    OldData->SaLifetime.SoftLifetime = NewData->SaLifetime.SoftLifetime;
  }

  if ((Mask & LIFETIME) != 0) {
    OldData->SaLifetime.HardLifetime = NewData->SaLifetime.HardLifetime;
  }

  if ((Mask & PATH_MTU) != 0) {
    OldData->PathMTU                 = NewData->PathMTU;
  }
  //
  // Process SpdSelector.
  //
  if (OldData->SpdSelector == NULL) {
    if ((Mask & (LOCAL | REMOTE | PROTO | LOCAL_PORT | REMOTE_PORT | ICMP_TYPE | ICMP_CODE)) != 0) {
      if ((Mask & (LOCAL | REMOTE | PROTO)) != (LOCAL | REMOTE | PROTO)) {
        ShellPrintHiiEx (
          -1,
          -1,
          NULL,
          STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
          mHiiHandle,
          mAppName,
          L"--local --remote --proto"
          );
        return EFI_INVALID_PARAMETER;
      }

      OldData->SpdSelector = NewData->SpdSelector;
    }
  } else {
    if ((Mask & LOCAL) != 0) {
      OldData->SpdSelector->LocalAddressCount  = NewData->SpdSelector->LocalAddressCount;
      OldData->SpdSelector->LocalAddress       = NewData->SpdSelector->LocalAddress;
    }

    if ((Mask & REMOTE) != 0) {
      OldData->SpdSelector->RemoteAddressCount = NewData->SpdSelector->RemoteAddressCount;
      OldData->SpdSelector->RemoteAddress      = NewData->SpdSelector->RemoteAddress;
    }

    if ((Mask & PROTO) != 0) {
      OldData->SpdSelector->NextLayerProtocol  = NewData->SpdSelector->NextLayerProtocol;
    }

    if (OldData->SpdSelector != NULL) {
      switch (OldData->SpdSelector->NextLayerProtocol) {
        case EFI_IP4_PROTO_TCP:
        case EFI_IP4_PROTO_UDP:
          if ((Mask & LOCAL_PORT) != 0) {
            OldData->SpdSelector->LocalPort  = NewData->SpdSelector->LocalPort;
          }

          if ((Mask & REMOTE_PORT) != 0) {
            OldData->SpdSelector->RemotePort = NewData->SpdSelector->RemotePort;
          }
          break;

        case EFI_IP4_PROTO_ICMP:
          if ((Mask & ICMP_TYPE) != 0) {
            OldData->SpdSelector->LocalPort  = (UINT8) NewData->SpdSelector->LocalPort;
          }

          if ((Mask & ICMP_CODE) != 0) {
            OldData->SpdSelector->RemotePort = (UINT8) NewData->SpdSelector->RemotePort;
          }
          break;
      }
    }
  }

  return EFI_SUCCESS;
}

/**
  Combine old PAD entry with new PAD entry.

  @param[in, out] OldPadId     The pointer to the EFI_IPSEC_PAD_ID structure.
  @param[in, out] OldData      The pointer to the EFI_IPSEC_PAD_DATA structure.
  @param[in]      NewPadId     The pointer to the EFI_IPSEC_PAD_ID structure.
  @param[in]      NewData      The pointer to the EFI_IPSEC_PAD_DATA structure.
  @param[in]      Mask         The pointer to the Mask.
  @param[out]     CreateNew    The switch to create new.

  @retval EFI_SUCCESS              Combined successfully.
  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

**/
EFI_STATUS
CombinePadEntry (
  IN OUT EFI_IPSEC_PAD_ID      *OldPadId,
  IN OUT EFI_IPSEC_PAD_DATA    *OldData,
  IN     EFI_IPSEC_PAD_ID      *NewPadId,
  IN     EFI_IPSEC_PAD_DATA    *NewData,
  IN     UINT32                Mask,
     OUT BOOLEAN               *CreateNew
  )
{

  *CreateNew = FALSE;

  if ((Mask & (PEER_ID | PEER_ADDRESS)) == 0) {
    CopyMem (NewPadId, OldPadId, sizeof (EFI_IPSEC_PAD_ID));
  } else {
    if ((Mask & PEER_ID) != 0) {
      if (OldPadId->PeerIdValid) {
        if (StrCmp ((CONST CHAR16 *) OldPadId->Id.PeerId, (CONST CHAR16 *) NewPadId->Id.PeerId) != 0) {
          *CreateNew = TRUE;
        }
      } else {
        *CreateNew = TRUE;
      }
    } else {
      //
      // MASK & PEER_ADDRESS
      //
      if (OldPadId->PeerIdValid) {
        *CreateNew = TRUE;
      } else {
        if ((CompareMem (&OldPadId->Id.IpAddress.Address, &NewPadId->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0) ||
            (OldPadId->Id.IpAddress.PrefixLength != NewPadId->Id.IpAddress.PrefixLength)) {
          *CreateNew = TRUE;
        }
      }
    }
  }

  if ((Mask & AUTH_PROTO) != 0) {
    OldData->AuthProtocol = NewData->AuthProtocol;
  }

  if ((Mask & AUTH_METHOD) != 0) {
    OldData->AuthMethod = NewData->AuthMethod;
  }

  if ((Mask & IKE_ID) != 0) {
    OldData->IkeIdFlag = NewData->IkeIdFlag;
  }

  if ((Mask & AUTH_DATA) != 0) {
    OldData->AuthDataSize = NewData->AuthDataSize;
    OldData->AuthData     = NewData->AuthData;
  }

  if ((Mask & REVOCATION_DATA) != 0) {
    OldData->RevocationDataSize = NewData->RevocationDataSize;
    OldData->RevocationData     = NewData->RevocationData;
  }

  return EFI_SUCCESS;
}

COMBINE_POLICY_ENTRY mCombinePolicyEntry[] = {
  (COMBINE_POLICY_ENTRY) CombineSpdEntry,
  (COMBINE_POLICY_ENTRY) CombineSadEntry,
  (COMBINE_POLICY_ENTRY) CombinePadEntry
};

/**
  Edit entry information in the database.

  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.
  @param[in] Data        The pointer to the data.
  @param[in] Context     The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.

  @retval EFI_SUCCESS    Continue the iteration.
  @retval EFI_ABORTED    Abort the iteration.
**/
EFI_STATUS
EditOperatePolicyEntry (
  IN EFI_IPSEC_CONFIG_SELECTOR    *Selector,
  IN VOID                         *Data,
  IN EDIT_POLICY_ENTRY_CONTEXT    *Context
  )
{
  EFI_STATUS    Status;
  BOOLEAN       CreateNew;

  if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {
    ASSERT (Context->DataType < 3);

    Status = mCombinePolicyEntry[Context->DataType] (
               Selector,
               Data,
               Context->Selector,
               Context->Data,
               Context->Mask,
               &CreateNew
               );
    if (!EFI_ERROR (Status)) {
      //
      // If the Selector already existed, this Entry will be updated by set data.
      //
      Status = mIpSecConfig->SetData (
                               mIpSecConfig,
                               Context->DataType,
                               Context->Selector, /// New created selector.
                               Data, /// Old date which has been modified, need to be set data.
                               Selector
                               );
      ASSERT_EFI_ERROR (Status);
      
      if (CreateNew) {
        //
        // Edit the entry to a new one. So, we need delete the old entry.
        //
        Status = mIpSecConfig->SetData (
                                 mIpSecConfig,
                                 Context->DataType,
                                 Selector, /// Old selector.
                                 NULL, /// NULL means to delete this Entry specified by Selector.
                                 NULL
                                 );
        ASSERT_EFI_ERROR (Status);
      }
    }

    Context->Status = Status;
    return EFI_ABORTED;
  }

  return EFI_SUCCESS;
}

/**
  Edit entry information in database according to datatype.

  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.
  @param[in] ParamPackage    The pointer to the ParamPackage list.

  @retval EFI_SUCCESS             Edit entry information successfully.
  @retval EFI_NOT_FOUND           Can't find the specified entry.
  @retval Others                  Some mistaken case.
**/
EFI_STATUS
EditPolicyEntry (
  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,
  IN LIST_ENTRY                    *ParamPackage
  )
{
  EFI_STATUS                   Status;
  EDIT_POLICY_ENTRY_CONTEXT    Context;
  CONST CHAR16                 *ValueStr;

  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");
  if (ValueStr == NULL) {
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);
    return EFI_NOT_FOUND;
  }

  Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);
  if (!EFI_ERROR (Status)) {
    Context.DataType = DataType;
    Context.Status   = EFI_NOT_FOUND;
    Status = mCreatePolicyEntry[DataType] (&Context.Selector, &Context.Data, ParamPackage, &Context.Mask, FALSE);
    if (!EFI_ERROR (Status)) {
      ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) EditOperatePolicyEntry, &Context);
      Status = Context.Status;
    }

    if (Context.Selector != NULL) {
      gBS->FreePool (Context.Selector);
    }

    if (Context.Data != NULL) {
      gBS->FreePool (Context.Data);
    }
  }

  if (Status == EFI_NOT_FOUND) {
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);
  } else if (EFI_ERROR (Status)) {
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_EDIT_FAILED), mHiiHandle, mAppName);
  }

  return Status;

}

/**
  Insert entry information in database.

  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.
  @param[in] Data        The pointer to the data.
  @param[in] Context     The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.

  @retval EFI_SUCCESS    Continue the iteration.
  @retval EFI_ABORTED    Abort the iteration.
**/
EFI_STATUS
InsertPolicyEntry (
  IN EFI_IPSEC_CONFIG_SELECTOR      *Selector,
  IN VOID                           *Data,
  IN INSERT_POLICY_ENTRY_CONTEXT    *Context
  )
{
  //
  // Found the entry which we want to insert before.
  //
  if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {

    Context->Status = mIpSecConfig->SetData (
                                      mIpSecConfig,
                                      Context->DataType,
                                      Context->Selector,
                                      Context->Data,
                                      Selector
                                      );
    //
    // Abort the iteration after the insertion.
    //
    return EFI_ABORTED;
  }

  return EFI_SUCCESS;
}

/**
  Insert or add entry information in database according to datatype.

  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.
  @param[in] ParamPackage    The pointer to the ParamPackage list.

  @retval EFI_SUCCESS             Insert or add entry information successfully.
  @retval EFI_NOT_FOUND           Can't find the specified entry.
  @retval EFI_BUFFER_TOO_SMALL    The entry already existed.
  @retval EFI_UNSUPPORTED         The operation is not supported.
  @retval Others                  Some mistaken case.
**/
EFI_STATUS
AddOrInsertPolicyEntry (
  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,
  IN LIST_ENTRY                    *ParamPackage
  )
{
  EFI_STATUS                     Status;
  EFI_IPSEC_CONFIG_SELECTOR      *Selector;
  VOID                           *Data;
  INSERT_POLICY_ENTRY_CONTEXT    Context;
  UINT32                         Mask;
  UINTN                          DataSize;
  CONST CHAR16                   *ValueStr;

  Status = mCreatePolicyEntry[DataType] (&Selector, &Data, ParamPackage, &Mask, TRUE);
  if (!EFI_ERROR (Status)) {
    //
    // Find if the Selector to be inserted already exists.
    //
    DataSize = 0;
    Status = mIpSecConfig->GetData (
                             mIpSecConfig,
                             DataType,
                             Selector,
                             &DataSize,
                             NULL
                             );
    if (Status == EFI_BUFFER_TOO_SMALL) {
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_EXISTS), mHiiHandle, mAppName);
    } else if (ShellCommandLineGetFlag (ParamPackage, L"-a")) {
      Status = mIpSecConfig->SetData (
                               mIpSecConfig,
                               DataType,
                               Selector,
                               Data,
                               NULL
                               );
    } else {
      ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");
      if (ValueStr == NULL) {
        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);
        return EFI_NOT_FOUND;
      }

      Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);
      if (!EFI_ERROR (Status)) {
        Context.DataType  = DataType;
        Context.Status    = EFI_NOT_FOUND;
        Context.Selector  = Selector;
        Context.Data      = Data;

        ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) InsertPolicyEntry, &Context);
        Status = Context.Status;
        if (Status == EFI_NOT_FOUND) {
          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);
        }
      }
    }

    gBS->FreePool (Selector);
    gBS->FreePool (Data);
  }

  if (Status == EFI_UNSUPPORTED) {
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_UNSUPPORT), mHiiHandle, mAppName);
  } else if (EFI_ERROR (Status)) {
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_FAILED), mHiiHandle, mAppName);
  }

  return Status;
}