/** @file
  The general interfaces of the IKEv2.

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

/**
  General interface to intialize a IKEv2 negotiation.

  @param[in]  UdpService      Point to Udp Servcie used for the IKE packet sending.
  @param[in]  SpdEntry        Point to SPD entry related to this IKE negotiation.
  @param[in]  PadEntry        Point to PAD entry related to this IKE negotiation.
  @param[in]  RemoteIp        Point to IP Address which the remote peer to negnotiate.

  @retval EFI_SUCCESS           The operation is successful.
  @retval EFI_OUT_OF_RESOURCES  The required system resource can't be allocated.
  @retval EFI_INVALID_PARAMETER If UdpService or RemoteIp is NULL.
  @return Others                The operation is failed.

**/
EFI_STATUS
Ikev2NegotiateSa (
  IN IKE_UDP_SERVICE         *UdpService,
  IN IPSEC_SPD_ENTRY         *SpdEntry,
  IN IPSEC_PAD_ENTRY         *PadEntry,
  IN EFI_IP_ADDRESS          *RemoteIp
  )
{
  IPSEC_PRIVATE_DATA        *Private;
  IKEV2_SA_SESSION          *IkeSaSession;
  IKEV2_SESSION_COMMON      *SessionCommon;
  IKEV2_PACKET_HANDLER      Handler;
  IKE_PACKET                *IkePacket;
  EFI_STATUS                Status;
  
  if (UdpService == NULL || RemoteIp == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  IkePacket = NULL;
  Private   = (UdpService->IpVersion == IP_VERSION_4) ?
               IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) :
               IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead);

  //
  // Lookup the remote ip address in the processing IKE SA session list.
  //
  IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2SessionList, RemoteIp);
  if (IkeSaSession != NULL) {
    //
    // Drop the packet if already in process.
    //
    return EFI_SUCCESS;
  }
  
  //
  // Create a new IkeSaSession and initiate the common parameters.
  //
  IkeSaSession = Ikev2SaSessionAlloc (Private, UdpService);
  if (IkeSaSession == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Set the specific parameters and state(IKE_STATE_INIT).
  //
  IkeSaSession->Spd            = SpdEntry;
  IkeSaSession->Pad            = PadEntry;  
  SessionCommon                = &IkeSaSession->SessionCommon;
  SessionCommon->IsInitiator   = TRUE;
  SessionCommon->State         = IkeStateInit;
  //
  // TODO: Get the prefer DH Group from the IPsec Configuration, after the IPsecconfig application update
  // to support it.
  //
  SessionCommon->PreferDhGroup = IKEV2_TRANSFORM_ID_DH_1024MODP;
  
  CopyMem (
    &SessionCommon->RemotePeerIp,
    RemoteIp,
    sizeof (EFI_IP_ADDRESS)
    );
  
  CopyMem (
    &SessionCommon->LocalPeerIp,
    &UdpService->DefaultAddress,
    sizeof (EFI_IP_ADDRESS)
    );

  IKEV2_DUMP_STATE (SessionCommon->State, IkeStateInit);

  //
  // Initiate the SAD data of the IkeSaSession.
  //
  IkeSaSession->SaData = Ikev2InitializeSaData (SessionCommon);
  if (IkeSaSession->SaData == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_ERROR;
  }

  //
  // Generate an IKE request packet and send it out.
  //
  Handler   = mIkev2Initial[IkeSaSession->Pad->Data->AuthMethod][SessionCommon->State];
  IkePacket = Handler.Generator ((UINT8 *) IkeSaSession, NULL);
  if (IkePacket == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_ERROR;
  }

  Status = Ikev2SendIkePacket (UdpService, (UINT8 *) SessionCommon, IkePacket, 0);

  if (EFI_ERROR (Status)) {
    goto ON_ERROR;
  }

  //
  // Insert the current IkeSaSession into the processing IKE SA list.
  //
  Ikev2SaSessionInsert (&Private->Ikev2SessionList, IkeSaSession, RemoteIp);

  return EFI_SUCCESS;

ON_ERROR:

  if (IkePacket != NULL) {
    IkePacketFree (IkePacket);
  }
  Ikev2SaSessionFree (IkeSaSession);
  return Status;
}

/**
  It is general interface to negotiate the Child SA.

  There are three situations which will invoke this function. First, create a CHILD 
  SA if the input Context is NULL. Second, rekeying the existing IKE SA if the Context 
  is a IKEv2_SA_SESSION. Third, rekeying the existing CHILD SA if the context is a 
  IKEv2_CHILD_SA_SESSION.

  @param[in] IkeSaSession  Pointer to IKEv2_SA_SESSION related to this operation.
  @param[in] SpdEntry      Pointer to IPSEC_SPD_ENTRY related to this operation.
  @param[in] Context       The data pass from the caller.
  
  @retval EFI_SUCCESS          The operation is successful.
  @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.
  @retval EFI_UNSUPPORTED      The condition is not support yet.
  @return Others               The operation is failed.

**/
EFI_STATUS
Ikev2NegotiateChildSa (
  IN UINT8           *IkeSaSession,
  IN IPSEC_SPD_ENTRY *SpdEntry,
  IN UINT8           *Context
  )
{
  EFI_STATUS                Status;
  IKEV2_SA_SESSION          *SaSession;
  IKEV2_CHILD_SA_SESSION    *ChildSaSession;
  IKEV2_SESSION_COMMON      *ChildSaCommon;
  IKE_PACKET                *IkePacket;
  IKE_UDP_SERVICE           *UdpService;

  SaSession  = (IKEV2_SA_SESSION*) IkeSaSession;
  UdpService = SaSession->SessionCommon.UdpService;
  IkePacket  = NULL;

  //
  // 1. Create another child SA session if context is null.
  // 2. Rekeying the IKE SA session if the context is IKE SA session.
  // 3. Rekeying the child SA session if the context is child SA session.
  //
  if (Context == NULL) {
    //
    // Create a new ChildSaSession and initiate the common parameters.
    //
    ChildSaSession = Ikev2ChildSaSessionAlloc (UdpService, SaSession);

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

    //
    // Set the specific parameters and state as IKE_STATE_CREATE_CHILD.
    //
    ChildSaSession->Spd         = SpdEntry;
    ChildSaCommon               = &ChildSaSession->SessionCommon;
    ChildSaCommon->IsInitiator  = TRUE;
    ChildSaCommon->State        = IkeStateCreateChild;

    IKEV2_DUMP_STATE (ChildSaCommon->State, IkeStateCreateChild);

    if (SpdEntry->Selector->NextLayerProtocol != EFI_IPSEC_ANY_PROTOCOL) {
      ChildSaSession->ProtoId = SpdEntry->Selector->NextLayerProtocol;
    }

    if (SpdEntry->Selector->LocalPort != EFI_IPSEC_ANY_PORT) {
      ChildSaSession->LocalPort = SpdEntry->Selector->LocalPort;
    }

    if (SpdEntry->Selector->RemotePort != EFI_IPSEC_ANY_PORT) {
      ChildSaSession->RemotePort = SpdEntry->Selector->RemotePort;
    }
    //
    // Initiate the SAD data parameters of the ChildSaSession.
    //
    ChildSaSession->SaData = Ikev2InitializeSaData (ChildSaCommon);
    if (ChildSaSession->SaData == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto ON_ERROR;
    }
    //
    // Generate an IKE request packet and send it out.
    //
    IkePacket = mIkev2CreateChild.Generator ((UINT8 *) ChildSaSession, NULL);

    if (IkePacket == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto ON_ERROR;
    }

    Status = Ikev2SendIkePacket (UdpService, (UINT8 *) ChildSaCommon, IkePacket, 0);

    if (EFI_ERROR (Status)) {
      goto ON_ERROR;
    }
    
    //
    // Insert the ChildSaSession into processing child SA list.
    //
    Ikev2ChildSaSessionInsert (&SaSession->ChildSaSessionList, ChildSaSession);
  } else {
    //
    // TODO: Rekeying IkeSaSession or ChildSaSession, NOT support yet.
    //
    // Rekey IkeSa, set IkeSaSession->State and pass over IkeSaSession
    // Rekey ChildSa, set ChildSaSession->State and pass over ChildSaSession
    //
    return EFI_UNSUPPORTED;
  }

  return EFI_SUCCESS;

ON_ERROR:

  if (ChildSaSession->SaData != NULL) {
    FreePool (ChildSaSession->SaData);
  }

  if (ChildSaSession->SessionCommon.TimeoutEvent != NULL) {
    gBS->CloseEvent (ChildSaSession->SessionCommon.TimeoutEvent);
  }

  if (IkePacket != NULL) {
    IkePacketFree (IkePacket);
  }

  Ikev2ChildSaSessionFree (ChildSaSession);
  return Status;
}

/**
  It is general interface to start the Information Exchange.

  There are three situations which will invoke this function. First, deliver a Delete Information
  to delete the IKE SA if the input Context is NULL and the state of related IkeSaSeesion's is on 
  deleting.Second, deliver a Notify Information without the contents if the input Context is NULL. 
  Third, deliver a Notify Information if the input Context is not NULL.

  @param[in] IkeSaSession  Pointer to IKEv2_SA_SESSION related to this operation.
  @param[in] Context       Data passed by caller.

  @retval EFI_SUCCESS          The operation is successful.
  @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.
  @retval EFI_UNSUPPORTED      The condition is not support yet.
  @return Otherwise            The operation is failed.

**/
EFI_STATUS
Ikev2NegotiateInfo (
  IN UINT8           *IkeSaSession,
  IN UINT8           *Context
  )
{
  
  EFI_STATUS                Status;
  IKEV2_SA_SESSION          *Ikev2SaSession;
  IKEV2_CHILD_SA_SESSION    *ChildSaSession;
  IKEV2_SESSION_COMMON      *SaCommon;
  IKE_PACKET                *IkePacket;
  IKE_UDP_SERVICE           *UdpService;
  LIST_ENTRY                *Entry;
  LIST_ENTRY                *NextEntry;

  Ikev2SaSession = (IKEV2_SA_SESSION *) IkeSaSession;
  UdpService     = Ikev2SaSession->SessionCommon.UdpService;
  SaCommon       = &Ikev2SaSession->SessionCommon;
  IkePacket      = NULL;
  Status         = EFI_SUCCESS;

  //
  // Delete the IKE SA.
  //
  if (Ikev2SaSession->SessionCommon.State == IkeStateSaDeleting && Context == NULL) {

    //
    // The IKE SA Session should be initiator if it triggers the deleting.
    //
    Ikev2SaSession->SessionCommon.IsInitiator = TRUE;

    //
    // Generate Information Packet which contains the Delete Payload.
    //
    IkePacket = mIkev2Info.Generator ((UINT8 *) Ikev2SaSession, NULL);
    if (IkePacket == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto ON_ERROR;
    }

    //
    // Send out the Packet
    //
    Status = Ikev2SendIkePacket (UdpService, (UINT8 *) SaCommon, IkePacket, 0);

    if (EFI_ERROR (Status)) {
      goto ON_ERROR;
    }
  } else if (!IsListEmpty (&Ikev2SaSession->DeleteSaList)) {
    //
    // Iterate all Deleting Child SAs.
    //
    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Ikev2SaSession->DeleteSaList) {
      ChildSaSession                      = IKEV2_CHILD_SA_SESSION_BY_DEL_SA (Entry);
      ChildSaSession->SessionCommon.State = IkeStateSaDeleting;

      //
      // Generate Information Packet which contains the Child SA Delete Payload.
      //
      IkePacket = mIkev2Info.Generator ((UINT8 *) ChildSaSession, NULL);
      if (IkePacket == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        goto ON_ERROR;
      }

      //
      // Send out the Packet
      //
      Status = Ikev2SendIkePacket (UdpService, (UINT8 *) &ChildSaSession->SessionCommon, IkePacket, 0);

      if (EFI_ERROR (Status)) {
        goto ON_ERROR;
      }
    }
  }  else if (Context == NULL) {
    //
    // TODO: Deliver null notification message.
    //
  }  else if (Context != NULL) {
    //
    // TODO: Send out the Information Exchange which contains the Notify Payload.
    //
  }
ON_ERROR:
  if (IkePacket != NULL) {
    IkePacketFree (IkePacket);
  }
  return Status;

}

/**
  The general interface when received a IKEv2 packet for the IKE SA establishing.

  This function first find the related IKE SA Session according to the IKE packet's 
  remote IP. Then call the corresponding function to handle this IKE packet according
  to the related IKE SA Session's State. 

  @param[in] UdpService    Pointer of related UDP Service.
  @param[in] IkePacket     Data passed by caller.

**/
VOID
Ikev2HandleSa (
  IN IKE_UDP_SERVICE     *UdpService,
  IN IKE_PACKET          *IkePacket
  )
{
  EFI_STATUS              Status;
  IKEV2_SA_SESSION        *IkeSaSession;
  IKEV2_CHILD_SA_SESSION  *ChildSaSession;
  IKEV2_SESSION_COMMON    *IkeSaCommon;
  IKEV2_SESSION_COMMON    *ChildSaCommon;
  IKEV2_PACKET_HANDLER    Handler;
  IKE_PACKET              *Reply;
  IPSEC_PAD_ENTRY         *PadEntry;
  IPSEC_PRIVATE_DATA      *Private;
  BOOLEAN                 IsNewSession;

  Private = (UdpService->IpVersion == IP_VERSION_4) ? 
             IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) :
             IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead);

  ChildSaSession = NULL;
  ChildSaCommon  = NULL;
  
  //
  // Lookup the remote ip address in the processing IKE SA session list.
  //
  IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2SessionList, &IkePacket->RemotePeerIp);
  IsNewSession = FALSE;

  if (IkeSaSession == NULL) {
    //
    // Lookup the remote ip address in the pad.
    //
    PadEntry = IpSecLookupPadEntry (UdpService->IpVersion, &IkePacket->RemotePeerIp);
    if (PadEntry == NULL) {
      //
      // Drop the packet if no pad entry matched, this is the request from RFC 4301.
      //
      return ;
    }

    //
    // Create a new IkeSaSession and initiate the common parameters.
    //
    IkeSaSession             = Ikev2SaSessionAlloc (Private, UdpService);
    if (IkeSaSession == NULL) {
      return;
    }
    IkeSaSession->Pad        = PadEntry;
    IkeSaCommon              = &IkeSaSession->SessionCommon;
    IkeSaCommon->IsInitiator = FALSE;
    IkeSaCommon->State       = IkeStateInit;

    IKEV2_DUMP_STATE (IkeSaCommon->State, IkeStateInit);

    CopyMem (
      &IkeSaCommon->RemotePeerIp,
      &IkePacket->RemotePeerIp,
      sizeof (EFI_IP_ADDRESS)
      );

    CopyMem (
      &IkeSaCommon->LocalPeerIp,
      &UdpService->DefaultAddress,
      sizeof (EFI_IP_ADDRESS)
      );
    
    IsNewSession = TRUE;
  }

  //
  // Validate the IKE packet header.
  //
  Status = Ikev2ValidateHeader (IkeSaSession, IkePacket->Header);
  if (EFI_ERROR (Status)) {
    //
    // Drop the packet if invalid IKE header.
    //
    goto ON_ERROR;
  }

  //
  // Decode all the payloads in the IKE packet.
  //
  IkeSaCommon = &IkeSaSession->SessionCommon;
  Status      = Ikev2DecodePacket (IkeSaCommon, IkePacket, IkeSessionTypeIkeSa);
  if (EFI_ERROR (Status)) {
    goto ON_ERROR;
  }

  //
  // Try to reate the first ChildSa Session of that IkeSaSession.
  // If the IkeSaSession is responder, here will create the first ChildSaSession.
  //
  if (IkeSaCommon->State == IkeStateAuth && IsListEmpty(&IkeSaSession->ChildSaSessionList)) {
    //
    // Generate a piggyback child SA in IKE_STATE_AUTH state.
    //
    ASSERT (IsListEmpty (&IkeSaSession->ChildSaSessionList) &&
            IsListEmpty (&IkeSaSession->ChildSaEstablishSessionList));

    ChildSaSession = Ikev2ChildSaSessionCreate (IkeSaSession, UdpService);
    ChildSaCommon  = &ChildSaSession->SessionCommon;
  }

  //
  // Parse the IKE request packet according to the auth method and current state.
  //
  Handler = mIkev2Initial[IkeSaSession->Pad->Data->AuthMethod][IkeSaCommon->State];
  Status  = Handler.Parser ((UINT8 *)IkeSaSession, IkePacket);
  if (EFI_ERROR (Status)) {
    goto ON_ERROR;
  }

  //
  // Try to reate the first ChildSa Session of that IkeSaSession.
  // If the IkeSaSession is initiator, here will create the first ChildSaSession.
  //
  if (IkeSaCommon->State == IkeStateAuth && IsListEmpty(&IkeSaSession->ChildSaSessionList)) {
    //
    // Generate a piggyback child SA in IKE_STATE_AUTH state.
    //
    ASSERT (IsListEmpty (&IkeSaSession->ChildSaSessionList) && 
            IsListEmpty (&IkeSaSession->ChildSaEstablishSessionList));
    
    ChildSaSession = Ikev2ChildSaSessionCreate (IkeSaSession, UdpService);
    ChildSaCommon  = &ChildSaSession->SessionCommon;

    //
    // Initialize the SA data for Child SA.
    //    
    ChildSaSession->SaData = Ikev2InitializeSaData (ChildSaCommon);
  }

  //
  // Generate the IKE response packet and send it out if not established.
  //
  if (IkeSaCommon->State != IkeStateIkeSaEstablished) {
    Handler = mIkev2Initial[IkeSaSession->Pad->Data->AuthMethod][IkeSaCommon->State];
    Reply   = Handler.Generator ((UINT8 *) IkeSaSession, NULL);
    if (Reply == NULL) {
      goto ON_ERROR;
    }

    Status = Ikev2SendIkePacket (UdpService, (UINT8 *) IkeSaCommon, Reply, 0);
    if (EFI_ERROR (Status)) {
      goto ON_ERROR;
    }
    if (!IkeSaCommon->IsInitiator) {
      IkeSaCommon->State ++;
      IKEV2_DUMP_STATE (IkeSaCommon->State - 1, IkeSaCommon->State);
    }
  }

  //
  // Insert the new IkeSaSession into the Private processing IkeSaSession List.
  //
  if (IsNewSession) {
    Ikev2SaSessionInsert (&Private->Ikev2SessionList, IkeSaSession, &IkePacket->RemotePeerIp);
  }

  //
  // Register the IkeSaSession and remove it from processing list.
  //
  if (IkeSaCommon->State == IkeStateIkeSaEstablished) {

    //
    // Remove the Established IKE SA Session from the IKE SA Session Negotiating list
    // and insert it into IKE SA Session Established list.
    //
    Ikev2SaSessionRemove (&Private->Ikev2SessionList, &IkePacket->RemotePeerIp);
    Ikev2SaSessionReg (IkeSaSession, Private);

    //
    // Remove the Established Child SA Session from the IkeSaSession->ChildSaSessionList
    // ,insert it into IkeSaSession->ChildSaEstablishSessionList and save this Child SA 
    // into SAD.
    //
    ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (IkeSaSession->ChildSaSessionList.BackLink);
    Ikev2ChildSaSessionRemove (
      &IkeSaSession->ChildSaSessionList,
      ChildSaSession->LocalPeerSpi,
      IKEV2_ESTABLISHING_CHILDSA_LIST
      );
    Ikev2ChildSaSessionReg (ChildSaSession, Private);
  }

  return ;

ON_ERROR:
  if (ChildSaSession != NULL) {
    //
    // Remove the ChildSa from the list (Established list or Negotiating list).
    //
    RemoveEntryList (&ChildSaSession->ByIkeSa);
    Ikev2ChildSaSessionFree (ChildSaSession);
  }

  if (IsNewSession && IkeSaSession != NULL) {
    //
    // Remove the IkeSa from the list (Established list or Negotiating list).
    //
    if ((&IkeSaSession->BySessionTable)->ForwardLink != NULL &&
        !IsListEmpty (&IkeSaSession->BySessionTable
       )){
      RemoveEntryList (&IkeSaSession->BySessionTable);
    }
    Ikev2SaSessionFree (IkeSaSession);
  }

  return ;
}

/**

  The general interface when received a IKEv2 packet for the IKE Child SA establishing 
  or IKE SA/CHILD SA rekeying.

  This function first find the related IKE SA Session according to the IKE packet's 
  remote IP. Then call the corresponding function to handle this IKE packet according
  to the related IKE Child Session's State. 

  @param[in] UdpService    Pointer of related UDP Service.
  @param[in] IkePacket     Data passed by caller.

**/
VOID
Ikev2HandleChildSa (
  IN IKE_UDP_SERVICE  *UdpService,
  IN IKE_PACKET       *IkePacket
  )
{
  EFI_STATUS                       Status;
  IKEV2_SA_SESSION                 *IkeSaSession;
  IKEV2_CREATE_CHILD_REQUEST_TYPE  RequestType;
  IKE_PACKET                       *Reply;
  IPSEC_PRIVATE_DATA               *Private;
  
  Private = (UdpService->IpVersion == IP_VERSION_4) ? 
             IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) :
             IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead);

  Reply   = NULL;

  //
  // Lookup the remote ip address in the processing IKE SA session list.
  //
  IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2EstablishedList, &IkePacket->RemotePeerIp);

  if (IkeSaSession == NULL) {
    //
    // Drop the packet if no IKE SA associated.
    //
    return ;
  }

  //
  // Validate the IKE packet header.
  //
  if (!Ikev2ValidateHeader (IkeSaSession, IkePacket->Header)) {
    //
    // Drop the packet if invalid IKE header.
    //
    return;
  }

  //
  // Decode all the payloads in the IKE packet.
  //
  Status = Ikev2DecodePacket (&IkeSaSession->SessionCommon, IkePacket, IkeSessionTypeIkeSa);
  if (EFI_ERROR (Status)) {
    return;
  }

  //
  // Get the request type: CreateChildSa/RekeyChildSa/RekeyIkeSa.
  //  
  RequestType = Ikev2ChildExchangeRequestType (IkePacket);

  switch (RequestType) {
  case IkeRequestTypeCreateChildSa:
  case IkeRequestTypeRekeyChildSa: 
  case IkeRequestTypeRekeyIkeSa: 
    //
    // Parse the IKE request packet. Not support CREATE_CHILD_SA exchange yet, so
    // only EFI_UNSUPPORTED will be returned and that will trigger a reply with a 
    // Notify payload of type NO_ADDITIONAL_SAS.
    //
    Status = mIkev2CreateChild.Parser ((UINT8 *) IkeSaSession, IkePacket);
    if (EFI_ERROR (Status)) {     
      goto ON_REPLY;
    }

  default:
    //
    // No support.
    //
    return ;
  }
  
ON_REPLY:
  //
  // Generate the reply packet if needed and send it out.
  //
  if (IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) {
    Reply = mIkev2CreateChild.Generator ((UINT8 *) IkeSaSession, &IkePacket->Header->MessageId);
    if (Reply != NULL) {
      Status = Ikev2SendIkePacket (UdpService, (UINT8 *) &(IkeSaSession->SessionCommon), Reply, 0);
      if (EFI_ERROR (Status)) {
        //
        //  Delete Reply payload.
        //
        if (Reply != NULL) {
          IkePacketFree (Reply);
        }
      }
    }
  }  
  return ;
}

/**

  It is general interface to handle IKEv2 information Exchange.
  
  @param[in] UdpService  Point to IKE UPD Service related to this information exchange.  
  @param[in] IkePacket   The IKE packet to be parsed.

**/
VOID
Ikev2HandleInfo (
  IN IKE_UDP_SERVICE  *UdpService,
  IN IKE_PACKET       *IkePacket
  )
{
  EFI_STATUS              Status;
  IKEV2_SESSION_COMMON    *SessionCommon;
  IKEV2_SA_SESSION        *IkeSaSession;
  IPSEC_PRIVATE_DATA      *Private;

  Private = (UdpService->IpVersion == IP_VERSION_4) ? 
             IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : 
             IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead);

  //
  // Lookup the remote ip address in the processing IKE SA session list.
  //
  IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2EstablishedList, &IkePacket->RemotePeerIp);
  
  if (IkeSaSession == NULL) {
    //
    // Drop the packet if no IKE SA associated.
    //
    return ;
  }
  //
  // Validate the IKE packet header.
  //
  if (!Ikev2ValidateHeader (IkeSaSession, IkePacket->Header)) {
  
    //
    // Drop the packet if invalid IKE header.
    //
    return;
  }  

  SessionCommon = &IkeSaSession->SessionCommon;

  //
  // Decode all the payloads in the IKE packet.
  //
  Status = Ikev2DecodePacket (SessionCommon, IkePacket, IkeSessionTypeIkeSa);
  if (EFI_ERROR (Status)) {
    return;
  }

  Status = mIkev2Info.Parser ((UINT8 *)IkeSaSession, IkePacket);

  if (EFI_ERROR (Status)) {
    //
    // Drop the packet if fail to parse.
    //
    return;
  }    
}

IKE_EXCHANGE_INTERFACE  mIkev1Exchange = {
  1,
  NULL, //Ikev1NegotiateSa
  NULL, //Ikev1NegotiateChildSa
  NULL,
  NULL, //Ikev1HandleSa,
  NULL, //Ikev1HandleChildSa
  NULL, //Ikev1HandleInfo
};

IKE_EXCHANGE_INTERFACE  mIkev2Exchange = {
  2,
  Ikev2NegotiateSa,
  Ikev2NegotiateChildSa,
  Ikev2NegotiateInfo,
  Ikev2HandleSa,
  Ikev2HandleChildSa,
  Ikev2HandleInfo
};

