]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Input.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Ip4Dxe / Ip4Input.c
index 24d2f29d383de45239df4c5028632fd9a23a1560..24c5846588031b0e0a43f7d13708973e98a63632 100644 (file)
@@ -1,14 +1,10 @@
 /** @file\r
   IP4 input process.\r
-  \r
-Copyright (c) 2005 - 2010, Intel Corporation.<BR>\r
-All rights reserved. This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
 \r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>\r
+(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>\r
+\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
@@ -292,7 +288,7 @@ Ip4Reassemble (
   // check whether THIS.Start < PREV.End for overlap. If two fragments\r
   // overlaps, trim the overlapped part off THIS fragment.\r
   //\r
-  if ((Prev = Cur->ForwardLink) != Head) {\r
+  if ((Prev = Cur->BackLink) != Head) {\r
     Fragment  = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);\r
     Node      = IP4_GET_CLIP_INFO (Fragment);\r
 \r
@@ -419,7 +415,15 @@ Ip4Reassemble (
     }\r
 \r
     NewPacket->Ip.Ip4 = Assemble->Head;\r
-    CopyMem (IP4_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (*IP4_GET_CLIP_INFO (NewPacket)));\r
+\r
+    ASSERT (Assemble->Info != NULL);\r
+\r
+    CopyMem (\r
+      IP4_GET_CLIP_INFO (NewPacket),\r
+      Assemble->Info,\r
+      sizeof (*IP4_GET_CLIP_INFO (NewPacket))\r
+      );\r
+\r
     return NewPacket;\r
   }\r
 \r
@@ -431,8 +435,8 @@ DROP:
 }\r
 \r
 /**\r
-  The callback function for the net buffer which wraps the packet processed by \r
-  IPsec. It releases the wrap packet and also signals IPsec to free the resources. \r
+  The callback function for the net buffer which wraps the packet processed by\r
+  IPsec. It releases the wrap packet and also signals IPsec to free the resources.\r
 \r
   @param[in]  Arg       The wrap context\r
 \r
@@ -459,85 +463,80 @@ Ip4IpSecFree (
 }\r
 \r
 /**\r
-  The work function to locate IPsec protocol to process the inbound or \r
+  The work function to locate IPsec protocol to process the inbound or\r
   outbound IP packets. The process routine handls the packet with following\r
-  actions: bypass the packet, discard the packet, or protect the packet.       \r
+  actions: bypass the packet, discard the packet, or protect the packet.\r
 \r
-  @param[in]       IpSb          The IP4 service instance\r
-  @param[in]       Head          The The caller supplied IP4 header.\r
-  @param[in, out]  Netbuf        The IP4 packet to be processed by IPsec\r
-  @param[in]       Options       The caller supplied options\r
-  @param[in]       OptionsLen    The length of the option\r
-  @param[in]       Direction     The directionality in an SPD entry, \r
-                                 EfiIPsecInBound or EfiIPsecOutBound\r
-  @param[in]       Context       The token's wrap\r
+  @param[in]       IpSb          The IP4 service instance.\r
+  @param[in, out]  Head          The The caller supplied IP4 header.\r
+  @param[in, out]  Netbuf        The IP4 packet to be processed by IPsec.\r
+  @param[in, out]  Options       The caller supplied options.\r
+  @param[in, out]  OptionsLen    The length of the option.\r
+  @param[in]       Direction     The directionality in an SPD entry,\r
+                                 EfiIPsecInBound or EfiIPsecOutBound.\r
+  @param[in]       Context       The token's wrap.\r
 \r
   @retval EFI_SUCCESS            The IPsec protocol is not available or disabled.\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
+  @retval EFI_ACCESS_DENIED      The packet was discarded.\r
   @retval EFI_OUT_OF_RESOURCES   There is no suffcient resource to complete the operation.\r
-  @retval EFI_BUFFER_TOO_SMALL   The number of non-empty block is bigger than the \r
+  @retval EFI_BUFFER_TOO_SMALL   The number of non-empty block is bigger than the\r
                                  number of input data blocks when build a fragment table.\r
 \r
 **/\r
 EFI_STATUS\r
 Ip4IpSecProcessPacket (\r
-  IN IP4_SERVICE            *IpSb,\r
-  IN IP4_HEAD               *Head,\r
-  IN OUT NET_BUF            **Netbuf,\r
-  IN UINT8                  *Options,\r
-  IN UINT32                 OptionsLen,\r
-  IN EFI_IPSEC_TRAFFIC_DIR  Direction,\r
-  IN VOID                   *Context\r
+  IN     IP4_SERVICE            *IpSb,\r
+  IN OUT IP4_HEAD               **Head,\r
+  IN OUT NET_BUF                **Netbuf,\r
+  IN OUT UINT8                  **Options,\r
+  IN OUT UINT32                 *OptionsLen,\r
+  IN     EFI_IPSEC_TRAFFIC_DIR  Direction,\r
+  IN     VOID                   *Context\r
   )\r
 {\r
   NET_FRAGMENT              *FragmentTable;\r
+  NET_FRAGMENT              *OriginalFragmentTable;\r
   UINT32                    FragmentCount;\r
+  UINT32                    OriginalFragmentCount;\r
   EFI_EVENT                 RecycleEvent;\r
   NET_BUF                   *Packet;\r
   IP4_TXTOKEN_WRAP          *TxWrap;\r
   IP4_IPSEC_WRAP            *IpSecWrap;\r
   EFI_STATUS                Status;\r
+  IP4_HEAD                  ZeroHead;\r
 \r
   Status        = EFI_SUCCESS;\r
+\r
+  if (!mIpSec2Installed) {\r
+    goto ON_EXIT;\r
+  }\r
+  ASSERT (mIpSec != NULL);\r
+\r
   Packet        = *Netbuf;\r
   RecycleEvent  = NULL;\r
   IpSecWrap     = NULL;\r
   FragmentTable = NULL;\r
-  TxWrap        = (IP4_TXTOKEN_WRAP *) Context; \r
+  TxWrap        = (IP4_TXTOKEN_WRAP *) Context;\r
   FragmentCount = Packet->BlockOpNum;\r
-  \r
-  if (mIpSec == NULL) {\r
-    gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &mIpSec);\r
-    if (mIpSec != NULL) {\r
-      //\r
-      // Save the original MTU\r
-      //\r
-      IpSb->OldMaxPacketSize = IpSb->MaxPacketSize; \r
-    }\r
-  }\r
 \r
-  //\r
-  // Check whether the IPsec protocol is available.\r
-  //\r
-  if (mIpSec == NULL) {\r
-    goto ON_EXIT;\r
-  }\r
+  ZeroMem (&ZeroHead, sizeof (IP4_HEAD));\r
+\r
   //\r
   // Check whether the IPsec enable variable is set.\r
   //\r
   if (mIpSec->DisabledFlag) {\r
     //\r
     // If IPsec is disabled, restore the original MTU\r
-    //   \r
+    //\r
     IpSb->MaxPacketSize = IpSb->OldMaxPacketSize;\r
     goto ON_EXIT;\r
   } else {\r
     //\r
-    // If IPsec is enabled, use the MTU which reduce the IPsec header length.  \r
+    // If IPsec is enabled, use the MTU which reduce the IPsec header length.\r
     //\r
-    IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP4_MAX_IPSEC_HEADLEN;   \r
+    IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP4_MAX_IPSEC_HEADLEN;\r
   }\r
 \r
   //\r
@@ -549,9 +548,15 @@ Ip4IpSecProcessPacket (
     Status = EFI_OUT_OF_RESOURCES;\r
     goto ON_EXIT;\r
   }\r
\r
+\r
   Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount);\r
 \r
+  //\r
+  // Record the original FragmentTable and count.\r
+  //\r
+  OriginalFragmentTable = FragmentTable;\r
+  OriginalFragmentCount = FragmentCount;\r
+\r
   if (EFI_ERROR (Status)) {\r
     FreePool (FragmentTable);\r
     goto ON_EXIT;\r
@@ -560,16 +565,16 @@ Ip4IpSecProcessPacket (
   //\r
   // Convert host byte order to network byte order\r
   //\r
-  Ip4NtohHead (Head);\r
-  \r
-  Status = mIpSec->Process (\r
+  Ip4NtohHead (*Head);\r
+\r
+  Status = mIpSec->ProcessExt (\r
                      mIpSec,\r
                      IpSb->Controller,\r
                      IP_VERSION_4,\r
-                     (VOID *) Head,\r
-                     &Head->Protocol,\r
-                     NULL,\r
-                     0,\r
+                     (VOID *) (*Head),\r
+                     &(*Head)->Protocol,\r
+                     (VOID **) Options,\r
+                     OptionsLen,\r
                      (EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable),\r
                      &FragmentCount,\r
                      Direction,\r
@@ -578,14 +583,28 @@ Ip4IpSecProcessPacket (
   //\r
   // Convert back to host byte order\r
   //\r
-  Ip4NtohHead (Head);\r
-  \r
+  Ip4NtohHead (*Head);\r
+\r
   if (EFI_ERROR (Status)) {\r
+    FreePool (OriginalFragmentTable);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (OriginalFragmentTable == FragmentTable && OriginalFragmentCount == FragmentCount) {\r
+    //\r
+    // For ByPass Packet\r
+    //\r
+    FreePool (FragmentTable);\r
     goto ON_EXIT;\r
+  } else {\r
+    //\r
+    // Free the FragmentTable which allocated before calling the IPsec.\r
+    //\r
+    FreePool (OriginalFragmentTable);\r
   }\r
 \r
   if (Direction == EfiIPsecOutBound && TxWrap != NULL) {\r
-  \r
+\r
     TxWrap->IpSecRecycleSignal = RecycleEvent;\r
     TxWrap->Packet             = NetbufFromExt (\r
                                    FragmentTable,\r
@@ -596,40 +615,54 @@ Ip4IpSecProcessPacket (
                                    TxWrap\r
                                    );\r
     if (TxWrap->Packet == NULL) {\r
+      //\r
+      // Recover the TxWrap->Packet, if meet a error, and the caller will free\r
+      // the TxWrap.\r
+      //\r
+      TxWrap->Packet = *Netbuf;\r
       Status = EFI_OUT_OF_RESOURCES;\r
       goto ON_EXIT;\r
     }\r
 \r
+    //\r
+    // Free orginal Netbuf.\r
+    //\r
+    NetIpSecNetbufFree (*Netbuf);\r
     *Netbuf = TxWrap->Packet;\r
-    \r
+\r
   } else {\r
-  \r
+\r
     IpSecWrap = AllocateZeroPool (sizeof (IP4_IPSEC_WRAP));\r
-  \r
+\r
     if (IpSecWrap == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      gBS->SignalEvent (RecycleEvent);\r
       goto ON_EXIT;\r
     }\r
-    \r
+\r
     IpSecWrap->IpSecRecycleSignal = RecycleEvent;\r
     IpSecWrap->Packet             = Packet;\r
     Packet                        = NetbufFromExt (\r
-                                      FragmentTable, \r
-                                      FragmentCount, \r
-                                      IP4_MAX_HEADLEN, \r
-                                      0, \r
-                                      Ip4IpSecFree, \r
+                                      FragmentTable,\r
+                                      FragmentCount,\r
+                                      IP4_MAX_HEADLEN,\r
+                                      0,\r
+                                      Ip4IpSecFree,\r
                                       IpSecWrap\r
                                       );\r
-  \r
+\r
     if (Packet == NULL) {\r
+      Packet = IpSecWrap->Packet;\r
+      gBS->SignalEvent (RecycleEvent);\r
+      FreePool (IpSecWrap);\r
       Status = EFI_OUT_OF_RESOURCES;\r
       goto ON_EXIT;\r
     }\r
 \r
-    if (Direction == EfiIPsecInBound) {\r
-      Ip4PrependHead (Packet, Head, Options, OptionsLen);\r
+    if (Direction == EfiIPsecInBound && 0 != CompareMem (*Head, &ZeroHead, sizeof (IP4_HEAD))) {\r
+      Ip4PrependHead (Packet, *Head, *Options, *OptionsLen);\r
       Ip4NtohHead (Packet->Ip.Ip4);\r
-      NetbufTrim (Packet, (Head->HeadLen << 2), TRUE);\r
+      NetbufTrim (Packet, ((*Head)->HeadLen << 2), TRUE);\r
 \r
       CopyMem (\r
         IP4_GET_CLIP_INFO (Packet),\r
@@ -637,7 +670,6 @@ Ip4IpSecProcessPacket (
         sizeof (IP4_CLIP_INFO)\r
         );\r
     }\r
-\r
     *Netbuf = Packet;\r
   }\r
 \r
@@ -646,63 +678,56 @@ ON_EXIT:
 }\r
 \r
 /**\r
-  The IP4 input routine. It is called by the IP4_INTERFACE when a\r
-  IP4 fragment is received from MNP.\r
+  Pre-process the IPv4 packet. First validates the IPv4 packet, and\r
+  then reassembles packet if it is necessary.\r
 \r
-  @param[in]  Ip4Instance        The IP4 child that request the receive, most like\r
-                                 it is NULL.\r
-  @param[in]  Packet             The IP4 packet received.\r
-  @param[in]  IoStatus           The return status of receive request.\r
-  @param[in]  Flag               The link layer flag for the packet received, such\r
-                                 as multicast.\r
-  @param[in]  Context            The IP4 service instance that own the MNP.\r
+  @param[in]       IpSb            Pointer to IP4_SERVICE.\r
+  @param[in, out]  Packet          Pointer to the Packet to be processed.\r
+  @param[in]       Head            Pointer to the IP4_HEAD.\r
+  @param[in]       Option          Pointer to a buffer which contains the IPv4 option.\r
+  @param[in]       OptionLen       The length of Option in bytes.\r
+  @param[in]       Flag            The link layer flag for the packet received, such\r
+                                   as multicast.\r
+\r
+  @retval     EFI_SEUCCESS               The recieved packet is in well form.\r
+  @retval     EFI_INVAILD_PARAMETER      The recieved packet is malformed.\r
 \r
 **/\r
-VOID\r
-Ip4AccpetFrame (\r
-  IN IP4_PROTOCOL           *Ip4Instance,\r
-  IN NET_BUF                *Packet,\r
-  IN EFI_STATUS             IoStatus,\r
-  IN UINT32                 Flag,\r
-  IN VOID                   *Context\r
+EFI_STATUS\r
+Ip4PreProcessPacket (\r
+  IN     IP4_SERVICE    *IpSb,\r
+  IN OUT NET_BUF        **Packet,\r
+  IN     IP4_HEAD       *Head,\r
+  IN     UINT8          *Option,\r
+  IN     UINT32         OptionLen,\r
+  IN     UINT32         Flag\r
   )\r
 {\r
-  IP4_SERVICE               *IpSb;\r
   IP4_CLIP_INFO             *Info;\r
-  IP4_HEAD                  *Head;\r
   UINT32                    HeadLen;\r
-  UINT32                    OptionLen;\r
   UINT32                    TotalLen;\r
   UINT16                    Checksum;\r
-  EFI_STATUS                Status;\r
-\r
-  IpSb = (IP4_SERVICE *) Context;\r
-\r
-  if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTORY)) {\r
-    goto DROP;\r
-  }\r
 \r
   //\r
-  // Check that the IP4 header is correctly formatted\r
+  // Check if the IP4 header is correctly formatted.\r
   //\r
-  if (Packet->TotalSize < IP4_MIN_HEADLEN) {\r
-    goto RESTART;\r
+  if ((*Packet)->TotalSize < IP4_MIN_HEADLEN) {\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  Head     = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
   HeadLen  = (Head->HeadLen << 2);\r
   TotalLen = NTOHS (Head->TotalLen);\r
 \r
   //\r
   // Mnp may deliver frame trailer sequence up, trim it off.\r
   //\r
-  if (TotalLen < Packet->TotalSize) {\r
-    NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE);\r
+  if (TotalLen < (*Packet)->TotalSize) {\r
+    NetbufTrim (*Packet, (*Packet)->TotalSize - TotalLen, FALSE);\r
   }\r
 \r
   if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) ||\r
-      (TotalLen < HeadLen) || (TotalLen != Packet->TotalSize)) {\r
-    goto RESTART;\r
+      (TotalLen < HeadLen) || (TotalLen != (*Packet)->TotalSize)) {\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   //\r
@@ -711,15 +736,15 @@ Ip4AccpetFrame (
   Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Head, HeadLen));\r
 \r
   if ((Head->Checksum != 0) && (Checksum != 0)) {\r
-    goto RESTART;\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   //\r
   // Convert the IP header to host byte order, then get the per packet info.\r
   //\r
-  Packet->Ip.Ip4  = Ip4NtohHead (Head);\r
+  (*Packet)->Ip.Ip4  = Ip4NtohHead (Head);\r
 \r
-  Info            = IP4_GET_CLIP_INFO (Packet);\r
+  Info            = IP4_GET_CLIP_INFO (*Packet);\r
   Info->LinkFlag  = Flag;\r
   Info->CastType  = Ip4GetHostCast (IpSb, Head->Dst, Head->Src);\r
   Info->Start     = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3;\r
@@ -731,24 +756,23 @@ Ip4AccpetFrame (
   // The packet is destinated to us if the CastType is non-zero.\r
   //\r
   if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) {\r
-    goto RESTART;\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   //\r
   // Validate the options. Don't call the Ip4OptionIsValid if\r
   // there is no option to save some CPU process.\r
   //\r
-  OptionLen = HeadLen - IP4_MIN_HEADLEN;\r
 \r
-  if ((OptionLen > 0) && !Ip4OptionIsValid ((UINT8 *) (Head + 1), OptionLen, TRUE)) {\r
-    goto RESTART;\r
+  if ((OptionLen > 0) && !Ip4OptionIsValid (Option, OptionLen, TRUE)) {\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   //\r
-  // Trim the head off, after this point, the packet is headless.\r
+  // Trim the head off, after this point, the packet is headless,\r
   // and Packet->TotalLen == Info->Length.\r
   //\r
-  NetbufTrim (Packet, HeadLen, TRUE);\r
+  NetbufTrim (*Packet, HeadLen, TRUE);\r
 \r
   //\r
   // Reassemble the packet if this is a fragment. The packet is a\r
@@ -761,47 +785,131 @@ Ip4AccpetFrame (
     // need to send a type 4 destination unreache ICMP message here.\r
     //\r
     if ((Head->Fragment & IP4_HEAD_DF_MASK) != 0) {\r
-      goto RESTART;\r
+      return EFI_INVALID_PARAMETER;\r
     }\r
 \r
     //\r
     // The length of all but the last fragments is in the unit of 8 bytes.\r
     //\r
     if (((Head->Fragment & IP4_HEAD_MF_MASK) != 0) && (Info->Length % 8 != 0)) {\r
-      goto RESTART;\r
+      return EFI_INVALID_PARAMETER;\r
     }\r
 \r
-    Packet = Ip4Reassemble (&IpSb->Assemble, Packet);\r
+    *Packet = Ip4Reassemble (&IpSb->Assemble, *Packet);\r
 \r
     //\r
     // Packet assembly isn't complete, start receive more packet.\r
     //\r
-    if (Packet == NULL) {\r
-      goto RESTART;\r
+    if (*Packet == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
     }\r
   }\r
 \r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  The IP4 input routine. It is called by the IP4_INTERFACE when a\r
+  IP4 fragment is received from MNP.\r
+\r
+  @param[in]  Ip4Instance        The IP4 child that request the receive, most like\r
+                                 it is NULL.\r
+  @param[in]  Packet             The IP4 packet received.\r
+  @param[in]  IoStatus           The return status of receive request.\r
+  @param[in]  Flag               The link layer flag for the packet received, such\r
+                                 as multicast.\r
+  @param[in]  Context            The IP4 service instance that own the MNP.\r
+\r
+**/\r
+VOID\r
+Ip4AccpetFrame (\r
+  IN IP4_PROTOCOL           *Ip4Instance,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN UINT32                 Flag,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP4_SERVICE               *IpSb;\r
+  IP4_HEAD                  *Head;\r
+  EFI_STATUS                Status;\r
+  IP4_HEAD                  ZeroHead;\r
+  UINT8                     *Option;\r
+  UINT32                    OptionLen;\r
+\r
+  IpSb   = (IP4_SERVICE *) Context;\r
+  Option = NULL;\r
+\r
+  if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTROY)) {\r
+    goto DROP;\r
+  }\r
+\r
+  Head      = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+  ASSERT (Head != NULL);\r
+  OptionLen = (Head->HeadLen << 2) - IP4_MIN_HEADLEN;\r
+  if (OptionLen > 0) {\r
+    Option = (UINT8 *) (Head + 1);\r
+  }\r
+\r
+  //\r
+  // Validate packet format and reassemble packet if it is necessary.\r
+  //\r
+  Status = Ip4PreProcessPacket (\r
+             IpSb,\r
+             &Packet,\r
+             Head,\r
+             Option,\r
+             OptionLen,\r
+             Flag\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto RESTART;\r
+  }\r
+\r
   //\r
   // After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer,\r
   // and no need consider any other ahead ext headers.\r
   //\r
   Status = Ip4IpSecProcessPacket (\r
-             IpSb, \r
-             Head, \r
-             &Packet, \r
-             NULL,\r
-             0, \r
+             IpSb,\r
+             &Head,\r
+             &Packet,\r
+             &Option,\r
+             &OptionLen,\r
              EfiIPsecInBound,\r
              NULL\r
              );\r
 \r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     goto RESTART;\r
   }\r
+\r
+  //\r
+  // If the packet is protected by tunnel mode, parse the inner Ip Packet.\r
+  //\r
+  ZeroMem (&ZeroHead, sizeof (IP4_HEAD));\r
+  if (0 == CompareMem (Head, &ZeroHead, sizeof (IP4_HEAD))) {\r
   // Packet may have been changed. Head, HeadLen, TotalLen, and\r
   // info must be reloaded bofore use. The ownership of the packet\r
   // is transfered to the packet process logic.\r
   //\r
+    Head = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+    ASSERT (Head != NULL);\r
+    Status = Ip4PreProcessPacket (\r
+               IpSb,\r
+               &Packet,\r
+               Head,\r
+               Option,\r
+               OptionLen,\r
+               Flag\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto RESTART;\r
+    }\r
+  }\r
+\r
+  ASSERT (Packet != NULL);\r
   Head  = Packet->Ip.Ip4;\r
   IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;\r
 \r
@@ -815,7 +923,7 @@ Ip4AccpetFrame (
     break;\r
 \r
   default:\r
-    Ip4Demultiplex (IpSb, Head, Packet);\r
+    Ip4Demultiplex (IpSb, Head, Packet, Option, OptionLen);\r
   }\r
 \r
   Packet = NULL;\r
@@ -1038,8 +1146,8 @@ Ip4OnRecyclePacket (
   to the upper layer. Upper layer will signal the recycle event in\r
   it when it is done with the packet.\r
 \r
-  @param[in]  IpInstance             The IP4 child to receive the packet\r
-  @param[in]  Packet                 The packet to deliver up.\r
+  @param[in]  IpInstance    The IP4 child to receive the packet.\r
+  @param[in]  Packet        The packet to deliver up.\r
 \r
   @retval Wrap              if warp the packet succeed.\r
   @retval NULL              failed to wrap the packet .\r
@@ -1054,6 +1162,7 @@ Ip4WrapRxData (
   IP4_RXDATA_WRAP           *Wrap;\r
   EFI_IP4_RECEIVE_DATA      *RxData;\r
   EFI_STATUS                Status;\r
+  BOOLEAN                   RawData;\r
 \r
   Wrap = AllocatePool (IP4_RXDATA_WRAP_SIZE (Packet->BlockOpNum));\r
 \r
@@ -1067,7 +1176,7 @@ Ip4WrapRxData (
   Wrap->Packet      = Packet;\r
   RxData            = &Wrap->RxData;\r
 \r
-  ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));\r
+  ZeroMem (RxData, sizeof (EFI_IP4_RECEIVE_DATA));\r
 \r
   Status = gBS->CreateEvent (\r
                   EVT_NOTIFY_SIGNAL,\r
@@ -1084,17 +1193,21 @@ Ip4WrapRxData (
 \r
   ASSERT (Packet->Ip.Ip4 != NULL);\r
 \r
+  ASSERT (IpInstance != NULL);\r
+  RawData = IpInstance->ConfigData.RawData;\r
+\r
   //\r
   // The application expects a network byte order header.\r
   //\r
-  RxData->HeaderLength  = (Packet->Ip.Ip4->HeadLen << 2);\r
-  RxData->Header        = (EFI_IP4_HEADER *) Ip4NtohHead (Packet->Ip.Ip4);\r
-\r
-  RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN;\r
-  RxData->Options       = NULL;\r
+  if (!RawData) {\r
+    RxData->HeaderLength  = (Packet->Ip.Ip4->HeadLen << 2);\r
+    RxData->Header        = (EFI_IP4_HEADER *) Ip4NtohHead (Packet->Ip.Ip4);\r
+    RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN;\r
+    RxData->Options       = NULL;\r
 \r
-  if (RxData->OptionsLength != 0) {\r
-    RxData->Options = (VOID *) (RxData->Header + 1);\r
+    if (RxData->OptionsLength != 0) {\r
+      RxData->Options = (VOID *) (RxData->Header + 1);\r
+    }\r
   }\r
 \r
   RxData->DataLength  = Packet->TotalSize;\r
@@ -1133,6 +1246,7 @@ Ip4InstanceDeliverPacket (
   NET_BUF                   *Packet;\r
   NET_BUF                   *Dup;\r
   UINT8                     *Head;\r
+  UINT32                    HeadLen;\r
 \r
   //\r
   // Deliver a packet if there are both a packet and a receive token.\r
@@ -1158,22 +1272,32 @@ Ip4InstanceDeliverPacket (
       //\r
       // Create a duplicated packet if this packet is shared\r
       //\r
-      Dup = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);\r
+      if (IpInstance->ConfigData.RawData) {\r
+        HeadLen = 0;\r
+      } else {\r
+        HeadLen = IP4_MAX_HEADLEN;\r
+      }\r
+\r
+      Dup = NetbufDuplicate (Packet, NULL, HeadLen);\r
 \r
       if (Dup == NULL) {\r
         return EFI_OUT_OF_RESOURCES;\r
       }\r
 \r
-      //\r
-      // Copy the IP head over. The packet to deliver up is\r
-      // headless. Trim the head off after copy. The IP head\r
-      // may be not continuous before the data.\r
-      //\r
-      Head    = NetbufAllocSpace (Dup, IP4_MAX_HEADLEN, NET_BUF_HEAD);\r
-      Dup->Ip.Ip4 = (IP4_HEAD *) Head;\r
+      if (!IpInstance->ConfigData.RawData) {\r
+        //\r
+        // Copy the IP head over. The packet to deliver up is\r
+        // headless. Trim the head off after copy. The IP head\r
+        // may be not continuous before the data.\r
+        //\r
+        Head = NetbufAllocSpace (Dup, IP4_MAX_HEADLEN, NET_BUF_HEAD);\r
+        ASSERT (Head != NULL);\r
 \r
-      CopyMem (Head, Packet->Ip.Ip4, Packet->Ip.Ip4->HeadLen << 2);\r
-      NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE);\r
+        Dup->Ip.Ip4 = (IP4_HEAD *) Head;\r
+\r
+        CopyMem (Head, Packet->Ip.Ip4, Packet->Ip.Ip4->HeadLen << 2);\r
+        NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE);\r
+      }\r
 \r
       Wrap = Ip4WrapRxData (IpInstance, Dup);\r
 \r
@@ -1211,10 +1335,12 @@ Ip4InstanceDeliverPacket (
   Enqueue a received packet to all the IP children that share\r
   the same interface.\r
 \r
-  @param[in]  IpSb                   The IP4 service instance that receive the packet\r
-  @param[in]  Head                   The header of the received packet\r
-  @param[in]  Packet                 The data of the received packet\r
-  @param[in]  IpIf                   The interface to enqueue the packet to\r
+  @param[in]  IpSb               The IP4 service instance that receive the packet.\r
+  @param[in]  Head               The header of the received packet.\r
+  @param[in]  Packet             The data of the received packet.\r
+  @param[in]  Option             Point to the IP4 packet header options.\r
+  @param[in]  OptionLen          Length of the IP4 packet header options.\r
+  @param[in]  IpIf               The interface to enqueue the packet to.\r
 \r
   @return The number of the IP4 children that accepts the packet\r
 \r
@@ -1224,6 +1350,8 @@ Ip4InterfaceEnquePacket (
   IN IP4_SERVICE            *IpSb,\r
   IN IP4_HEAD               *Head,\r
   IN NET_BUF                *Packet,\r
+  IN UINT8                  *Option,\r
+  IN UINT32                 OptionLen,\r
   IN IP4_INTERFACE          *IpIf\r
   )\r
 {\r
@@ -1289,6 +1417,13 @@ Ip4InterfaceEnquePacket (
     IpInstance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);\r
     NET_CHECK_SIGNATURE (IpInstance, IP4_PROTOCOL_SIGNATURE);\r
 \r
+    //\r
+    // In RawData mode, add IPv4 headers and options back to packet.\r
+    //\r
+    if ((IpInstance->ConfigData.RawData) && (Option != NULL) && (OptionLen != 0)){\r
+      Ip4PrependHead (Packet, Head, Option, OptionLen);\r
+    }\r
+\r
     if (Ip4InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {\r
       Enqueued++;\r
     }\r
@@ -1335,11 +1470,13 @@ Ip4InterfaceDeliverPacket (
   child wants to consume the packet because each IP child needs\r
   its own copy of the packet to make changes.\r
 \r
-  @param[in]  IpSb                   The IP4 service instance that received the packet\r
-  @param[in]  Head                   The header of the received packet\r
-  @param[in]  Packet                 The data of the received packet\r
+  @param[in]  IpSb               The IP4 service instance that received the packet.\r
+  @param[in]  Head               The header of the received packet.\r
+  @param[in]  Packet             The data of the received packet.\r
+  @param[in]  Option             Point to the IP4 packet header options.\r
+  @param[in]  OptionLen          Length of the IP4 packet header options.\r
 \r
-  @retval EFI_NOT_FOUND          No IP child accepts the packet\r
+  @retval EFI_NOT_FOUND          No IP child accepts the packet.\r
   @retval EFI_SUCCESS            The packet is enqueued or delivered to some IP\r
                                  children.\r
 \r
@@ -1348,7 +1485,9 @@ EFI_STATUS
 Ip4Demultiplex (\r
   IN IP4_SERVICE            *IpSb,\r
   IN IP4_HEAD               *Head,\r
-  IN NET_BUF                *Packet\r
+  IN NET_BUF                *Packet,\r
+  IN UINT8                  *Option,\r
+  IN UINT32                 OptionLen\r
   )\r
 {\r
   LIST_ENTRY                *Entry;\r
@@ -1365,7 +1504,14 @@ Ip4Demultiplex (
     IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
 \r
     if (IpIf->Configured) {\r
-      Enqueued += Ip4InterfaceEnquePacket (IpSb, Head, Packet, IpIf);\r
+      Enqueued += Ip4InterfaceEnquePacket (\r
+                    IpSb,\r
+                    Head,\r
+                    Packet,\r
+                    Option,\r
+                    OptionLen,\r
+                    IpIf\r
+                    );\r
     }\r
   }\r
 \r