]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/IpSecDxe/IpSecImpl.c
NetworkPkg: Remove unused variables from IpSecDxe to fix GCC build.
[mirror_edk2.git] / NetworkPkg / IpSecDxe / IpSecImpl.c
index b693eb94d1cc44391bd78d4301f9958acf152788..3a8a6e9b584f69d036ff35ebd4ee721ffc8893b7 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
-  The implementation of IPsec Protocol\r
+  The implementation of IPsec.\r
 \r
-  Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+  (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>\r
+  Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
 \r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
 \r
 **/\r
 \r
+#include "IpSecImpl.h"\r
+#include "IkeService.h"\r
+#include "IpSecDebug.h"\r
+#include "IpSecCryptIo.h"\r
 #include "IpSecConfigImpl.h"\r
 \r
-EFI_IPSEC2_PROTOCOL  mIpSecInstance = { IpSecProcess, NULL, TRUE };\r
-\r
-extern LIST_ENTRY   mConfigData[IPsecConfigDataTypeMaximum];\r
-\r
 /**\r
   Check if the specified Address is the Valid Address Range.\r
 \r
   This function checks if the bytes after prefixed length are all Zero in this\r
-  Address. This Address is supposed to point to a range address,  meaning it only\r
-  gives the correct prefixed address.\r
+  Address. This Address is supposed to point to a range address. That means it\r
+  should gives the correct prefixed address and the bytes outside the prefixed are\r
+  zero.\r
 \r
   @param[in]  IpVersion         The IP version.\r
   @param[in]  Address           Points to EFI_IP_ADDRESS to be checked.\r
@@ -208,7 +210,6 @@ IpSecMatchIpAddress (
       break;\r
     }\r
   }\r
-\r
   return IsMatch;\r
 }\r
 \r
@@ -315,6 +316,7 @@ IpSecMatchNextLayerProtocol (
 \r
   @param[in]  SadList           SAD list related to a specified SPD entry.\r
   @param[in]  DestAddress       The destination address used to find the SAD entry.\r
+  @param[in]  IpVersion         The IP version. Ip4 or Ip6.\r
 \r
   @return  The pointer to a certain SAD entry.\r
 \r
@@ -322,23 +324,25 @@ IpSecMatchNextLayerProtocol (
 IPSEC_SAD_ENTRY *\r
 IpSecLookupSadBySpd (\r
   IN LIST_ENTRY                 *SadList,\r
-  IN EFI_IP_ADDRESS             *DestAddress\r
+  IN EFI_IP_ADDRESS             *DestAddress,\r
+  IN UINT8                      IpVersion\r
   )\r
 {\r
   LIST_ENTRY      *Entry;\r
   IPSEC_SAD_ENTRY *SadEntry;\r
 \r
-  for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {\r
+  NET_LIST_FOR_EACH (Entry, SadList) {\r
 \r
     SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);\r
     //\r
-    // Find the right sad entry which contains the appointed dest address.\r
+    // Find the right SAD entry which contains the appointed dest address.\r
     //\r
-    if (CompareMem (\r
-          &SadEntry->Id->DestAddress,\r
+    if (IpSecMatchIpAddress (\r
+          IpVersion,\r
           DestAddress,\r
-          sizeof (EFI_IP_ADDRESS)\r
-          ) == 0) {\r
+          SadEntry->Data->SpdSelector->RemoteAddress,\r
+          SadEntry->Data->SpdSelector->RemoteAddressCount\r
+          )){\r
       return SadEntry;\r
     }\r
   }\r
@@ -351,6 +355,7 @@ IpSecLookupSadBySpd (
 \r
   @param[in]  Spi               The SPI used to search the SAD entry.\r
   @param[in]  DestAddress       The destination used to search the SAD entry.\r
+  @param[in]  IpVersion         The IP version. Ip4 or Ip6.\r
 \r
   @return  the pointer to a certain SAD entry.\r
 \r
@@ -358,7 +363,8 @@ IpSecLookupSadBySpd (
 IPSEC_SAD_ENTRY *\r
 IpSecLookupSadBySpi (\r
   IN UINT32                   Spi,\r
-  IN EFI_IP_ADDRESS           *DestAddress\r
+  IN EFI_IP_ADDRESS           *DestAddress,\r
+  IN UINT8                    IpVersion\r
   )\r
 {\r
   LIST_ENTRY      *Entry;\r
@@ -367,22 +373,36 @@ IpSecLookupSadBySpi (
 \r
   SadList = &mConfigData[IPsecConfigDataTypeSad];\r
 \r
-  for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {\r
+  NET_LIST_FOR_EACH (Entry, SadList) {\r
 \r
     SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+\r
     //\r
-    // Find the right sad entry which contain the appointed spi and dest addr.\r
+    // Find the right SAD entry which contain the appointed spi and dest addr.\r
     //\r
-    if (SadEntry->Id->Spi == Spi && CompareMem (\r
-                                      &SadEntry->Id->DestAddress,\r
-                                      DestAddress,\r
-                                      sizeof (EFI_IP_ADDRESS)\r
-                                      ) == 0) {\r
-\r
-      return SadEntry;\r
+    if (SadEntry->Id->Spi == Spi) {\r
+      if (SadEntry->Data->Mode == EfiIPsecTunnel) {\r
+        if (CompareMem (\r
+              &DestAddress,\r
+              &SadEntry->Data->TunnelDestAddress,\r
+              sizeof (EFI_IP_ADDRESS)\r
+              )) {\r
+          return SadEntry;\r
+        }\r
+      } else {\r
+        if (SadEntry->Data->SpdSelector != NULL &&\r
+            IpSecMatchIpAddress (\r
+              IpVersion,\r
+              DestAddress,\r
+              SadEntry->Data->SpdSelector->RemoteAddress,\r
+              SadEntry->Data->SpdSelector->RemoteAddressCount\r
+              )\r
+            ) {\r
+          return SadEntry;\r
+        }\r
+      }\r
     }\r
   }\r
-\r
   return NULL;\r
 }\r
 \r
@@ -407,7 +427,6 @@ IpSecLookupSadBySpi (
                             - If don't find related UDP service.\r
                             - Sequence Number is used up.\r
                             - Extension Sequence Number is used up.\r
-  @retval EFI_DEVICE_ERROR  GC_TODO: Add description for return value.\r
   @retval EFI_NOT_READY     No existing SAD entry could be used.\r
   @retval EFI_SUCCESS       Find the related SAD entry.\r
 \r
@@ -424,12 +443,18 @@ IpSecLookupSadEntry (
   OUT IPSEC_SAD_ENTRY        **SadEntry\r
   )\r
 {\r
+  IKE_UDP_SERVICE *UdpService;\r
   IPSEC_SAD_ENTRY *Entry;\r
   IPSEC_SAD_DATA  *Data;\r
   EFI_IP_ADDRESS  DestIp;\r
   UINT32          SeqNum32;\r
 \r
   *SadEntry   = NULL;\r
+  UdpService  = IkeLookupUdp (Private, NicHandle, IpVersion);\r
+\r
+  if (UdpService == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
   //\r
   // Parse the destination address from ip header.\r
   //\r
@@ -447,20 +472,33 @@ IpSecLookupSadEntry (
       sizeof (EFI_IP_ADDRESS)\r
       );\r
   }\r
+\r
   //\r
-  // Find the sad entry in the spd.sas list according to the dest address.\r
+  // Find the SAD entry in the spd.sas list according to the dest address.\r
   //\r
-  Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp);\r
+  Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp, IpVersion);\r
 \r
   if (Entry == NULL) {\r
-\r
     if (OldLastHead != IP6_ICMP ||\r
         (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST)\r
         ) {\r
       //\r
-      // TODO: Start ike negotiation process except the request packet of ping.\r
+      // Start ike negotiation process except the request packet of ping.\r
       //\r
-      //IkeNegotiate (UdpService, SpdEntry, &DestIp);\r
+      if (SpdEntry->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {\r
+        IkeNegotiate (\r
+          UdpService,\r
+          SpdEntry,\r
+          &SpdEntry->Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress\r
+          );\r
+      } else {\r
+        IkeNegotiate (\r
+          UdpService,\r
+          SpdEntry,\r
+          &DestIp\r
+        );\r
+      }\r
+\r
     }\r
 \r
     return EFI_NOT_READY;\r
@@ -473,7 +511,7 @@ IpSecLookupSadEntry (
       //\r
       // Validate the 64bit sn number if 64bit sn enabled.\r
       //\r
-      if (Data->SequenceNumber + 1 < Data->SequenceNumber) {\r
+      if ((UINT64) (Data->SequenceNumber + 1) == 0) {\r
         //\r
         // TODO: Re-negotiate SA\r
         //\r
@@ -484,7 +522,7 @@ IpSecLookupSadEntry (
       // Validate the 32bit sn number if 64bit sn disabled.\r
       //\r
       SeqNum32 = (UINT32) Data->SequenceNumber;\r
-      if (SeqNum32 + 1 < SeqNum32) {\r
+      if ((UINT32) (SeqNum32 + 1) == 0) {\r
         //\r
         // TODO: Re-negotiate SA\r
         //\r
@@ -544,19 +582,21 @@ IpSecLookupPadEntry (
   @param[in]  IpPayload         Point to IP payload.\r
   @param[in]  Protocol          The Last protocol of IP packet.\r
   @param[in]  IsOutbound        Traffic direction.\r
+  @param[out] Action            The support action of SPD entry.\r
 \r
-  @retval EFI_IPSEC_ACTION  The support action of SPD entry.\r
-  @retval -1                If the input packet header doesn't match the SpdEntry.\r
+  @retval EFI_SUCCESS       Find the related SPD.\r
+  @retval EFI_NOT_FOUND     Not find the related SPD entry;\r
 \r
 **/\r
-EFI_IPSEC_ACTION\r
+EFI_STATUS\r
 IpSecLookupSpdEntry (\r
-  IN IPSEC_SPD_ENTRY         *SpdEntry,\r
-  IN UINT8                   IpVersion,\r
-  IN VOID                    *IpHead,\r
-  IN UINT8                   *IpPayload,\r
-  IN UINT8                   Protocol,\r
-  IN BOOLEAN                 IsOutbound\r
+  IN     IPSEC_SPD_ENTRY         *SpdEntry,\r
+  IN     UINT8                   IpVersion,\r
+  IN     VOID                    *IpHead,\r
+  IN     UINT8                   *IpPayload,\r
+  IN     UINT8                   Protocol,\r
+  IN     BOOLEAN                 IsOutbound,\r
+     OUT EFI_IPSEC_ACTION        *Action\r
   )\r
 {\r
   EFI_IPSEC_SPD_SELECTOR  *SpdSel;\r
@@ -637,220 +677,1501 @@ IpSecLookupSpdEntry (
 \r
   if (SpdMatch) {\r
     //\r
-    // Find the right spd entry if match the 5 key elements.\r
+    // Find the right SPD entry if match the 5 key elements.\r
     //\r
-    return SpdEntry->Data->Action;\r
+    *Action = SpdEntry->Data->Action;\r
+    return EFI_SUCCESS;\r
   }\r
 \r
-  return (EFI_IPSEC_ACTION) - 1;\r
+  return EFI_NOT_FOUND;\r
 }\r
 \r
 /**\r
-  Handles IPsec packet processing for inbound and outbound IP packets.\r
-\r
-  The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.\r
-  The behavior is that it can perform one of the following actions:\r
-  bypass the packet, discard the packet, or protect the packet.\r
-\r
-  @param[in]      This             Pointer to the EFI_IPSEC_PROTOCOL instance.\r
-  @param[in]      NicHandle        Instance of the network interface.\r
-  @param[in]      IpVersion        IPV4 or IPV6.\r
-  @param[in, out] IpHead           Pointer to the IP Header.\r
-  @param[in, out] LastHead         The protocol of the next layer to be processed by IPsec.\r
-  @param[in, out] OptionsBuffer    Pointer to the options buffer.\r
-  @param[in, out] OptionsLength    Length of the options buffer.\r
-  @param[in, out] FragmentTable    Pointer to a list of fragments.\r
-  @param[in, out] FragmentCount    Number of fragments.\r
-  @param[in]      TrafficDirection Traffic direction.\r
-  @param[out]     RecycleSignal    Event for recycling of resources.\r
-\r
-  @retval EFI_SUCCESS              The packet was bypassed and all buffers remain the same.\r
-  @retval EFI_SUCCESS              The packet was protected.\r
-  @retval EFI_ACCESS_DENIED        The packet was discarded.\r
+  The call back function of NetbufFromExt.\r
+\r
+  @param[in]  Arg            The argument passed from the caller.\r
 \r
 **/\r
-EFI_STATUS\r
+VOID\r
 EFIAPI\r
-IpSecProcess (\r
-  IN     EFI_IPSEC2_PROTOCOL              *This,\r
-  IN     EFI_HANDLE                      NicHandle,\r
-  IN     UINT8                           IpVersion,\r
-  IN OUT VOID                            *IpHead,\r
-  IN OUT UINT8                           *LastHead,\r
-  IN OUT VOID                            **OptionsBuffer,\r
-  IN OUT UINT32                          *OptionsLength,\r
-  IN OUT EFI_IPSEC_FRAGMENT_DATA         **FragmentTable,\r
-  IN OUT UINT32                          *FragmentCount,\r
-  IN     EFI_IPSEC_TRAFFIC_DIR           TrafficDirection,\r
-     OUT EFI_EVENT                       *RecycleSignal\r
+IpSecOnRecyclePacket (\r
+  IN VOID                            *Arg\r
   )\r
 {\r
-  IPSEC_PRIVATE_DATA  *Private;\r
-  IPSEC_SPD_ENTRY     *SpdEntry;\r
-  IPSEC_SAD_ENTRY     *SadEntry;\r
-  LIST_ENTRY          *SpdList;\r
-  LIST_ENTRY          *Entry;\r
-  EFI_IPSEC_ACTION    Action;\r
-  EFI_STATUS          Status;\r
-  UINT8               *IpPayload;\r
-  UINT8               OldLastHead;\r
-  BOOLEAN             IsOutbound;\r
+}\r
+\r
+/**\r
+  This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP\r
+  is released.\r
+\r
+  @param[in]  Event              The related event.\r
+  @param[in]  Context            The data passed by the caller.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IpSecRecycleCallback (\r
+  IN EFI_EVENT                       Event,\r
+  IN VOID                            *Context\r
+  )\r
+{\r
+  IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
+\r
+  RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context;\r
+\r
+  if (RecycleContext->FragmentTable != NULL) {\r
+    FreePool (RecycleContext->FragmentTable);\r
+  }\r
+\r
+  if (RecycleContext->PayloadBuffer != NULL) {\r
+    FreePool (RecycleContext->PayloadBuffer);\r
+  }\r
+\r
+  FreePool (RecycleContext);\r
+  gBS->CloseEvent (Event);\r
+\r
+}\r
+\r
+/**\r
+  Calculate the extension hader of IP. The return length only doesn't contain\r
+  the fixed IP header length.\r
+\r
+  @param[in]  IpHead             Points to an IP head to be calculated.\r
+  @param[in]  LastHead           Points to the last header of the IP header.\r
+\r
+  @return The length of the extension header.\r
 \r
-  Private         = IPSEC_PRIVATE_DATA_FROM_IPSEC (This);\r
-  IpPayload       = (*FragmentTable)[0].FragmentBuffer;\r
-  IsOutbound      = (BOOLEAN) ((TrafficDirection == EfiIPsecOutBound) ? TRUE : FALSE);\r
-  OldLastHead     = *LastHead;\r
-  *RecycleSignal  = NULL;\r
+**/\r
+UINT16\r
+IpSecGetPlainExtHeadSize (\r
+  IN VOID                             *IpHead,\r
+  IN UINT8                            *LastHead\r
+  )\r
+{\r
+  UINT16  Size;\r
+\r
+  Size = (UINT16) (LastHead - (UINT8 *) IpHead);\r
 \r
-  if (!IsOutbound) {\r
+  if (Size > sizeof (EFI_IP6_HEADER)) {\r
     //\r
-    // For inbound traffic, process the ipsec header of the packet.\r
+    // * (LastHead+1) point the last header's length but not include the first\r
+    // 8 octers, so this formluation add 8 at the end.\r
     //\r
-    Status = IpSecProtectInboundPacket (\r
-              IpVersion,\r
-              IpHead,\r
-              LastHead,\r
-              OptionsBuffer,\r
-              OptionsLength,\r
-              FragmentTable,\r
-              FragmentCount,\r
-              &SpdEntry,\r
-              RecycleSignal\r
-              );\r
+    Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8);\r
+  } else {\r
+    Size = 0;\r
+  }\r
 \r
-    if (Status == EFI_ACCESS_DENIED) {\r
-      //\r
-      // The packet is denied to access.\r
-      //\r
-      goto ON_EXIT;\r
-    }\r
+  return Size;\r
+}\r
+\r
+/**\r
+  Verify if the Authentication payload is correct.\r
+\r
+  @param[in]  EspBuffer          Points to the ESP wrapped buffer.\r
+  @param[in]  EspSize            The size of the ESP wrapped buffer.\r
+  @param[in]  SadEntry           The related SAD entry to store the authentication\r
+                                 algorithm key.\r
+  @param[in]  IcvSize            The length of ICV.\r
+\r
+  @retval EFI_SUCCESS        The authentication data is correct.\r
+  @retval EFI_ACCESS_DENIED  The authentication data is not correct.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspAuthVerifyPayload (\r
+  IN UINT8                           *EspBuffer,\r
+  IN UINTN                           EspSize,\r
+  IN IPSEC_SAD_ENTRY                 *SadEntry,\r
+  IN UINTN                           IcvSize\r
+  )\r
+{\r
+  EFI_STATUS           Status;\r
+  UINTN                AuthSize;\r
+  UINT8                IcvBuffer[12];\r
+  HASH_DATA_FRAGMENT   HashFragment[1];\r
+\r
+  //\r
+  // Calculate the size of authentication payload.\r
+  //\r
+  AuthSize  = EspSize - IcvSize;\r
+\r
+  //\r
+  // Calculate the icv buffer and size of the payload.\r
+  //\r
+  HashFragment[0].Data     = EspBuffer;\r
+  HashFragment[0].DataSize = AuthSize;\r
+\r
+  Status = IpSecCryptoIoHmac (\r
+             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId,\r
+             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength,\r
+             HashFragment,\r
+             1,\r
+             IcvBuffer,\r
+             IcvSize\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Compare the calculated icv and the appended original icv.\r
+  //\r
+  if (CompareMem (EspBuffer + AuthSize, IcvBuffer, IcvSize) == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  DEBUG ((DEBUG_ERROR, "Error auth verify payload\n"));\r
+  return EFI_ACCESS_DENIED;\r
+}\r
+\r
+/**\r
+  Search the related SAD entry by the input .\r
+\r
+  @param[in]  IpHead       The pointer to IP header.\r
+  @param[in]  IpVersion    The version of IP (IP4 or IP6).\r
+  @param[in]  Spi          The SPI used to search the related SAD entry.\r
+\r
+\r
+  @retval     NULL             Not find the related SAD entry.\r
+  @retval     IPSEC_SAD_ENTRY  Return the related SAD entry.\r
+\r
+**/\r
+IPSEC_SAD_ENTRY *\r
+IpSecFoundSadFromInboundPacket (\r
+   UINT8   *IpHead,\r
+   UINT8   IpVersion,\r
+   UINT32  Spi\r
+   )\r
+{\r
+  EFI_IP_ADDRESS   DestIp;\r
+\r
+  //\r
+  // Parse destination address from ip header.\r
+  //\r
+  ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));\r
+  if (IpVersion == IP_VERSION_4) {\r
+    CopyMem (\r
+      &DestIp,\r
+      &((IP4_HEAD *) IpHead)->Dst,\r
+      sizeof (IP4_ADDR)\r
+      );\r
+  } else {\r
+    CopyMem (\r
+      &DestIp,\r
+      &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,\r
+      sizeof (EFI_IPv6_ADDRESS)\r
+      );\r
+  }\r
+\r
+  //\r
+  // Lookup SAD entry according to the spi and dest address.\r
+  //\r
+  return IpSecLookupSadBySpi (Spi, &DestIp, IpVersion);\r
+}\r
+\r
+/**\r
+  Validate the IP6 extension header format for both the packets we received\r
+  and that we will transmit.\r
+\r
+  @param[in]  NextHeader    The next header field in IPv6 basic header.\r
+  @param[in]  ExtHdrs       The first bye of the option.\r
+  @param[in]  ExtHdrsLen    The length of the whole option.\r
+  @param[out] LastHeader    The pointer of NextHeader of the last extension\r
+                            header processed by IP6.\r
+  @param[out] RealExtsLen   The length of extension headers processed by IP6 layer.\r
+                            This is an optional parameter that may be NULL.\r
+\r
+  @retval     TRUE          The option is properly formated.\r
+  @retval     FALSE         The option is malformated.\r
+\r
+**/\r
+BOOLEAN\r
+IpSecIsIp6ExtsValid (\r
+  IN UINT8                  *NextHeader,\r
+  IN UINT8                  *ExtHdrs,\r
+  IN UINT32                 ExtHdrsLen,\r
+  OUT UINT8                 **LastHeader,\r
+  OUT UINT32                *RealExtsLen    OPTIONAL\r
+  )\r
+{\r
+  UINT32                     Pointer;\r
+  UINT8                      *Option;\r
+  UINT8                      OptionLen;\r
+  UINT8                      CountD;\r
+  UINT8                      CountF;\r
+  UINT8                      CountA;\r
+\r
+  if (RealExtsLen != NULL) {\r
+    *RealExtsLen = 0;\r
+  }\r
+\r
+  *LastHeader = NextHeader;\r
+\r
+  if (ExtHdrs == NULL && ExtHdrsLen == 0) {\r
+    return TRUE;\r
+  }\r
+\r
+  if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {\r
+    return FALSE;\r
+  }\r
+\r
+  Pointer = 0;\r
+  CountD  = 0;\r
+  CountF  = 0;\r
+  CountA  = 0;\r
+\r
+  while (Pointer <= ExtHdrsLen) {\r
+\r
+    switch (*NextHeader) {\r
+    case IP6_HOP_BY_HOP:\r
+      if (Pointer != 0) {\r
+        return FALSE;\r
+      }\r
+\r
+    //\r
+    // Fall through\r
+    //\r
+    case IP6_DESTINATION:\r
+      if (*NextHeader == IP6_DESTINATION) {\r
+        CountD++;\r
+      }\r
+\r
+      if (CountD > 2) {\r
+        return FALSE;\r
+      }\r
+\r
+      NextHeader = ExtHdrs + Pointer;\r
 \r
-    if (Status == EFI_SUCCESS) {\r
+      Pointer++;\r
+      Option     = ExtHdrs + Pointer;\r
+      OptionLen  = (UINT8) ((*Option + 1) * 8 - 2);\r
+      Option++;\r
+      Pointer++;\r
+\r
+      Pointer = Pointer + OptionLen;\r
+      break;\r
+\r
+    case IP6_FRAGMENT:\r
+      if (++CountF > 1) {\r
+        return FALSE;\r
+      }\r
       //\r
-      // Check the spd entry if the packet is accessible.\r
+      // RFC2402, AH header should after fragment header.\r
       //\r
-      if (SpdEntry == NULL) {\r
-        Status = EFI_ACCESS_DENIED;\r
-        goto ON_EXIT;\r
+      if (CountA > 1) {\r
+        return FALSE;\r
       }\r
-      Action = IpSecLookupSpdEntry (\r
-                SpdEntry,\r
-                IpVersion,\r
-                IpHead,\r
-                IpPayload,\r
-                *LastHead,\r
-                IsOutbound\r
-                );\r
-\r
-      if (Action != EfiIPsecActionProtect) {\r
-        //\r
-        // Discard the packet if the spd entry is not protect.\r
-        //\r
-        gBS->SignalEvent (*RecycleSignal);\r
-        *RecycleSignal  = NULL;\r
-        Status          = EFI_ACCESS_DENIED;\r
+\r
+      NextHeader = ExtHdrs + Pointer;\r
+      Pointer    = Pointer + 8;\r
+      break;\r
+\r
+    case IP6_AH:\r
+      if (++CountA > 1) {\r
+        return FALSE;\r
       }\r
 \r
-      goto ON_EXIT;\r
+      Option     = ExtHdrs + Pointer;\r
+      NextHeader = Option;\r
+      Option++;\r
+      //\r
+      // RFC2402, Payload length is specified in 32-bit words, minus "2".\r
+      //\r
+      OptionLen  = (UINT8) ((*Option + 2) * 4);\r
+      Pointer    = Pointer + OptionLen;\r
+      break;\r
+\r
+    default:\r
+      *LastHeader = NextHeader;\r
+       if (RealExtsLen != NULL) {\r
+         *RealExtsLen = Pointer;\r
+       }\r
+\r
+       return TRUE;\r
     }\r
   }\r
 \r
-  Status  = EFI_ACCESS_DENIED;\r
-  SpdList = &mConfigData[IPsecConfigDataTypeSpd];\r
+  *LastHeader = NextHeader;\r
+\r
+  if (RealExtsLen != NULL) {\r
+    *RealExtsLen = Pointer;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  The actual entry to process the tunnel header and inner header for tunnel mode\r
+  outbound traffic.\r
+\r
+  This function is the subfunction of IpSecEspInboundPacket(). It change the destination\r
+  Ip address to the station address and recalculate the uplayyer's checksum.\r
+\r
+\r
+  @param[in, out] IpHead             Points to the IP header containing the ESP header\r
+                                     to be trimed on input, and without ESP header\r
+                                     on return.\r
+  @param[in]      IpPayload          The decrypted Ip payload. It start from the inner\r
+                                     header.\r
+  @param[in]      IpVersion          The version of IP.\r
+  @param[in]      SadData            Pointer of the relevant SAD.\r
+  @param[in, out] LastHead           The Last Header in IP header on return.\r
+\r
+**/\r
+VOID\r
+IpSecTunnelInboundPacket (\r
+  IN OUT UINT8           *IpHead,\r
+  IN     UINT8           *IpPayload,\r
+  IN     UINT8           IpVersion,\r
+  IN     IPSEC_SAD_DATA  *SadData,\r
+  IN OUT UINT8           *LastHead\r
+  )\r
+{\r
+  EFI_UDP_HEADER   *UdpHeader;\r
+  TCP_HEAD         *TcpHeader;\r
+  UINT16            *Checksum;\r
+  UINT16           PseudoChecksum;\r
+  UINT16           PacketChecksum;\r
+  UINT32           OptionLen;\r
+  IP6_ICMP_HEAD    *Icmp6Head;\r
+\r
+  Checksum = NULL;\r
 \r
-  for (Entry = SpdList->ForwardLink; Entry != SpdList; Entry = Entry->ForwardLink) {\r
+  if (IpVersion == IP_VERSION_4) {\r
     //\r
-    // For outbound and non-ipsec Inbound traffic: check the spd entry.\r
+    // Zero OutIP header use this to indicate the input packet is under\r
+    // IPsec Tunnel protected.\r
     //\r
-    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
-    Action = IpSecLookupSpdEntry (\r
-              SpdEntry,\r
-              IpVersion,\r
-              IpHead,\r
-              IpPayload,\r
-              OldLastHead,\r
-              IsOutbound\r
-              );\r
+    ZeroMem (\r
+      (IP4_HEAD *)IpHead,\r
+      sizeof (IP4_HEAD)\r
+      );\r
+    CopyMem (\r
+      &((IP4_HEAD *)IpPayload)->Dst,\r
+      &SadData->TunnelDestAddress.v4,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
 \r
-    switch (Action) {\r
+    //\r
+    // Recalculate IpHeader Checksum\r
+    //\r
+    if (((IP4_HEAD *)(IpPayload))->Checksum != 0 ) {\r
+      ((IP4_HEAD *)(IpPayload))->Checksum = 0;\r
+      ((IP4_HEAD *)(IpPayload))->Checksum = (UINT16) (~NetblockChecksum (\r
+                                                        (UINT8 *)IpPayload,\r
+                                                        ((IP4_HEAD *)IpPayload)->HeadLen << 2\r
+                                                        ));\r
 \r
-    case EfiIPsecActionProtect:\r
 \r
-      if (IsOutbound) {\r
-        //\r
-        // For outbound traffic, lookup the sad entry.\r
-        //\r
-        Status = IpSecLookupSadEntry (\r
-                  Private,\r
-                  NicHandle,\r
-                  IpVersion,\r
-                  IpHead,\r
-                  IpPayload,\r
-                  OldLastHead,\r
-                  SpdEntry,\r
-                  &SadEntry\r
-                  );\r
+    }\r
 \r
-        if (SadEntry != NULL) {\r
-          //\r
-          // Process the packet by the found sad entry.\r
-          //\r
-          Status = IpSecProtectOutboundPacket (\r
-                    IpVersion,\r
-                    IpHead,\r
-                    LastHead,\r
-                    OptionsBuffer,\r
-                    OptionsLength,\r
-                    FragmentTable,\r
-                    FragmentCount,\r
-                    SadEntry,\r
-                    RecycleSignal\r
-                    );\r
-\r
-        } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {\r
-          //\r
-          // TODO: if no need return not ready to upper layer, change here.\r
-          //\r
-          Status = EFI_SUCCESS;\r
-        }\r
-      } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {\r
-        //\r
-        // For inbound icmpv6 traffic except ping request, accept the packet\r
-        // although no sad entry associated with protect spd entry.\r
-        //\r
-        IpSecLookupSadEntry (\r
-          Private,\r
-          NicHandle,\r
-          IpVersion,\r
-          IpHead,\r
-          IpPayload,\r
-          OldLastHead,\r
-          SpdEntry,\r
-          &SadEntry\r
-          );\r
-        if (SadEntry == NULL) {\r
-          Status = EFI_SUCCESS;\r
-        }\r
-      }\r
+    //\r
+    // Recalcualte PseudoChecksum\r
+    //\r
+    switch (((IP4_HEAD *)IpPayload)->Protocol) {\r
+    case EFI_IP_PROTO_UDP :\r
+      UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2));\r
+      Checksum  = & UdpHeader->Checksum;\r
+      *Checksum = 0;\r
+      break;\r
 \r
-      goto ON_EXIT;\r
+    case EFI_IP_PROTO_TCP:\r
+      TcpHeader = (TCP_HEAD *) ((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2));\r
+      Checksum  = &TcpHeader->Checksum;\r
+      *Checksum = 0;\r
+      break;\r
 \r
-    case EfiIPsecActionBypass:\r
-      Status = EFI_SUCCESS;\r
-      goto ON_EXIT;\r
+    default:\r
+      break;\r
+      }\r
+    PacketChecksum = NetblockChecksum (\r
+                       (UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2),\r
+                       NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2)\r
+                       );\r
+    PseudoChecksum = NetPseudoHeadChecksum (\r
+                       ((IP4_HEAD *)IpPayload)->Src,\r
+                       ((IP4_HEAD *)IpPayload)->Dst,\r
+                       ((IP4_HEAD *)IpPayload)->Protocol,\r
+                       0\r
+                       );\r
 \r
-    case EfiIPsecActionDiscard:\r
-      goto ON_EXIT;\r
+      if (Checksum != NULL) {\r
+        *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);\r
+        *Checksum = (UINT16) ~(NetAddChecksum (*Checksum, HTONS((UINT16)(NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2)))));\r
+      }\r
+    }else {\r
+      //\r
+      //  Zero OutIP header use this to indicate the input packet is under\r
+      //  IPsec Tunnel protected.\r
+      //\r
+      ZeroMem (\r
+        IpHead,\r
+        sizeof (EFI_IP6_HEADER)\r
+        );\r
+      CopyMem (\r
+        &((EFI_IP6_HEADER*)IpPayload)->DestinationAddress,\r
+        &SadData->TunnelDestAddress.v6,\r
+        sizeof (EFI_IPv6_ADDRESS)\r
+        );\r
 \r
-    default:\r
       //\r
-      // Discard the packet if no spd entry match.\r
+      // Get the Extension Header and Header length.\r
       //\r
-      break;\r
+      IpSecIsIp6ExtsValid (\r
+        &((EFI_IP6_HEADER *)IpPayload)->NextHeader,\r
+        IpPayload + sizeof (EFI_IP6_HEADER),\r
+        ((EFI_IP6_HEADER *)IpPayload)->PayloadLength,\r
+        &LastHead,\r
+        &OptionLen\r
+        );\r
+\r
+      //\r
+      // Recalcualte PseudoChecksum\r
+      //\r
+      switch (*LastHead) {\r
+      case EFI_IP_PROTO_UDP:\r
+        UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);\r
+        Checksum  = &UdpHeader->Checksum;\r
+        *Checksum = 0;\r
+        break;\r
+\r
+      case EFI_IP_PROTO_TCP:\r
+        TcpHeader = (TCP_HEAD *)(IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);\r
+        Checksum  = &TcpHeader->Checksum;\r
+        *Checksum = 0;\r
+        break;\r
+\r
+      case IP6_ICMP:\r
+        Icmp6Head  = (IP6_ICMP_HEAD *) (IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);\r
+        Checksum   = &Icmp6Head->Checksum;\r
+        *Checksum  = 0;\r
+        break;\r
+      }\r
+      PacketChecksum = NetblockChecksum (\r
+                         IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen,\r
+                         NTOHS(((EFI_IP6_HEADER *)IpPayload)->PayloadLength) - OptionLen\r
+                         );\r
+      PseudoChecksum = NetIp6PseudoHeadChecksum (\r
+                         &((EFI_IP6_HEADER *)IpPayload)->SourceAddress,\r
+                         &((EFI_IP6_HEADER *)IpPayload)->DestinationAddress,\r
+                         *LastHead,\r
+                         0\r
+                         );\r
+\r
+    if (Checksum != NULL) {\r
+      *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);\r
+      *Checksum = (UINT16) ~(NetAddChecksum (\r
+                               *Checksum,\r
+                               HTONS ((UINT16)((NTOHS (((EFI_IP6_HEADER *)(IpPayload))->PayloadLength)) - OptionLen))\r
+                               ));\r
     }\r
   }\r
-\r
-ON_EXIT:\r
-  return Status;\r
 }\r
 \r
+/**\r
+  The actual entry to create inner header for tunnel mode inbound traffic.\r
+\r
+  This function is the subfunction of IpSecEspOutboundPacket(). It create\r
+  the sending packet by encrypting its payload and inserting ESP header in the orginal\r
+  IP header, then return the IpHeader and IPsec protected Fragmentable.\r
+\r
+  @param[in, out] IpHead             Points to IP header containing the orginal IP header\r
+                                     to be processed on input, and inserted ESP header\r
+                                     on return.\r
+  @param[in]      IpVersion          The version of IP.\r
+  @param[in]      SadData            The related SAD data.\r
+  @param[in, out] LastHead           The Last Header in IP header.\r
+  @param[in]      OptionsBuffer      Pointer to the options buffer.\r
+  @param[in]      OptionsLength      Length of the options buffer.\r
+  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by\r
+                                     IPsec on input, and with IPsec protected\r
+                                     on return.\r
+  @param[in]      FragmentCount      The number of fragments.\r
+\r
+  @retval EFI_SUCCESS              The operation was successful.\r
+  @retval EFI_OUT_OF_RESOURCES     The required system resources can't be allocated.\r
+\r
+**/\r
+UINT8 *\r
+IpSecTunnelOutboundPacket (\r
+  IN OUT UINT8                   *IpHead,\r
+  IN     UINT8                   IpVersion,\r
+  IN     IPSEC_SAD_DATA          *SadData,\r
+  IN OUT UINT8                   *LastHead,\r
+  IN     VOID                    **OptionsBuffer,\r
+  IN     UINT32                  *OptionsLength,\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
+  IN     UINT32                  *FragmentCount\r
+  )\r
+{\r
+  UINT8         *InnerHead;\r
+  NET_BUF       *Packet;\r
+  UINT16        PacketChecksum;\r
+  UINT16        *Checksum;\r
+  UINT16        PseudoChecksum;\r
+  IP6_ICMP_HEAD *IcmpHead;\r
+\r
+  Checksum = NULL;\r
+  if (OptionsLength == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  if (IpVersion == IP_VERSION_4) {\r
+    InnerHead = AllocateZeroPool (sizeof (IP4_HEAD) + *OptionsLength);\r
+    ASSERT (InnerHead != NULL);\r
+    CopyMem (\r
+      InnerHead,\r
+      IpHead,\r
+      sizeof (IP4_HEAD)\r
+      );\r
+    CopyMem (\r
+      InnerHead + sizeof (IP4_HEAD),\r
+      *OptionsBuffer,\r
+      *OptionsLength\r
+      );\r
+  } else {\r
+    InnerHead = AllocateZeroPool (sizeof (EFI_IP6_HEADER) + *OptionsLength);\r
+    ASSERT (InnerHead != NULL);\r
+    CopyMem (\r
+      InnerHead,\r
+      IpHead,\r
+      sizeof (EFI_IP6_HEADER)\r
+      );\r
+    CopyMem (\r
+      InnerHead + sizeof (EFI_IP6_HEADER),\r
+      *OptionsBuffer,\r
+      *OptionsLength\r
+      );\r
+  }\r
+  if (OptionsBuffer != NULL) {\r
+    if (*OptionsLength != 0) {\r
+\r
+      *OptionsBuffer = NULL;\r
+      *OptionsLength = 0;\r
+    }\r
+  }\r
+\r
+  //\r
+  // 2. Reassamlbe Fragment into Packet\r
+  //\r
+  Packet = NetbufFromExt (\r
+             (NET_FRAGMENT *)(*FragmentTable),\r
+             *FragmentCount,\r
+             0,\r
+             0,\r
+             IpSecOnRecyclePacket,\r
+             NULL\r
+             );\r
+  ASSERT (Packet != NULL);\r
+  //\r
+  // 3. Check the Last Header, if it is TCP, UDP or ICMP recalcualate its pesudo\r
+  //    CheckSum.\r
+  //\r
+  switch (*LastHead) {\r
+  case EFI_IP_PROTO_UDP:\r
+    Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, 0);\r
+    ASSERT (Packet->Udp != NULL);\r
+    Checksum = &Packet->Udp->Checksum;\r
+    *Checksum = 0;\r
+    break;\r
+\r
+  case EFI_IP_PROTO_TCP:\r
+    Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, 0);\r
+    ASSERT (Packet->Tcp != NULL);\r
+    Checksum = &Packet->Tcp->Checksum;\r
+    *Checksum = 0;\r
+    break;\r
+\r
+  case IP6_ICMP:\r
+    IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+    ASSERT (IcmpHead != NULL);\r
+    Checksum = &IcmpHead->Checksum;\r
+    *Checksum = 0;\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  PacketChecksum = NetbufChecksum (Packet);\r
+\r
+  if (IpVersion == IP_VERSION_4) {\r
+    //\r
+    // Replace the source address of Inner Header.\r
+    //\r
+    CopyMem (\r
+      &((IP4_HEAD *)InnerHead)->Src,\r
+      &SadData->SpdSelector->LocalAddress[0].Address.v4,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+\r
+    PacketChecksum = NetbufChecksum (Packet);\r
+    PseudoChecksum = NetPseudoHeadChecksum (\r
+                       ((IP4_HEAD *)InnerHead)->Src,\r
+                       ((IP4_HEAD *)InnerHead)->Dst,\r
+                       *LastHead,\r
+                       0\r
+                       );\r
+\r
+   } else {\r
+     //\r
+     // Replace the source address of Inner Header.\r
+     //\r
+     CopyMem (\r
+       &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,\r
+       &(SadData->SpdSelector->LocalAddress[0].Address.v6),\r
+       sizeof (EFI_IPv6_ADDRESS)\r
+       );\r
+     PacketChecksum = NetbufChecksum (Packet);\r
+     PseudoChecksum = NetIp6PseudoHeadChecksum (\r
+                      &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,\r
+                      &((EFI_IP6_HEADER *)InnerHead)->DestinationAddress,\r
+                      *LastHead,\r
+                      0\r
+                      );\r
+\r
+   }\r
+   if (Checksum != NULL) {\r
+     *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);\r
+     *Checksum = (UINT16) ~(NetAddChecksum ((UINT16)*Checksum, HTONS ((UINT16) Packet->TotalSize)));\r
+   }\r
+\r
+  if (Packet != NULL) {\r
+    NetbufFree (Packet);\r
+  }\r
+  return InnerHead;\r
+}\r
+\r
+/**\r
+  The actual entry to relative function processes the inbound traffic of ESP header.\r
+\r
+  This function is the subfunction of IpSecProtectInboundPacket(). It checks the\r
+  received packet security property and trim the ESP header and then returns without\r
+  an IPsec protected IP Header and FramgmentTable.\r
+\r
+  @param[in]      IpVersion          The version of IP.\r
+  @param[in, out] IpHead             Points to the IP header containing the ESP header\r
+                                     to be trimed on input, and without ESP header\r
+                                     on return.\r
+  @param[out]     LastHead           The Last Header in IP header on return.\r
+  @param[in, out] OptionsBuffer      Pointer to the options buffer.\r
+  @param[in, out] OptionsLength      Length of the options buffer.\r
+  @param[in, out] FragmentTable      Pointer to a list of fragments in the form of IPsec\r
+                                     protected on input, and without IPsec protected\r
+                                     on return.\r
+  @param[in, out] FragmentCount      The number of fragments.\r
+  @param[out]     SpdSelector        Pointer to contain the address of SPD selector on return.\r
+  @param[out]     RecycleEvent       The event for recycling of resources.\r
+\r
+  @retval EFI_SUCCESS              The operation was successful.\r
+  @retval EFI_ACCESS_DENIED        One or more following conditions is TRUE:\r
+                                   - ESP header was not found or mal-format.\r
+                                   - The related SAD entry was not found.\r
+                                   - The related SAD entry does not support the ESP protocol.\r
+  @retval EFI_OUT_OF_RESOURCES     The required system resource can't be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspInboundPacket (\r
+  IN     UINT8                       IpVersion,\r
+  IN OUT VOID                        *IpHead,\r
+     OUT UINT8                       *LastHead,\r
+  IN OUT VOID                        **OptionsBuffer,\r
+  IN OUT UINT32                      *OptionsLength,\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN OUT UINT32                      *FragmentCount,\r
+     OUT EFI_IPSEC_SPD_SELECTOR      **SpdSelector,\r
+     OUT EFI_EVENT                   *RecycleEvent\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  NET_BUF               *Payload;\r
+  UINTN                 EspSize;\r
+  UINTN                 IvSize;\r
+  UINTN                 BlockSize;\r
+  UINTN                 MiscSize;\r
+  UINTN                 PlainPayloadSize;\r
+  UINTN                 PaddingSize;\r
+  UINTN                 IcvSize;\r
+  UINT8                 *ProcessBuffer;\r
+  EFI_ESP_HEADER        *EspHeader;\r
+  EFI_ESP_TAIL          *EspTail;\r
+  EFI_IPSEC_SA_ID       *SaId;\r
+  IPSEC_SAD_DATA        *SadData;\r
+  IPSEC_SAD_ENTRY       *SadEntry;\r
+  IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
+  UINT8                 NextHeader;\r
+  UINT16                IpSecHeadSize;\r
+  UINT8                 *InnerHead;\r
+\r
+  Status            = EFI_SUCCESS;\r
+  Payload           = NULL;\r
+  ProcessBuffer     = NULL;\r
+  RecycleContext    = NULL;\r
+  *RecycleEvent     = NULL;\r
+  PlainPayloadSize  = 0;\r
+  NextHeader        = 0;\r
+\r
+  //\r
+  // Build netbuf from fragment table first.\r
+  //\r
+  Payload = NetbufFromExt (\r
+              (NET_FRAGMENT *) *FragmentTable,\r
+              *FragmentCount,\r
+              0,\r
+              sizeof (EFI_ESP_HEADER),\r
+              IpSecOnRecyclePacket,\r
+              NULL\r
+              );\r
+  if (Payload == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Get the esp size and esp header from netbuf.\r
+  //\r
+  EspSize   = Payload->TotalSize;\r
+  EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);\r
+\r
+  if (EspHeader == NULL) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Parse destination address from ip header and found the related SAD Entry.\r
+  //\r
+  SadEntry = IpSecFoundSadFromInboundPacket (\r
+               IpHead,\r
+               IpVersion,\r
+               NTOHL (EspHeader->Spi)\r
+               );\r
+\r
+  if (SadEntry == NULL) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  SaId    = SadEntry->Id;\r
+  SadData = SadEntry->Data;\r
+\r
+  //\r
+  // Only support esp protocol currently.\r
+  //\r
+  if (SaId->Proto != EfiIPsecESP) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (!SadData->ManualSet) {\r
+    //\r
+    // TODO: Check SA lifetime and sequence number\r
+    //\r
+  }\r
+\r
+  //\r
+  // Allocate buffer for decryption and authentication.\r
+  //\r
+  ProcessBuffer = AllocateZeroPool (EspSize);\r
+  if (ProcessBuffer == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer);\r
+\r
+  //\r
+  // Get the IcvSize for authentication and BlockSize/IvSize for Decryption.\r
+  //\r
+  IcvSize   = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);\r
+  IvSize    = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
+  BlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
+\r
+  //\r
+  // Make sure the ESP packet is not mal-formt.\r
+  // 1. Check whether the Espsize is larger than ESP header + IvSize + EspTail + IcvSize.\r
+  // 2. Check whether the left payload size is multiple of IvSize.\r
+  //\r
+  MiscSize = sizeof (EFI_ESP_HEADER) + IvSize + IcvSize;\r
+  if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL))) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+  if ((EspSize - MiscSize) % BlockSize != 0) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Authenticate the ESP packet.\r
+  //\r
+  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+    Status = IpSecEspAuthVerifyPayload (\r
+               ProcessBuffer,\r
+               EspSize,\r
+               SadEntry,\r
+               IcvSize\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+  //\r
+  // Decrypt the payload by the SAD entry if it has decrypt key.\r
+  //\r
+  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+    Status = IpSecCryptoIoDecrypt (\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,\r
+               ProcessBuffer + sizeof (EFI_ESP_HEADER),\r
+               ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize,\r
+               EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize,\r
+               ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Parse EspTail and compute the plain payload size.\r
+  //\r
+  EspTail           = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));\r
+  PaddingSize       = EspTail->PaddingLength;\r
+  NextHeader        = EspTail->NextHeader;\r
+\r
+  if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL) + PaddingSize)) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+  PlainPayloadSize  = EspSize - MiscSize - sizeof (EFI_ESP_TAIL) - PaddingSize;\r
+\r
+  //\r
+  // TODO: handle anti-replay window\r
+  //\r
+  //\r
+  // Decryption and authentication with esp has been done, so it's time to\r
+  // reload the new packet, create recycle event and fixup ip header.\r
+  //\r
+  RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));\r
+  if (RecycleContext == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  IpSecRecycleCallback,\r
+                  RecycleContext,\r
+                  RecycleEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // The caller will take responsible to handle the original fragment table\r
+  //\r
+  *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));\r
+  if (*FragmentTable == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  RecycleContext->PayloadBuffer       = ProcessBuffer;\r
+  RecycleContext->FragmentTable       = *FragmentTable;\r
+\r
+  //\r
+  // If Tunnel, recalculate upper-layyer PesudoCheckSum and trim the out\r
+  //\r
+  if (SadData->Mode == EfiIPsecTunnel) {\r
+    InnerHead = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;\r
+    IpSecTunnelInboundPacket (\r
+      IpHead,\r
+      InnerHead,\r
+      IpVersion,\r
+      SadData,\r
+      LastHead\r
+      );\r
+\r
+    if (IpVersion == IP_VERSION_4) {\r
+      (*FragmentTable)[0].FragmentBuffer  = InnerHead ;\r
+      (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;\r
+\r
+    }else {\r
+      (*FragmentTable)[0].FragmentBuffer  = InnerHead;\r
+      (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;\r
+    }\r
+  } else {\r
+    (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;\r
+    (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;\r
+  }\r
+\r
+  *FragmentCount                      = 1;\r
+\r
+  //\r
+  // Update the total length field in ip header since processed by esp.\r
+  //\r
+  if (!SadData->Mode == EfiIPsecTunnel) {\r
+    if (IpVersion == IP_VERSION_4) {\r
+      ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + PlainPayloadSize));\r
+    } else {\r
+      IpSecHeadSize                              = IpSecGetPlainExtHeadSize (IpHead, LastHead);\r
+      ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize));\r
+    }\r
+    //\r
+    // Update the next layer field in ip header since esp header inserted.\r
+    //\r
+    *LastHead = NextHeader;\r
+  }\r
+\r
+\r
+  //\r
+  // Update the SPD association of the SAD entry.\r
+  //\r
+  *SpdSelector = SadData->SpdSelector;\r
+\r
+ON_EXIT:\r
+  if (Payload != NULL) {\r
+    NetbufFree (Payload);\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    if (ProcessBuffer != NULL) {\r
+      FreePool (ProcessBuffer);\r
+    }\r
+\r
+    if (RecycleContext != NULL) {\r
+      FreePool (RecycleContext);\r
+    }\r
+\r
+    if (*RecycleEvent != NULL) {\r
+      gBS->CloseEvent (*RecycleEvent);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The actual entry to the relative function processes the output traffic using the ESP protocol.\r
+\r
+  This function is the subfunction of IpSecProtectOutboundPacket(). It protected\r
+  the sending packet by encrypting its payload and inserting ESP header in the orginal\r
+  IP header, then return the IpHeader and IPsec protected Fragmentable.\r
+\r
+  @param[in]      IpVersion          The version of IP.\r
+  @param[in, out] IpHead             Points to IP header containing the orginal IP header\r
+                                     to be processed on input, and inserted ESP header\r
+                                     on return.\r
+  @param[in, out] LastHead           The Last Header in IP header.\r
+  @param[in, out] OptionsBuffer      Pointer to the options buffer.\r
+  @param[in, out] OptionsLength      Length of the options buffer.\r
+  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by\r
+                                     IPsec on input, and with IPsec protected\r
+                                     on return.\r
+  @param[in, out] FragmentCount      The number of fragments.\r
+  @param[in]      SadEntry           The related SAD entry.\r
+  @param[out]     RecycleEvent       The event for recycling of resources.\r
+\r
+  @retval EFI_SUCCESS              The operation was successful.\r
+  @retval EFI_OUT_OF_RESOURCES     The required system resources can't be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspOutboundPacket (\r
+  IN UINT8                           IpVersion,\r
+  IN OUT VOID                        *IpHead,\r
+  IN OUT UINT8                       *LastHead,\r
+  IN OUT VOID                        **OptionsBuffer,\r
+  IN OUT UINT32                      *OptionsLength,\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN OUT UINT32                      *FragmentCount,\r
+  IN     IPSEC_SAD_ENTRY             *SadEntry,\r
+     OUT EFI_EVENT                   *RecycleEvent\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UINTN                 Index;\r
+  EFI_IPSEC_SA_ID       *SaId;\r
+  IPSEC_SAD_DATA        *SadData;\r
+  IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
+  UINT8                 *ProcessBuffer;\r
+  UINTN                 BytesCopied;\r
+  INTN                  EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4\r
+  UINTN                 EspSize;         // Total size of esp wrapped ip payload\r
+  UINTN                 IvSize;          // Size of IV, optional, might be 0\r
+  UINTN                 PlainPayloadSize;// Original IP payload size\r
+  UINTN                 PaddingSize;     // Size of padding\r
+  UINTN                 EncryptSize;     // Size of data to be encrypted, start after IV and\r
+                                         // stop before ICV\r
+  UINTN                 IcvSize;         // Size of ICV, optional, might be 0\r
+  UINT8                 *RestOfPayload;  // Start of Payload after IV\r
+  UINT8                 *Padding;        // Start address of padding\r
+  EFI_ESP_HEADER        *EspHeader;      // Start address of ESP frame\r
+  EFI_ESP_TAIL          *EspTail;        // Address behind padding\r
+  UINT8                 *InnerHead;\r
+  HASH_DATA_FRAGMENT    HashFragment[1];\r
+\r
+  Status          = EFI_ACCESS_DENIED;\r
+  SaId            = SadEntry->Id;\r
+  SadData         = SadEntry->Data;\r
+  ProcessBuffer   = NULL;\r
+  RecycleContext  = NULL;\r
+  *RecycleEvent   = NULL;\r
+  InnerHead       = NULL;\r
+\r
+  if (!SadData->ManualSet &&\r
+      SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL &&\r
+      SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL\r
+      ) {\r
+    //\r
+    // Invalid manual SAD entry configuration.\r
+    //\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Create OutHeader according to Inner Header\r
+  //\r
+  if (SadData->Mode == EfiIPsecTunnel) {\r
+    InnerHead = IpSecTunnelOutboundPacket (\r
+                  IpHead,\r
+                  IpVersion,\r
+                  SadData,\r
+                  LastHead,\r
+                  OptionsBuffer,\r
+                  OptionsLength,\r
+                  FragmentTable,\r
+                  FragmentCount\r
+                  );\r
+\r
+    if (InnerHead == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+  }\r
+\r
+  //\r
+  // Calculate enctrypt block size, need iv by default and 4 bytes alignment.\r
+  //\r
+  EncryptBlockSize  = 4;\r
+\r
+  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+    EncryptBlockSize  = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
+\r
+    if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Calculate the plain payload size accroding to the fragment table.\r
+  //\r
+  PlainPayloadSize = 0;\r
+  for (Index = 0; Index < *FragmentCount; Index++) {\r
+    PlainPayloadSize += (*FragmentTable)[Index].FragmentLength;\r
+  }\r
+\r
+  //\r
+  // Add IPHeader size for Tunnel Mode\r
+  //\r
+  if (SadData->Mode == EfiIPsecTunnel) {\r
+    if (IpVersion == IP_VERSION_4) {\r
+      PlainPayloadSize += sizeof (IP4_HEAD);\r
+    } else {\r
+      PlainPayloadSize += sizeof (EFI_IP6_HEADER);\r
+    }\r
+    //\r
+    // OPtions should be encryption into it\r
+    //\r
+    PlainPayloadSize += *OptionsLength;\r
+  }\r
+\r
+\r
+  //\r
+  // Calculate icv size, optional by default and 4 bytes alignment.\r
+  //\r
+  IcvSize = 0;\r
+  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+    IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);\r
+    if (IcvSize % 4 != 0) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Calcuate the total size of esp wrapped ip payload.\r
+  //\r
+  IvSize        = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
+  EncryptSize   = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize;\r
+  PaddingSize   = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL);\r
+  EspSize       = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize;\r
+\r
+  ProcessBuffer = AllocateZeroPool (EspSize);\r
+  if (ProcessBuffer == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Calculate esp header and esp tail including header, payload and padding.\r
+  //\r
+  EspHeader     = (EFI_ESP_HEADER *) ProcessBuffer;\r
+  RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize;\r
+  Padding       = RestOfPayload + PlainPayloadSize;\r
+  EspTail       = (EFI_ESP_TAIL *) (Padding + PaddingSize);\r
+\r
+  //\r
+  // Fill the sn and spi fields in esp header.\r
+  //\r
+  EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1);\r
+  //EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber);\r
+  EspHeader->Spi            = HTONL (SaId->Spi);\r
+\r
+  //\r
+  // Copy the rest of payload (after iv) from the original fragment buffer.\r
+  //\r
+  BytesCopied = 0;\r
+\r
+  //\r
+  // For Tunnel Mode\r
+  //\r
+  if (SadData->Mode == EfiIPsecTunnel) {\r
+    if (IpVersion == IP_VERSION_4) {\r
+      //\r
+      // HeadLen, Total Length\r
+      //\r
+      ((IP4_HEAD *)InnerHead)->HeadLen  = (UINT8) ((sizeof (IP4_HEAD) + *OptionsLength) >> 2);\r
+      ((IP4_HEAD *)InnerHead)->TotalLen = HTONS ((UINT16) PlainPayloadSize);\r
+      ((IP4_HEAD *)InnerHead)->Checksum = 0;\r
+      ((IP4_HEAD *)InnerHead)->Checksum = (UINT16) (~NetblockChecksum (\r
+                                                  (UINT8 *)InnerHead,\r
+                                                  sizeof(IP4_HEAD)\r
+                                                  ));\r
+      CopyMem (\r
+        RestOfPayload + BytesCopied,\r
+        InnerHead,\r
+        sizeof (IP4_HEAD) + *OptionsLength\r
+        );\r
+      BytesCopied += sizeof (IP4_HEAD) + *OptionsLength;\r
+\r
+    } else {\r
+    ((EFI_IP6_HEADER *)InnerHead)->PayloadLength = HTONS ((UINT16) (PlainPayloadSize - sizeof (EFI_IP6_HEADER)));\r
+      CopyMem (\r
+        RestOfPayload + BytesCopied,\r
+        InnerHead,\r
+        sizeof (EFI_IP6_HEADER) + *OptionsLength\r
+        );\r
+      BytesCopied += sizeof (EFI_IP6_HEADER) + *OptionsLength;\r
+    }\r
+  }\r
+\r
+  for (Index = 0; Index < *FragmentCount; Index++) {\r
+    CopyMem (\r
+      (RestOfPayload + BytesCopied),\r
+      (*FragmentTable)[Index].FragmentBuffer,\r
+      (*FragmentTable)[Index].FragmentLength\r
+      );\r
+    BytesCopied += (*FragmentTable)[Index].FragmentLength;\r
+  }\r
+  //\r
+  // Fill the padding buffer by natural number sequence.\r
+  //\r
+  for (Index = 0; Index < PaddingSize; Index++) {\r
+    Padding[Index] = (UINT8) (Index + 1);\r
+  }\r
+  //\r
+  // Fill the padding length and next header fields in esp tail.\r
+  //\r
+  EspTail->PaddingLength  = (UINT8) PaddingSize;\r
+  EspTail->NextHeader     = *LastHead;\r
+\r
+  //\r
+  // Fill the next header for Tunnel mode.\r
+  //\r
+  if (SadData->Mode == EfiIPsecTunnel) {\r
+    if (IpVersion == IP_VERSION_4) {\r
+      EspTail->NextHeader = 4;\r
+    } else {\r
+      EspTail->NextHeader = 41;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Generate iv at random by crypt library.\r
+  //\r
+  Status = IpSecGenerateIv (\r
+             (UINT8 *) (EspHeader + 1),\r
+             IvSize\r
+             );\r
+\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Encryption the payload (after iv) by the SAD entry if has encrypt key.\r
+  //\r
+  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+    Status = IpSecCryptoIoEncrypt (\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,\r
+               (UINT8 *)(EspHeader + 1),\r
+               RestOfPayload,\r
+               EncryptSize,\r
+               RestOfPayload\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Authenticate the esp wrapped buffer by the SAD entry if it has auth key.\r
+  //\r
+  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+\r
+    HashFragment[0].Data     = ProcessBuffer;\r
+    HashFragment[0].DataSize = EspSize - IcvSize;\r
+    Status = IpSecCryptoIoHmac (\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId,\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength,\r
+               HashFragment,\r
+               1,\r
+               ProcessBuffer + EspSize - IcvSize,\r
+               IcvSize\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Encryption and authentication with esp has been done, so it's time to\r
+  // reload the new packet, create recycle event and fixup ip header.\r
+  //\r
+  RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));\r
+  if (RecycleContext == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  IpSecRecycleCallback,\r
+                  RecycleContext,\r
+                  RecycleEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Caller take responsible to handle the original fragment table.\r
+  //\r
+  *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));\r
+  if (*FragmentTable == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  RecycleContext->FragmentTable       = *FragmentTable;\r
+  RecycleContext->PayloadBuffer       = ProcessBuffer;\r
+  (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer;\r
+  (*FragmentTable)[0].FragmentLength  = (UINT32) EspSize;\r
+  *FragmentCount                      = 1;\r
+\r
+  //\r
+  // Update the total length field in ip header since processed by esp.\r
+  //\r
+  if (IpVersion == IP_VERSION_4) {\r
+    ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + EspSize));\r
+  } else {\r
+    ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);\r
+  }\r
+\r
+  //\r
+  // If tunnel mode, it should change the outer Ip header with tunnel source address\r
+  // and destination tunnel address.\r
+  //\r
+  if (SadData->Mode == EfiIPsecTunnel) {\r
+    if (IpVersion == IP_VERSION_4) {\r
+      CopyMem (\r
+        &((IP4_HEAD *) IpHead)->Src,\r
+        &SadData->TunnelSourceAddress.v4,\r
+        sizeof (EFI_IPv4_ADDRESS)\r
+        );\r
+      CopyMem (\r
+        &((IP4_HEAD *) IpHead)->Dst,\r
+        &SadData->TunnelDestAddress.v4,\r
+        sizeof (EFI_IPv4_ADDRESS)\r
+        );\r
+    } else {\r
+      CopyMem (\r
+        &((EFI_IP6_HEADER *) IpHead)->SourceAddress,\r
+        &SadData->TunnelSourceAddress.v6,\r
+        sizeof (EFI_IPv6_ADDRESS)\r
+        );\r
+      CopyMem (\r
+        &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,\r
+        &SadData->TunnelDestAddress.v6,\r
+        sizeof (EFI_IPv6_ADDRESS)\r
+        );\r
+    }\r
+  }\r
+\r
+  //\r
+  // Update the next layer field in ip header since esp header inserted.\r
+  //\r
+  *LastHead = IPSEC_ESP_PROTOCOL;\r
+\r
+  //\r
+  // Increase the sn number in SAD entry according to rfc4303.\r
+  //\r
+  SadData->SequenceNumber++;\r
+\r
+ON_EXIT:\r
+  if (EFI_ERROR (Status)) {\r
+    if (ProcessBuffer != NULL) {\r
+      FreePool (ProcessBuffer);\r
+    }\r
+\r
+    if (RecycleContext != NULL) {\r
+      FreePool (RecycleContext);\r
+    }\r
+\r
+    if (*RecycleEvent != NULL) {\r
+      gBS->CloseEvent (*RecycleEvent);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function processes the inbound traffic with IPsec.\r
+\r
+  It checks the received packet security property, trims the ESP/AH header, and then\r
+  returns without an IPsec protected IP Header and FragmentTable.\r
+\r
+  @param[in]      IpVersion          The version of IP.\r
+  @param[in, out] IpHead             Points to IP header containing the ESP/AH header\r
+                                     to be trimed on input, and without ESP/AH header\r
+                                     on return.\r
+  @param[in, out] LastHead           The Last Header in IP header on return.\r
+  @param[in, out] OptionsBuffer      Pointer to the options buffer.\r
+  @param[in, out] OptionsLength      Length of the options buffer.\r
+  @param[in, out] FragmentTable      Pointer to a list of fragments in form of IPsec\r
+                                     protected on input, and without IPsec protected\r
+                                     on return.\r
+  @param[in, out] FragmentCount      The number of fragments.\r
+  @param[out]     SpdEntry           Pointer to contain the address of SPD entry on return.\r
+  @param[out]     RecycleEvent       The event for recycling of resources.\r
+\r
+  @retval EFI_SUCCESS              The operation was successful.\r
+  @retval EFI_UNSUPPORTED          The IPSEC protocol is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecProtectInboundPacket (\r
+  IN     UINT8                       IpVersion,\r
+  IN OUT VOID                        *IpHead,\r
+  IN OUT UINT8                       *LastHead,\r
+  IN OUT VOID                        **OptionsBuffer,\r
+  IN OUT UINT32                      *OptionsLength,\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN OUT UINT32                      *FragmentCount,\r
+     OUT EFI_IPSEC_SPD_SELECTOR      **SpdEntry,\r
+     OUT EFI_EVENT                   *RecycleEvent\r
+  )\r
+{\r
+  if (*LastHead == IPSEC_ESP_PROTOCOL) {\r
+    //\r
+    // Process the esp ipsec header of the inbound traffic.\r
+    //\r
+    return IpSecEspInboundPacket (\r
+             IpVersion,\r
+             IpHead,\r
+             LastHead,\r
+             OptionsBuffer,\r
+             OptionsLength,\r
+             FragmentTable,\r
+             FragmentCount,\r
+             SpdEntry,\r
+             RecycleEvent\r
+             );\r
+  }\r
+  //\r
+  // The other protocols are not supported.\r
+  //\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  This fucntion processes the output traffic with IPsec.\r
+\r
+  It protected the sending packet by encrypting it payload and inserting ESP/AH header\r
+  in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable.\r
+\r
+  @param[in]      IpVersion          The version of IP.\r
+  @param[in, out] IpHead             Point to IP header containing the orginal IP header\r
+                                     to be processed on input, and inserted ESP/AH header\r
+                                     on return.\r
+  @param[in, out] LastHead           The Last Header in IP header.\r
+  @param[in, out] OptionsBuffer      Pointer to the options buffer.\r
+  @param[in, out] OptionsLength      Length of the options buffer.\r
+  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by\r
+                                     IPsec on input, and with IPsec protected\r
+                                     on return.\r
+  @param[in, out] FragmentCount      Number of fragments.\r
+  @param[in]      SadEntry           Related SAD entry.\r
+  @param[out]     RecycleEvent       Event for recycling of resources.\r
+\r
+  @retval EFI_SUCCESS              The operation is successful.\r
+  @retval EFI_UNSUPPORTED          If the IPSEC protocol is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecProtectOutboundPacket (\r
+  IN     UINT8                       IpVersion,\r
+  IN OUT VOID                        *IpHead,\r
+  IN OUT UINT8                       *LastHead,\r
+  IN OUT VOID                        **OptionsBuffer,\r
+  IN OUT UINT32                      *OptionsLength,\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN OUT UINT32                      *FragmentCount,\r
+  IN     IPSEC_SAD_ENTRY             *SadEntry,\r
+     OUT EFI_EVENT                   *RecycleEvent\r
+  )\r
+{\r
+  if (SadEntry->Id->Proto == EfiIPsecESP) {\r
+    //\r
+    // Process the esp ipsec header of the outbound traffic.\r
+    //\r
+    return IpSecEspOutboundPacket (\r
+             IpVersion,\r
+             IpHead,\r
+             LastHead,\r
+             OptionsBuffer,\r
+             OptionsLength,\r
+             FragmentTable,\r
+             FragmentCount,\r
+             SadEntry,\r
+             RecycleEvent\r
+             );\r
+  }\r
+  //\r
+  // The other protocols are not supported.\r
+  //\r
+  return EFI_UNSUPPORTED;\r
+}\r