]> git.proxmox.com Git - mirror_edk2.git/commitdiff
Add NetworkPkg (P.UDK2010.UP3.Network.P1)
authorhhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 1 Nov 2010 06:13:54 +0000 (06:13 +0000)
committerhhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 1 Nov 2010 06:13:54 +0000 (06:13 +0000)
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524

142 files changed:
NetworkPkg/Application/IfConfig6/IfConfig6.c [new file with mode: 0644]
NetworkPkg/Application/IfConfig6/IfConfig6.h [new file with mode: 0644]
NetworkPkg/Application/IfConfig6/IfConfig6.inf [new file with mode: 0644]
NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Delete.c [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Delete.h [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Dump.c [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Dump.h [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/ForEach.c [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/ForEach.h [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Helper.c [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Helper.h [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Indexer.c [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Indexer.h [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/IpSecConfig.c [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/IpSecConfig.h [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/IpSecConfig.inf [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Match.c [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/Match.h [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c [new file with mode: 0644]
NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h [new file with mode: 0644]
NetworkPkg/Application/Ping6/Ia32/Tsc.c [new file with mode: 0644]
NetworkPkg/Application/Ping6/Ipf/Itc.c [new file with mode: 0644]
NetworkPkg/Application/Ping6/Ping6.c [new file with mode: 0644]
NetworkPkg/Application/Ping6/Ping6.h [new file with mode: 0644]
NetworkPkg/Application/Ping6/Ping6.inf [new file with mode: 0644]
NetworkPkg/Application/Ping6/Ping6Strings.uni [new file with mode: 0644]
NetworkPkg/Application/Ping6/X64/Tsc.c [new file with mode: 0644]
NetworkPkg/Application/VConfig/VConfig.c [new file with mode: 0644]
NetworkPkg/Application/VConfig/VConfig.inf [new file with mode: 0644]
NetworkPkg/Application/VConfig/VConfigStrings.uni [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/ComponentName.c [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/Dhcp6Io.c [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/Dhcp6Io.h [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c [new file with mode: 0644]
NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/ComponentName.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Common.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Common.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Config.vfr [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6ConfigNv.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6ConfigNv.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Driver.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Driver.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Dxe.inf [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Icmp.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Icmp.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6If.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6If.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Impl.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Impl.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Input.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Input.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Mld.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Mld.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Nd.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Nd.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6NvData.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Option.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Option.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Output.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Output.h [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Route.c [new file with mode: 0644]
NetworkPkg/Ip6Dxe/Ip6Route.h [new file with mode: 0644]
NetworkPkg/IpSecDxe/ComponentName.c [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecConfigImpl.c [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecConfigImpl.h [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecCryptIo.c [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecCryptIo.h [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecDebug.c [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecDebug.h [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecDriver.c [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecDxe.inf [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecImpl.c [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecImpl.h [new file with mode: 0644]
NetworkPkg/IpSecDxe/IpSecSaEngine.c [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/ComponentName.c [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Option.c [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Option.h [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Support.c [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Support.h [new file with mode: 0644]
NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c [new file with mode: 0644]
NetworkPkg/NetworkPkg.dec [new file with mode: 0644]
NetworkPkg/NetworkPkg.dsc [new file with mode: 0644]
NetworkPkg/TcpDxe/ComponentName.c [new file with mode: 0644]
NetworkPkg/TcpDxe/SockImpl.c [new file with mode: 0644]
NetworkPkg/TcpDxe/SockImpl.h [new file with mode: 0644]
NetworkPkg/TcpDxe/SockInterface.c [new file with mode: 0644]
NetworkPkg/TcpDxe/Socket.h [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpDispatcher.c [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpDriver.c [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpDriver.h [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpDxe.inf [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpFunc.h [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpInput.c [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpIo.c [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpMain.c [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpMain.h [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpMisc.c [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpOption.c [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpOption.h [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpOutput.c [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpProto.h [new file with mode: 0644]
NetworkPkg/TcpDxe/TcpTimer.c [new file with mode: 0644]
NetworkPkg/Udp6Dxe/ComponentName.c [new file with mode: 0644]
NetworkPkg/Udp6Dxe/Udp6Driver.c [new file with mode: 0644]
NetworkPkg/Udp6Dxe/Udp6Driver.h [new file with mode: 0644]
NetworkPkg/Udp6Dxe/Udp6Dxe.inf [new file with mode: 0644]
NetworkPkg/Udp6Dxe/Udp6Impl.c [new file with mode: 0644]
NetworkPkg/Udp6Dxe/Udp6Impl.h [new file with mode: 0644]
NetworkPkg/Udp6Dxe/Udp6Main.c [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/ComponentName.c [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h [new file with mode: 0644]
NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf [new file with mode: 0644]

diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.c b/NetworkPkg/Application/IfConfig6/IfConfig6.c
new file mode 100644 (file)
index 0000000..b2eada6
--- /dev/null
@@ -0,0 +1,1781 @@
+/** @file\r
+  The implementation for Shell application IfConfig6.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include <Library/ShellLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Ip6Config.h>\r
+\r
+#include "IfConfig6.h"\r
+\r
+EFI_HII_HANDLE      mHiiHandle;\r
+\r
+EFI_GUID            mEfiIfConfig6Guid     = EFI_IFCONFIG6_GUID;\r
+SHELL_PARAM_ITEM    mIfConfig6CheckList[] = {\r
+  {\r
+    L"-b",\r
+    TypeFlag\r
+  },\r
+  {\r
+    L"-s",\r
+    TypeMaxValue\r
+  },\r
+  {\r
+    L"-l",\r
+    TypeValue\r
+  },\r
+  {\r
+    L"-r",\r
+    TypeValue\r
+  },\r
+  {\r
+    L"-?",\r
+    TypeFlag\r
+  },\r
+  {\r
+    NULL,\r
+    TypeMax\r
+  },\r
+};\r
+\r
+VAR_CHECK_ITEM  mSetCheckList[] = {\r
+  {\r
+   L"auto",\r
+    0x00000001,\r
+    0x00000001,\r
+    FlagTypeSingle\r
+  },\r
+  {\r
+    L"man",\r
+    0x00000002,\r
+    0x00000001,\r
+    FlagTypeSingle\r
+  },\r
+  {\r
+    L"host",\r
+    0x00000004,\r
+    0x00000002,\r
+    FlagTypeSingle\r
+  },\r
+  {\r
+    L"dad",\r
+    0x00000008,\r
+    0x00000004,\r
+    FlagTypeSingle\r
+  },\r
+  {\r
+    L"gw",\r
+    0x00000010,\r
+    0x00000008,\r
+    FlagTypeSingle\r
+  },\r
+  {\r
+    L"dns",\r
+    0x00000020,\r
+    0x00000010,\r
+    FlagTypeSingle\r
+  },\r
+  {\r
+    L"id",\r
+    0x00000040,\r
+    0x00000020,\r
+    FlagTypeSingle\r
+  },\r
+  {\r
+    NULL,\r
+    0x0,\r
+    0x0,\r
+    FlagTypeSkipUnknown\r
+  },\r
+};\r
+\r
+/**\r
+  Split a string with specified separator and save the substring to a list.\r
+\r
+  @param[in]    String       The pointer of the input string.\r
+  @param[in]    Separator    The specified separator.\r
+\r
+  @return The pointer of headnode of ARG_LIST.\r
+\r
+**/\r
+ARG_LIST *\r
+SplitStrToList (\r
+  IN CONST CHAR16    *String,\r
+  IN CHAR16          Separator\r
+  )\r
+{\r
+  CHAR16      *Str;\r
+  CHAR16      *ArgStr;\r
+  ARG_LIST    *ArgList;\r
+  ARG_LIST    *ArgNode;\r
+\r
+  if (*String == L'\0') {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Copy the CONST string to a local copy.\r
+  //\r
+  Str     = (CHAR16 *) AllocateZeroPool (StrSize (String));\r
+  ASSERT (Str != NULL);\r
+  Str     = StrCpy (Str, String);\r
+  ArgStr  = Str;\r
+\r
+  //\r
+  // init a node for the list head.\r
+  //\r
+  ArgNode = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST));\r
+  ASSERT (ArgNode != NULL);\r
+  ArgList = ArgNode;\r
+\r
+  //\r
+  // Split the local copy and save in the list node.\r
+  //\r
+  while (*Str != L'\0') {\r
+    if (*Str == Separator) {\r
+      *Str          = L'\0';\r
+      ArgNode->Arg  = ArgStr;\r
+      ArgStr        = Str + 1;\r
+      ArgNode->Next = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST));\r
+      ASSERT (ArgNode->Next != NULL);\r
+      ArgNode = ArgNode->Next;\r
+    }\r
+\r
+    Str++;\r
+  }\r
+\r
+  ArgNode->Arg  = ArgStr;\r
+  ArgNode->Next = NULL;\r
+\r
+  return ArgList;\r
+}\r
+\r
+/**\r
+  Check the correctness of input Args with '-s' option.\r
+\r
+  @param[in]    CheckList    The pointer of VAR_CHECK_ITEM array.\r
+  @param[in]    Name         The pointer of input arg.\r
+  @param[in]    Init         The switch to execute the check.\r
+\r
+  @return The value of VAR_CHECK_CODE.\r
+\r
+**/\r
+VAR_CHECK_CODE\r
+IfConfig6RetriveCheckListByName(\r
+  IN VAR_CHECK_ITEM    *CheckList,\r
+  IN CHAR16            *Name,\r
+  IN BOOLEAN           Init\r
+)\r
+{\r
+  STATIC UINT32     CheckDuplicate;\r
+  STATIC UINT32     CheckConflict;\r
+  VAR_CHECK_CODE    RtCode;\r
+  UINT32            Index;\r
+  VAR_CHECK_ITEM    Arg;\r
+\r
+  if (Init) {\r
+    CheckDuplicate = 0;\r
+    CheckConflict  = 0;\r
+    return VarCheckOk;\r
+  }\r
+\r
+  RtCode  = VarCheckOk;\r
+  Index   = 0;\r
+  Arg     = CheckList[Index];\r
+\r
+  //\r
+  // Check the Duplicated/Conflicted/Unknown input Args.\r
+  //\r
+  while (Arg.FlagStr != NULL) {\r
+    if (StrCmp (Arg.FlagStr, Name) == 0) {\r
+\r
+      if (CheckDuplicate & Arg.FlagID) {\r
+        RtCode = VarCheckDuplicate;\r
+        break;\r
+      }\r
+\r
+      if (CheckConflict & Arg.ConflictMask) {\r
+        RtCode = VarCheckConflict;\r
+        break;\r
+      }\r
+\r
+      CheckDuplicate |= Arg.FlagID;\r
+      CheckConflict  |= Arg.ConflictMask;\r
+      break;\r
+    }\r
+\r
+    Arg = CheckList[++Index];\r
+  }\r
+\r
+  if (Arg.FlagStr == NULL) {\r
+    RtCode = VarCheckUnknown;\r
+  }\r
+\r
+  return RtCode;\r
+}\r
+\r
+/**\r
+  The notify function of create event when performing a manual config.\r
+\r
+  @param[in]    CheckList    The pointer of Event.\r
+  @param[in]    Context      The pointer of Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IfConfig6ManualAddressNotify (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+  *((BOOLEAN *) Context) = TRUE;\r
+}\r
+\r
+/**\r
+  Print MAC address.\r
+\r
+  @param[in]    Node    The pointer of MAC address buffer.\r
+  @param[in]    Size    The size of MAC address buffer.\r
+\r
+**/\r
+VOID\r
+IfConfig6PrintMacAddr (\r
+  IN UINT8     *Node,\r
+  IN UINT32    Size\r
+  )\r
+{\r
+  UINTN    Index;\r
+\r
+  ASSERT (Size <= MACADDRMAXSIZE);\r
+\r
+  for (Index = 0; Index < Size; Index++) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_BODY), mHiiHandle, Node[Index]);\r
+    if (Index + 1 < Size) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);\r
+    }\r
+  }\r
+\r
+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);\r
+}\r
+\r
+/**\r
+  Print IPv6 address.\r
+\r
+  @param[in]    Ip           The pointer of Ip bufffer in EFI_IPv6_ADDRESS format.\r
+  @param[in]    PrefixLen    The pointer of PrefixLen that describes the size Prefix.\r
+\r
+**/\r
+VOID\r
+IfConfig6PrintIpAddr (\r
+  IN EFI_IPv6_ADDRESS    *Ip,\r
+  IN UINT8               *PrefixLen\r
+  )\r
+{\r
+  UINTN      Index;\r
+  BOOLEAN    Short;\r
+\r
+  Short = FALSE;\r
+\r
+  for (Index = 0; Index < PREFIXMAXLEN; Index = Index + 2) {\r
+\r
+    if (!Short && (Index + 1 < PREFIXMAXLEN) && (Index % 2 == 0) && (Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0)) {\r
+      //\r
+      // Deal with the case of ::.\r
+      //\r
+      if (Index == 0) {\r
+        //\r
+        // :: is at the beginning of the address.\r
+        //\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);\r
+      }\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);\r
+\r
+      while ((Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0) && (Index < PREFIXMAXLEN)) {\r
+        Index = Index + 2;\r
+        if (Index > PREFIXMAXLEN - 2) {\r
+          break;\r
+        }\r
+      }\r
+\r
+      Short = TRUE;\r
+\r
+      if (Index == PREFIXMAXLEN) {\r
+        //\r
+        // :: is at the end of the address.\r
+        //\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (Index < PREFIXMAXLEN - 1) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index]);\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index + 1]);\r
+    }\r
+\r
+    if (Index + 2 < PREFIXMAXLEN) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);\r
+    }\r
+  }\r
+\r
+  if (PrefixLen != NULL) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_PREFIX_LEN), mHiiHandle, *PrefixLen);\r
+  }\r
+}\r
+\r
+/**\r
+  Pick up host IPv6 address in string format from Args with "-s" option and convert it to EFI_IP6_CONFIG_MANUAL_ADDRESS format.\r
+\r
+  @param[in, out]    Arg        The pointer of the address of ARG_LIST which save Args with the "-s" option.\r
+  @param[out]        Buf        The pointer of the address of EFI_IP6_CONFIG_MANUAL_ADDRESS.\r
+  @param[out]        Address    The pointer of BufSize that describes the size of Buf in bytes.\r
+\r
+  @retval EFI_SUCCESS    The convertion is successful.\r
+  @retval Others         Does't find the host address, or it is an invalid IPv6 address in string format.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ParseManualAddressList (\r
+  IN OUT ARG_LIST                         **Arg,\r
+     OUT EFI_IP6_CONFIG_MANUAL_ADDRESS    **Buf,\r
+     OUT UINTN                            *BufSize\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS    *AddrBuf;\r
+  ARG_LIST                         *VarArg;\r
+  EFI_IPv6_ADDRESS                 Address;\r
+  UINT8                            Prefix;\r
+  UINT8                            AddrCnt;\r
+\r
+  AddrCnt  = 0;\r
+  *BufSize = 0;\r
+  *Buf     = NULL;\r
+  VarArg   = *Arg;\r
+  Status   = EFI_SUCCESS;\r
+\r
+  //\r
+  // Go through the list to check the correctness of input host ip6 address.\r
+  //\r
+  while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {\r
+\r
+    Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // host ip ip ... gw\r
+      //\r
+      break;\r
+    }\r
+\r
+    VarArg = VarArg->Next;\r
+    AddrCnt++;\r
+  }\r
+\r
+  if (AddrCnt == 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
+  ASSERT (AddrBuf != NULL);\r
+\r
+  AddrCnt = 0;\r
+  VarArg  = *Arg;\r
+  Status  = EFI_SUCCESS;\r
+\r
+  //\r
+  // Go through the list to fill in the EFI_IP6_CONFIG_MANUAL_ADDRESS structure.\r
+  //\r
+  while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {\r
+\r
+    Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    //\r
+    // If prefix length is not set, set it as Zero here. In the IfConfigSetInterfaceInfo()\r
+    // Zero prefix, length will be transfered to default prefix length.\r
+    //\r
+    if (Prefix == 0xFF) {\r
+      Prefix = 0;\r
+    }\r
+    AddrBuf[AddrCnt].IsAnycast    = FALSE;\r
+    AddrBuf[AddrCnt].PrefixLength = Prefix;\r
+    IP6_COPY_ADDRESS (&AddrBuf[AddrCnt].Address, &Address);\r
+    VarArg = VarArg->Next;\r
+    AddrCnt++;\r
+  }\r
+\r
+  *Arg = VarArg;\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  *Buf     = AddrBuf;\r
+  *BufSize = AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  FreePool (AddrBuf);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Pick up gw/dns IPv6 address in string format from Args with "-s" option and convert it to EFI_IPv6_ADDRESS format.\r
+\r
+  @param[in, out]    Arg        The pointer of the address of ARG_LIST that save Args with the "-s" option.\r
+  @param[out]        Buf        The pointer of the address of EFI_IPv6_ADDRESS.\r
+  @param[out]        Address    The pointer of BufSize that describes the size of Buf in bytes.\r
+\r
+  @retval EFI_SUCCESS    The conversion is successful.\r
+  @retval Others         Doesn't find the host address, or it is an invalid IPv6 address in string format.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ParseGwDnsAddressList (\r
+  IN OUT ARG_LIST            **Arg,\r
+     OUT EFI_IPv6_ADDRESS    **Buf,\r
+     OUT UINTN               *BufSize\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  EFI_IPv6_ADDRESS    *AddrBuf;\r
+  ARG_LIST            *VarArg;\r
+  EFI_IPv6_ADDRESS    Address;\r
+  UINT8               Prefix;\r
+  UINT8               AddrCnt;\r
+\r
+  AddrCnt  = 0;\r
+  *BufSize = 0;\r
+  *Buf     = NULL;\r
+  VarArg   = *Arg;\r
+  Status   = EFI_SUCCESS;\r
+\r
+  //\r
+  // Go through the list to check the correctness of input gw/dns address.\r
+  //\r
+  while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {\r
+\r
+    Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // gw ip ip ... host\r
+      //\r
+      break;\r
+    }\r
+\r
+    VarArg = VarArg->Next;\r
+    AddrCnt++;\r
+  }\r
+\r
+  if (AddrCnt == 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IPv6_ADDRESS));\r
+  ASSERT (AddrBuf != NULL);\r
+\r
+  AddrCnt = 0;\r
+  VarArg  = *Arg;\r
+  Status  = EFI_SUCCESS;\r
+\r
+  //\r
+  // Go through the list to fill in the EFI_IPv6_ADDRESS structure.\r
+  //\r
+  while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {\r
+\r
+    Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    IP6_COPY_ADDRESS (&AddrBuf[AddrCnt], &Address);\r
+\r
+    VarArg = VarArg->Next;\r
+    AddrCnt++;\r
+  }\r
+\r
+  *Arg = VarArg;\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) {\r
+   goto ON_ERROR;\r
+  }\r
+\r
+  *Buf     = AddrBuf;\r
+  *BufSize = AddrCnt * sizeof (EFI_IPv6_ADDRESS);\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  FreePool (AddrBuf);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Parse InterfaceId in string format from Args with the "-s" option and convert it to EFI_IP6_CONFIG_INTERFACE_ID format.\r
+\r
+  @param[in, out]   Arg     The pointer of the address of ARG_LIST that saves Args with the "-s" option.\r
+  @param[out]       IfId    The pointer of EFI_IP6_CONFIG_INTERFACE_ID.\r
+\r
+  @retval EFI_SUCCESS              The get status processed successfullly.\r
+  @retval EFI_INVALID_PARAMETER    The get status process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ParseInterfaceId (\r
+  IN OUT ARG_LIST                       **Arg,\r
+     OUT EFI_IP6_CONFIG_INTERFACE_ID    **IfId\r
+  )\r
+{\r
+  UINT8     Index;\r
+  UINT8     NodeVal;\r
+  CHAR16    *IdStr;\r
+\r
+  if (*Arg == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Index = 0;\r
+  IdStr = (*Arg)->Arg;\r
+  ASSERT (IfId != NULL);\r
+  *IfId = AllocateZeroPool (sizeof (EFI_IP6_CONFIG_INTERFACE_ID));\r
+  ASSERT (*IfId != NULL);\r
+\r
+  while ((*IdStr != L'\0') && (Index < 8)) {\r
+\r
+    NodeVal = 0;\r
+    while ((*IdStr != L':') && (*IdStr != L'\0')) {\r
+\r
+      if ((*IdStr <= L'F') && (*IdStr >= L'A')) {\r
+        NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'A' + 10);\r
+      } else if ((*IdStr <= L'f') && (*IdStr >= L'a')) {\r
+        NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'a' + 10);\r
+      } else if ((*IdStr <= L'9') && (*IdStr >= L'0')) {\r
+        NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'0');\r
+      } else {\r
+        FreePool (*IfId);\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      IdStr++;\r
+    }\r
+\r
+    (*IfId)->Id[Index++] = NodeVal;\r
+\r
+    if (*IdStr == L':') {\r
+      IdStr++;\r
+    }\r
+  }\r
+\r
+  *Arg = (*Arg)->Next;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Parse dad in string format from Args with the "-s" option and convert it to UINT32 format.\r
+\r
+  @param[in, out]   Arg      The pointer of the address of ARG_LIST that saves Args with the "-s" option.\r
+  @param[out]       Xmits    The pointer of Xmits.\r
+\r
+  @retval EFI_SUCCESS    The get status processed successfully.\r
+  @retval others         The get status process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ParseDadXmits (\r
+  IN OUT ARG_LIST    **Arg,\r
+     OUT UINT32      *Xmits\r
+  )\r
+{\r
+  CHAR16    *ValStr;\r
+\r
+  if (*Arg == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ValStr = (*Arg)->Arg;\r
+  *Xmits = 0;\r
+\r
+  while (*ValStr != L'\0') {\r
+\r
+    if ((*ValStr <= L'9') && (*ValStr >= L'0')) {\r
+\r
+      *Xmits = (*Xmits * 10) + (*ValStr - L'0');\r
+\r
+    } else {\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    ValStr++;\r
+  }\r
+\r
+  *Arg = (*Arg)->Next;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  The get current status of all handles.\r
+\r
+  @param[in]   ImageHandle    The handle of  ImageHandle.\r
+  @param[in]   IfName         The pointer of  IfName(interface name).\r
+  @param[in]   IfList         The pointer of  IfList(interface list).\r
+\r
+  @retval EFI_SUCCESS    The get status processed successfully.\r
+  @retval others         The get status process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6GetInterfaceInfo (\r
+  IN EFI_HANDLE    ImageHandle,\r
+  IN CHAR16        *IfName,\r
+  IN LIST_ENTRY    *IfList\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINTN                            HandleIndex;\r
+  UINTN                            HandleNum;\r
+  EFI_HANDLE                       *HandleBuffer;\r
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;\r
+  EFI_IP6_CONFIG_INTERFACE_INFO    *IfInfo;\r
+  IFCONFIG6_INTERFACE_CB           *IfCb;\r
+  UINTN                            DataSize;\r
+\r
+  HandleBuffer = NULL;\r
+  HandleNum    = 0;\r
+\r
+  IfInfo       = NULL;\r
+  IfCb         = NULL;\r
+\r
+  //\r
+  // Locate all the handles with ip6 service binding protocol.\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  &HandleNum,\r
+                  &HandleBuffer\r
+                 );\r
+  if (EFI_ERROR (Status) || (HandleNum == 0)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  //\r
+  // Enumerate all handles that installed with ip6 service binding protocol.\r
+  //\r
+  for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {\r
+    IfCb      = NULL;\r
+    IfInfo    = NULL;\r
+    DataSize  = 0;\r
+\r
+    //\r
+    // Ip6config protocol and ip6 service binding protocol are installed\r
+    // on the same handle.\r
+    //\r
+    ASSERT (HandleBuffer != NULL);\r
+    Status = gBS->HandleProtocol (\r
+                    HandleBuffer[HandleIndex],\r
+                    &gEfiIp6ConfigProtocolGuid,\r
+                    (VOID **) &Ip6Cfg\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+    //\r
+    // Get the interface information size.\r
+    //\r
+    Status = Ip6Cfg->GetData (\r
+                       Ip6Cfg,\r
+                       Ip6ConfigDataTypeInterfaceInfo,\r
+                       &DataSize,\r
+                       NULL\r
+                       );\r
+\r
+    if (Status != EFI_BUFFER_TOO_SMALL) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    IfInfo = AllocateZeroPool (DataSize);\r
+\r
+    if (IfInfo == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_ERROR;\r
+    }\r
+    //\r
+    // Get the interface info.\r
+    //\r
+    Status = Ip6Cfg->GetData (\r
+                       Ip6Cfg,\r
+                       Ip6ConfigDataTypeInterfaceInfo,\r
+                       &DataSize,\r
+                       IfInfo\r
+                       );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+      goto ON_ERROR;\r
+    }\r
+    //\r
+    // Check the interface name if required.\r
+    //\r
+    if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) != 0)) {\r
+      FreePool (IfInfo);\r
+      continue;\r
+    }\r
+\r
+    DataSize = 0;\r
+    //\r
+    // Get the size of dns server list.\r
+    //\r
+    Status = Ip6Cfg->GetData (\r
+                       Ip6Cfg,\r
+                       Ip6ConfigDataTypeDnsServer,\r
+                       &DataSize,\r
+                       NULL\r
+                       );\r
+\r
+    if ((Status != EFI_BUFFER_TOO_SMALL) && (Status != EFI_NOT_FOUND)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    IfCb = AllocateZeroPool (sizeof (IFCONFIG6_INTERFACE_CB) + DataSize);\r
+\r
+    if (IfCb == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    IfCb->NicHandle = HandleBuffer[HandleIndex];\r
+    IfCb->IfInfo    = IfInfo;\r
+    IfCb->IfCfg     = Ip6Cfg;\r
+    IfCb->DnsCnt    = (UINT32) (DataSize / sizeof (EFI_IPv6_ADDRESS));\r
+\r
+    //\r
+    // Get the dns server list if has.\r
+    //\r
+    if (DataSize > 0) {\r
+\r
+      Status = Ip6Cfg->GetData (\r
+                         Ip6Cfg,\r
+                         Ip6ConfigDataTypeDnsServer,\r
+                         &DataSize,\r
+                         IfCb->DnsAddr\r
+                         );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+        goto ON_ERROR;\r
+      }\r
+    }\r
+    //\r
+    // Get the interface id if has.\r
+    //\r
+    DataSize   = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);\r
+    IfCb->IfId = AllocateZeroPool (DataSize);\r
+\r
+    if (IfCb->IfId == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    Status = Ip6Cfg->GetData (\r
+                       Ip6Cfg,\r
+                       Ip6ConfigDataTypeAltInterfaceId,\r
+                       &DataSize,\r
+                       IfCb->IfId\r
+                       );\r
+\r
+    if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    if (Status == EFI_NOT_FOUND) {\r
+      FreePool (IfCb->IfId);\r
+      IfCb->IfId = NULL;\r
+    }\r
+    //\r
+    // Get the config policy.\r
+    //\r
+    DataSize = sizeof (EFI_IP6_CONFIG_POLICY);\r
+    Status   = Ip6Cfg->GetData (\r
+                         Ip6Cfg,\r
+                         Ip6ConfigDataTypePolicy,\r
+                         &DataSize,\r
+                         &IfCb->Policy\r
+                         );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+      goto ON_ERROR;\r
+    }\r
+    //\r
+    // Get the dad transmits.\r
+    //\r
+    DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+    Status   = Ip6Cfg->GetData (\r
+                         Ip6Cfg,\r
+                         Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+                         &DataSize,\r
+                         &IfCb->Xmits\r
+                         );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    InsertTailList (IfList, &IfCb->Link);\r
+\r
+    if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) == 0)) {\r
+      //\r
+      // Only need the appointed interface, keep the allocated buffer.\r
+      //\r
+      IfCb   = NULL;\r
+      IfInfo = NULL;\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (HandleBuffer != NULL) {\r
+    FreePool (HandleBuffer);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  if (IfInfo != NULL) {\r
+    FreePool (IfInfo);\r
+  }\r
+\r
+  if (IfCb != NULL) {\r
+    if (IfCb->IfId != NULL) {\r
+      FreePool (IfCb->IfId);\r
+    }\r
+\r
+    FreePool (IfCb);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The list process of the IfConfig6 application.\r
+\r
+  @param[in]   IfList    The pointer of IfList(interface list).\r
+\r
+  @retval EFI_SUCCESS    The IfConfig6 list processed successfully.\r
+  @retval others         The IfConfig6 list process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ShowInterfaceInfo (\r
+  IN LIST_ENTRY    *IfList\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  LIST_ENTRY                *Entry;\r
+  IFCONFIG6_INTERFACE_CB    *IfCb;\r
+  UINTN                     Index;\r
+\r
+  Entry  = IfList->ForwardLink;\r
+  Status = EFI_SUCCESS;\r
+\r
+  if (IsListEmpty (IfList)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);\r
+  }\r
+\r
+  //\r
+  // Go through the interface list.\r
+  //\r
+  while (Entry != IfList) {\r
+\r
+    IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);\r
+\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle);\r
+\r
+    //\r
+    // Print interface name.\r
+    //\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IF_NAME), mHiiHandle, IfCb->IfInfo->Name);\r
+\r
+    //\r
+    // Print interface config policy.\r
+    //\r
+    if (IfCb->Policy == Ip6ConfigPolicyAutomatic) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_AUTO), mHiiHandle);\r
+    } else {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_MAN), mHiiHandle);\r
+    }\r
+\r
+    //\r
+    // Print dad transmit.\r
+    //\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DAD_TRANSMITS), mHiiHandle, IfCb->Xmits);\r
+\r
+    //\r
+    // Print interface id if has.\r
+    //\r
+    if (IfCb->IfId != NULL) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_INTERFACE_ID_HEAD), mHiiHandle);\r
+\r
+      IfConfig6PrintMacAddr (\r
+        IfCb->IfId->Id,\r
+        8\r
+        );\r
+    }\r
+    //\r
+    // Print mac address of the interface.\r
+    //\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_HEAD), mHiiHandle);\r
+\r
+    IfConfig6PrintMacAddr (\r
+      IfCb->IfInfo->HwAddress.Addr,\r
+      IfCb->IfInfo->HwAddressSize\r
+      );\r
+\r
+    //\r
+    // Print ip addresses list of the interface.\r
+    //\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_HEAD), mHiiHandle);\r
+\r
+    for (Index = 0; Index < IfCb->IfInfo->AddressInfoCount; Index++) {\r
+      IfConfig6PrintIpAddr (\r
+        &IfCb->IfInfo->AddressInfo[Index].Address,\r
+        &IfCb->IfInfo->AddressInfo[Index].PrefixLength\r
+        );\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);\r
+    }\r
+\r
+    //\r
+    // Print dns server addresses list of the interface if has.\r
+    //\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DNS_ADDR_HEAD), mHiiHandle);\r
+\r
+    for (Index = 0; Index < IfCb->DnsCnt; Index++) {\r
+      IfConfig6PrintIpAddr (\r
+        &IfCb->DnsAddr[Index],\r
+        NULL\r
+        );\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);\r
+    }\r
+\r
+    //\r
+    // Print route table of the interface if has.\r
+    //\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_ROUTE_HEAD), mHiiHandle);\r
+\r
+    for (Index = 0; Index < IfCb->IfInfo->RouteCount; Index++) {\r
+      IfConfig6PrintIpAddr (\r
+        &IfCb->IfInfo->RouteTable[Index].Destination,\r
+        &IfCb->IfInfo->RouteTable[Index].PrefixLength\r
+        );\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_JOINT), mHiiHandle);\r
+\r
+      IfConfig6PrintIpAddr (\r
+        &IfCb->IfInfo->RouteTable[Index].Gateway,\r
+        NULL\r
+        );\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);\r
+    }\r
+\r
+    Entry = Entry->ForwardLink;\r
+  }\r
+\r
+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The clean process of the IfConfig6 application.\r
+\r
+  @param[in]   IfList    The pointer of IfList(interface list).\r
+\r
+  @retval EFI_SUCCESS    The IfConfig6 clean processed successfully.\r
+  @retval others         The IfConfig6 clean process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ClearInterfaceInfo (\r
+  IN LIST_ENTRY    *IfList\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  LIST_ENTRY                *Entry;\r
+  IFCONFIG6_INTERFACE_CB    *IfCb;\r
+  EFI_IP6_CONFIG_POLICY     Policy;\r
+\r
+  Policy = Ip6ConfigPolicyAutomatic;\r
+  Entry  = IfList->ForwardLink;\r
+  Status = EFI_SUCCESS;\r
+\r
+  if (IsListEmpty (IfList)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);\r
+  }\r
+\r
+  //\r
+  // Go through the interface list.\r
+  //\r
+  while (Entry != IfList) {\r
+\r
+    IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);\r
+\r
+    Status = IfCb->IfCfg->SetData (\r
+                            IfCb->IfCfg,\r
+                            Ip6ConfigDataTypePolicy,\r
+                            sizeof (EFI_IP6_CONFIG_POLICY),\r
+                            &Policy\r
+                            );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    Entry  = Entry->ForwardLink;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The set process of the IfConfig6 application.\r
+\r
+  @param[in]   IfList    The pointer of IfList(interface list).\r
+  @param[in]   VarArg    The pointer of ARG_LIST(Args with "-s" option).\r
+\r
+  @retval EFI_SUCCESS    The IfConfig6 set processed successfully.\r
+  @retval others         The IfConfig6 set process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6SetInterfaceInfo (\r
+  IN LIST_ENTRY    *IfList,\r
+  IN ARG_LIST      *VarArg\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  IFCONFIG6_INTERFACE_CB           *IfCb;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS    *CfgManAddr;\r
+  EFI_IPv6_ADDRESS                 *CfgAddr;\r
+  UINTN                            AddrSize;\r
+  EFI_IP6_CONFIG_INTERFACE_ID      *InterfaceId;\r
+  UINT32                           DadXmits;\r
+  UINT32                           CurDadXmits;\r
+  UINTN                            CurDadXmitsLen;\r
+  EFI_IP6_CONFIG_POLICY            Policy;\r
+\r
+  VAR_CHECK_CODE                   CheckCode;\r
+  EFI_EVENT                        TimeOutEvt;\r
+  EFI_EVENT                        MappedEvt;\r
+  BOOLEAN                          IsAddressOk;\r
+\r
+  UINTN                            DataSize;\r
+  UINT32                           Index;\r
+  UINT32                           Index2;\r
+  BOOLEAN                          IsAddressSet;\r
+  EFI_IP6_CONFIG_INTERFACE_INFO    *IfInfo;\r
+\r
+  CfgManAddr  = NULL;\r
+  CfgAddr     = NULL;\r
+  TimeOutEvt  = NULL;\r
+  MappedEvt   = NULL;\r
+  IfInfo      = NULL;\r
+  InterfaceId = NULL;\r
+  CurDadXmits = 0;\r
+\r
+  if (IsListEmpty (IfList)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Make sure to set only one interface each time.\r
+  //\r
+  IfCb   = BASE_CR (IfList->ForwardLink, IFCONFIG6_INTERFACE_CB, Link);\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // Initialize check list mechanism.\r
+  //\r
+  CheckCode = IfConfig6RetriveCheckListByName(\r
+                NULL,\r
+                NULL,\r
+                TRUE\r
+                );\r
+\r
+  //\r
+  // Create events & timers for asynchronous settings.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &TimeOutEvt\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  IfConfig6ManualAddressNotify,\r
+                  &IsAddressOk,\r
+                  &MappedEvt\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Parse the setting variables.\r
+  //\r
+  while (VarArg != NULL) {\r
+     //\r
+     // Check invalid parameters (duplication & unknown & conflict).\r
+     //\r
+    CheckCode = IfConfig6RetriveCheckListByName(\r
+                  mSetCheckList,\r
+                  VarArg->Arg,\r
+                  FALSE\r
+                  );\r
+\r
+    if (VarCheckOk != CheckCode) {\r
+      switch (CheckCode) {\r
+        case VarCheckDuplicate:\r
+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_DUPLICATE_COMMAND), mHiiHandle, VarArg->Arg);\r
+          break;\r
+\r
+        case VarCheckConflict:\r
+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_CONFLICT_COMMAND), mHiiHandle, VarArg->Arg);\r
+          break;\r
+\r
+        case VarCheckUnknown:\r
+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_UNKNOWN_COMMAND), mHiiHandle, VarArg->Arg);\r
+          break;\r
+\r
+        default:\r
+          break;\r
+      }\r
+\r
+      VarArg = VarArg->Next;\r
+      continue;\r
+    }\r
+    //\r
+    // Process valid variables.\r
+    //\r
+    if (StrCmp(VarArg->Arg, L"auto") == 0) {\r
+      //\r
+      // Set automaic config policy\r
+      //\r
+      Policy = Ip6ConfigPolicyAutomatic;\r
+      Status = IfCb->IfCfg->SetData (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypePolicy,\r
+                              sizeof (EFI_IP6_CONFIG_POLICY),\r
+                              &Policy\r
+                              );\r
+\r
+      if (EFI_ERROR(Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      VarArg= VarArg->Next;\r
+\r
+    } else if (StrCmp (VarArg->Arg, L"man") == 0) {\r
+      //\r
+      // Set manual config policy.\r
+      //\r
+      Policy = Ip6ConfigPolicyManual;\r
+      Status = IfCb->IfCfg->SetData (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypePolicy,\r
+                              sizeof (EFI_IP6_CONFIG_POLICY),\r
+                              &Policy\r
+                              );\r
+\r
+      if (EFI_ERROR(Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      VarArg= VarArg->Next;\r
+\r
+    } else if (StrCmp (VarArg->Arg, L"host") == 0) {\r
+      //\r
+      // Parse till the next tag or the end of command line.\r
+      //\r
+      VarArg = VarArg->Next;\r
+      Status = IfConfig6ParseManualAddressList (\r
+                 &VarArg,\r
+                 &CfgManAddr,\r
+                 &AddrSize\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        if (Status == EFI_INVALID_PARAMETER) {\r
+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"host");\r
+          continue;\r
+        } else {\r
+          goto ON_EXIT;\r
+        }\r
+      }\r
+      //\r
+      // Set static host ip6 address list.\r
+      //   This is a asynchronous process.\r
+      //\r
+      IsAddressOk = FALSE;\r
+\r
+      Status = IfCb->IfCfg->RegisterDataNotify (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypeManualAddress,\r
+                              MappedEvt\r
+                              );\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      Status = IfCb->IfCfg->SetData (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypeManualAddress,\r
+                              AddrSize,\r
+                              CfgManAddr\r
+                              );\r
+\r
+      if (Status == EFI_NOT_READY) {\r
+        //\r
+        // Get current dad transmits count.\r
+        //\r
+        CurDadXmitsLen = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+        IfCb->IfCfg->GetData (\r
+                       IfCb->IfCfg,\r
+                       Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+                       &CurDadXmitsLen,\r
+                       &CurDadXmits\r
+                       );\r
+\r
+        gBS->SetTimer (TimeOutEvt, TimerRelative, 50000000 + 10000000 * CurDadXmits);\r
+\r
+        while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
+          if (IsAddressOk) {\r
+            Status = EFI_SUCCESS;\r
+            break;\r
+          }\r
+        }\r
+      }\r
+\r
+      IfCb->IfCfg->UnregisterDataNotify (\r
+                     IfCb->IfCfg,\r
+                     Ip6ConfigDataTypeManualAddress,\r
+                     MappedEvt\r
+                     );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_MAN_HOST), mHiiHandle, Status);\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      //\r
+      // Check whether the address is set successfully.\r
+      //\r
+      DataSize = 0;\r
+\r
+      Status = IfCb->IfCfg->GetData (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypeInterfaceInfo,\r
+                              &DataSize,\r
+                              NULL\r
+                              );\r
+\r
+      if (Status != EFI_BUFFER_TOO_SMALL) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      IfInfo = AllocateZeroPool (DataSize);\r
+\r
+      if (IfInfo == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      Status = IfCb->IfCfg->GetData (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypeInterfaceInfo,\r
+                              &DataSize,\r
+                              IfInfo\r
+                              );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      for ( Index = 0; Index < (UINTN) (AddrSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); Index++) {\r
+        IsAddressSet = FALSE;\r
+        //\r
+        // By default, the prefix length 0 is regarded as 64.\r
+        //\r
+        if (CfgManAddr[Index].PrefixLength == 0) {\r
+          CfgManAddr[Index].PrefixLength = 64;\r
+        }\r
+\r
+        for (Index2 = 0; Index2 < IfInfo->AddressInfoCount; Index2++) {\r
+          if (EFI_IP6_EQUAL (&IfInfo->AddressInfo[Index2].Address, &CfgManAddr[Index].Address) &&\r
+              (IfInfo->AddressInfo[Index2].PrefixLength == CfgManAddr[Index].PrefixLength)) {\r
+            IsAddressSet = TRUE;\r
+            break;\r
+          }\r
+        }\r
+\r
+        if (!IsAddressSet) {\r
+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_ADDRESS_FAILED), mHiiHandle);\r
+          IfConfig6PrintIpAddr (\r
+            &CfgManAddr[Index].Address,\r
+            &CfgManAddr[Index].PrefixLength\r
+            );\r
+        }\r
+      }\r
+\r
+    } else if (StrCmp (VarArg->Arg, L"gw") == 0) {\r
+      //\r
+      // Parse till the next tag or the end of command line.\r
+      //\r
+      VarArg = VarArg->Next;\r
+      Status = IfConfig6ParseGwDnsAddressList (\r
+                 &VarArg,\r
+                 &CfgAddr,\r
+                 &AddrSize\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        if (Status == EFI_INVALID_PARAMETER) {\r
+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"gw");\r
+          continue;\r
+        } else {\r
+          goto ON_EXIT;\r
+        }\r
+      }\r
+      //\r
+      // Set static gateway ip6 address list.\r
+      //\r
+      Status = IfCb->IfCfg->SetData (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypeGateway,\r
+                              AddrSize,\r
+                              CfgAddr\r
+                              );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+\r
+    } else if (StrCmp (VarArg->Arg, L"dns") == 0) {\r
+      //\r
+      // Parse till the next tag or the end of command line.\r
+      //\r
+      VarArg = VarArg->Next;\r
+      Status = IfConfig6ParseGwDnsAddressList (\r
+                 &VarArg,\r
+                 &CfgAddr,\r
+                 &AddrSize\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        if (Status == EFI_INVALID_PARAMETER) {\r
+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"dns");\r
+          continue;\r
+        } else {\r
+          goto ON_EXIT;\r
+        }\r
+      }\r
+      //\r
+      // Set static dhs server ip6 address list.\r
+      //\r
+      Status = IfCb->IfCfg->SetData (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypeDnsServer,\r
+                              AddrSize,\r
+                              CfgAddr\r
+                              );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+\r
+    } else if (StrCmp (VarArg->Arg, L"id") == 0) {\r
+      //\r
+      // Parse till the next tag or the end of command line.\r
+      //\r
+      VarArg = VarArg->Next;\r
+      Status = IfConfig6ParseInterfaceId (&VarArg, &InterfaceId);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+      //\r
+      // Set alternative interface id.\r
+      //\r
+      Status = IfCb->IfCfg->SetData (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypeAltInterfaceId,\r
+                              sizeof (EFI_IP6_CONFIG_INTERFACE_ID),\r
+                              InterfaceId\r
+                              );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+\r
+    } else if (StrCmp (VarArg->Arg, L"dad") == 0) {\r
+      //\r
+      // Parse till the next tag or the end of command line.\r
+      //\r
+      VarArg = VarArg->Next;\r
+      Status = IfConfig6ParseDadXmits (&VarArg, &DadXmits);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+      //\r
+      // Set dad transmits count.\r
+      //\r
+      Status = IfCb->IfCfg->SetData (\r
+                              IfCb->IfCfg,\r
+                              Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+                              sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS),\r
+                              &DadXmits\r
+                              );\r
+\r
+      if (EFI_ERROR(Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (CfgManAddr != NULL) {\r
+    FreePool (CfgManAddr);\r
+  }\r
+\r
+  if (CfgAddr != NULL) {\r
+    FreePool (CfgAddr);\r
+  }\r
+\r
+  if (MappedEvt != NULL) {\r
+    gBS->CloseEvent (MappedEvt);\r
+  }\r
+\r
+  if (TimeOutEvt != NULL) {\r
+    gBS->CloseEvent (TimeOutEvt);\r
+  }\r
+\r
+  if (IfInfo != NULL) {\r
+    FreePool (IfInfo);\r
+  }\r
+\r
+  return Status;\r
+\r
+}\r
+\r
+/**\r
+  The IfConfig6 main process.\r
+\r
+  @param[in]   Private    The pointer of IFCONFIG6_PRIVATE_DATA.\r
+\r
+  @retval EFI_SUCCESS    IfConfig6 processed successfully.\r
+  @retval others         The IfConfig6 process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6 (\r
+  IN IFCONFIG6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+\r
+  //\r
+  // Get configure information of all interfaces.\r
+  //\r
+  Status = IfConfig6GetInterfaceInfo (\r
+             Private->ImageHandle,\r
+             Private->IfName,\r
+             &Private->IfList\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  switch (Private->OpCode) {\r
+  case IfConfig6OpList:\r
+    Status = IfConfig6ShowInterfaceInfo (&Private->IfList);\r
+    break;\r
+\r
+  case IfConfig6OpClear:\r
+    Status = IfConfig6ClearInterfaceInfo (&Private->IfList);\r
+    break;\r
+\r
+  case IfConfig6OpSet:\r
+    Status = IfConfig6SetInterfaceInfo (&Private->IfList, Private->VarArg);\r
+    break;\r
+\r
+  default:\r
+    Status = EFI_ABORTED;\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The IfConfig6 cleanup process, free the allocated memory.\r
+\r
+  @param[in]   Private    The pointer of  IFCONFIG6_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+IfConfig6Cleanup (\r
+  IN IFCONFIG6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *NextEntry;\r
+  IFCONFIG6_INTERFACE_CB    *IfCb;\r
+  ARG_LIST                  *ArgNode;\r
+  ARG_LIST                  *ArgHead;\r
+\r
+  ASSERT (Private != NULL);\r
+\r
+  //\r
+  // Clean the list which save the set config Args.\r
+  //\r
+  if (Private->VarArg != NULL) {\r
+    ArgHead = Private->VarArg;\r
+\r
+    while (ArgHead->Next != NULL) {\r
+      ArgNode = ArgHead->Next;\r
+      FreePool (ArgHead);\r
+      ArgHead = ArgNode;\r
+    }\r
+\r
+    FreePool (ArgHead);\r
+  }\r
+\r
+  if (Private->IfName != NULL)\r
+    FreePool (Private->IfName);\r
+\r
+\r
+  //\r
+  // Clean the IFCONFIG6_INTERFACE_CB list.\r
+  //\r
+  Entry     = Private->IfList.ForwardLink;\r
+  NextEntry = Entry->ForwardLink;\r
+\r
+  while (Entry != &Private->IfList) {\r
+\r
+    IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);\r
+\r
+    RemoveEntryList (&IfCb->Link);\r
+\r
+    if (IfCb->IfId != NULL) {\r
+\r
+      FreePool (IfCb->IfId);\r
+    }\r
+\r
+    if (IfCb->IfInfo != NULL) {\r
+\r
+      FreePool (IfCb->IfInfo);\r
+    }\r
+\r
+    FreePool (IfCb);\r
+\r
+    Entry     = NextEntry;\r
+    NextEntry = Entry->ForwardLink;\r
+  }\r
+\r
+  FreePool (Private);\r
+}\r
+\r
+/**\r
+  This is the declaration of an EFI image entry point. This entry point is\r
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+  both device drivers and bus drivers.\r
+\r
+  The entry point for the IfConfig6 application which parses the command line input and calls the IfConfig6 process.\r
+\r
+  @param[in] ImageHandle    The image handle of this application.\r
+  @param[in] SystemTable    The pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS    The operation completed successfully.\r
+  @retval Others         Some errors occur.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IfConfig6Initialize (\r
+  IN  EFI_HANDLE         ImageHandle,\r
+  IN  EFI_SYSTEM_TABLE    *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  IFCONFIG6_PRIVATE_DATA    *Private;\r
+  LIST_ENTRY                *ParamPackage;\r
+  CONST CHAR16              *ValueStr;\r
+  ARG_LIST                  *ArgList;\r
+  CHAR16                    *ProblemParam;\r
+  CHAR16                    *Str;\r
+\r
+  Private = NULL;\r
+\r
+  //\r
+  // Register our string package with HII and return the handle to it.\r
+  //\r
+  mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IfConfig6Strings, NULL);\r
+  ASSERT (mHiiHandle != NULL);\r
+\r
+  Status = ShellCommandLineParseEx (mIfConfig6CheckList, &ParamPackage, &ProblemParam, TRUE, FALSE);\r
+  if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_COMMAND), mHiiHandle, ProblemParam);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // To handle no option.\r
+  //\r
+  if (!ShellCommandLineGetFlag (ParamPackage, L"-r") && !ShellCommandLineGetFlag (ParamPackage, L"-s") &&\r
+      !ShellCommandLineGetFlag (ParamPackage, L"-?") && !ShellCommandLineGetFlag (ParamPackage, L"-l")) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_LACK_OPTION), mHiiHandle);\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // To handle conflict options.\r
+  //\r
+  if (((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-s"))) ||\r
+      ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) ||\r
+      ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) ||\r
+      ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) ||\r
+      ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) ||\r
+      ((ShellCommandLineGetFlag (ParamPackage, L"-l")) && (ShellCommandLineGetFlag (ParamPackage, L"-?")))) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_CONFLICT_OPTIONS), mHiiHandle);\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // To show the help information of ifconfig6 command.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_HELP), mHiiHandle);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = EFI_INVALID_PARAMETER;\r
+\r
+  Private = AllocateZeroPool (sizeof (IFCONFIG6_PRIVATE_DATA));\r
+\r
+  if (Private == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  InitializeListHead (&Private->IfList);\r
+\r
+  //\r
+  // To get interface name for the list option.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-l")) {\r
+    Private->OpCode = IfConfig6OpList;\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");\r
+    if (ValueStr != NULL) {\r
+      Str             = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr));\r
+      ASSERT (Str != NULL);\r
+\r
+      Str             = StrCpy (Str, ValueStr);\r
+      Private->IfName = Str;\r
+    }\r
+  }\r
+  //\r
+  // To get interface name for the clear option.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-r")) {\r
+    Private->OpCode = IfConfig6OpClear;\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-r");\r
+    if (ValueStr != NULL) {\r
+      Str             = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr));\r
+      ASSERT (Str != NULL);\r
+\r
+      Str             = StrCpy (Str, ValueStr);\r
+      Private->IfName = Str;\r
+    }\r
+  }\r
+  //\r
+  // To get interface name and corresponding Args for the set option.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-s")) {\r
+\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");\r
+    if (ValueStr == NULL) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_INTERFACE), mHiiHandle);\r
+      goto ON_EXIT;\r
+    }\r
+    //\r
+    // To split the configuration into multi-section.\r
+    //\r
+    ArgList         = SplitStrToList (ValueStr, L' ');\r
+    ASSERT (ArgList != NULL);\r
+\r
+    Private->OpCode = IfConfig6OpSet;\r
+    Private->IfName = ArgList->Arg;\r
+\r
+    Private->VarArg = ArgList->Next;\r
+\r
+    if (Private->IfName == NULL || Private->VarArg == NULL) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_COMMAND), mHiiHandle);\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+  //\r
+  // Main process of ifconfig6.\r
+  //\r
+  Status = IfConfig6 (Private);\r
+\r
+ON_EXIT:\r
+\r
+  ShellCommandLineFreeVarList (ParamPackage);\r
+  HiiRemovePackages (mHiiHandle);\r
+  if (Private != NULL)\r
+    IfConfig6Cleanup (Private);\r
+\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.h b/NetworkPkg/Application/IfConfig6/IfConfig6.h
new file mode 100644 (file)
index 0000000..eea7df5
--- /dev/null
@@ -0,0 +1,84 @@
+/** @file\r
+  The interface function declaration of shell application IfConfig6.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _IFCONFIG6_H_\r
+#define _IFCONFIG6_H_\r
+\r
+#define EFI_IFCONFIG6_GUID \\r
+  { \\r
+    0xbab7296b, 0x222c, 0x4408, {0x9e, 0x6c, 0xc2, 0x5c, 0x18, 0x7e, 0xff, 0x33} \\r
+  }\r
+\r
+enum {\r
+  IfConfig6OpList     = 1,\r
+  IfConfig6OpSet      = 2,\r
+  IfConfig6OpClear    = 3\r
+};\r
+\r
+typedef enum {\r
+  VarCheckReserved      = -1,\r
+  VarCheckOk            = 0,\r
+  VarCheckDuplicate,\r
+  VarCheckConflict,\r
+  VarCheckUnknown,\r
+  VarCheckLackValue,\r
+  VarCheckOutOfMem\r
+} VAR_CHECK_CODE;\r
+\r
+typedef enum {\r
+  FlagTypeSingle         = 0,\r
+  FlagTypeNeedVar,\r
+  FlagTypeNeedSet,\r
+  FlagTypeSkipUnknown\r
+} VAR_CHECK_FLAG_TYPE;\r
+\r
+#define MACADDRMAXSIZE    32\r
+#define PREFIXMAXLEN      16 \r
+\r
+typedef struct _IFCONFIG6_INTERFACE_CB {\r
+  EFI_HANDLE                                  NicHandle;\r
+  LIST_ENTRY                                  Link;\r
+  EFI_IP6_CONFIG_PROTOCOL                     *IfCfg;\r
+  EFI_IP6_CONFIG_INTERFACE_INFO               *IfInfo; \r
+  EFI_IP6_CONFIG_INTERFACE_ID                 *IfId;\r
+  EFI_IP6_CONFIG_POLICY                       Policy;\r
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS    Xmits;\r
+  UINT32                                      DnsCnt;\r
+  EFI_IPv6_ADDRESS                            DnsAddr[1];\r
+} IFCONFIG6_INTERFACE_CB;\r
+\r
+typedef struct _ARG_LIST ARG_LIST;\r
+\r
+struct _ARG_LIST {\r
+  ARG_LIST    *Next;\r
+  CHAR16      *Arg;\r
+};\r
+\r
+typedef struct _IFCONFIG6_PRIVATE_DATA {\r
+  EFI_HANDLE  ImageHandle;\r
+  LIST_ENTRY  IfList;\r
+\r
+  UINT32      OpCode;\r
+  CHAR16      *IfName;\r
+  ARG_LIST    *VarArg;\r
+} IFCONFIG6_PRIVATE_DATA;\r
+\r
+typedef struct _VAR_CHECK_ITEM{\r
+  CHAR16                 *FlagStr;\r
+  UINT32                 FlagID;\r
+  UINT32                 ConflictMask;\r
+  VAR_CHECK_FLAG_TYPE    FlagType;\r
+} VAR_CHECK_ITEM;\r
+#endif\r
diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.inf b/NetworkPkg/Application/IfConfig6/IfConfig6.inf
new file mode 100644 (file)
index 0000000..dd3ab64
--- /dev/null
@@ -0,0 +1,52 @@
+## @file\r
+#  Component description file for Shell application IfConfig6.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010006\r
+  BASE_NAME                      = IfConfig6\r
+  FILE_GUID                      = 6F71926E-60CE-428d-AA58-A3D9FB879429\r
+  MODULE_TYPE                    = UEFI_APPLICATION\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = IfConfig6Initialize\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF\r
+#\r
+[Sources]\r
+  IfConfig6Strings.uni\r
+  IfConfig6.c\r
+  IfConfig6.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  ShellPkg/ShellPkg.dec\r
+  \r
+[LibraryClasses]\r
+  BaseLib\r
+  UefiBootServicesTableLib\r
+  UefiApplicationEntryPoint\r
+  BaseMemoryLib\r
+  ShellLib\r
+  MemoryAllocationLib\r
+  DebugLib\r
+  HiiLib\r
+  NetLib\r
+\r
+[Protocols]\r
+  gEfiIp6ServiceBindingProtocolGuid             ## CONSUMS\r
+  gEfiIp6ConfigProtocolGuid                     ## CONSUMS\r
diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni b/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni
new file mode 100644 (file)
index 0000000..a5e7fd0
Binary files /dev/null and b/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni differ
diff --git a/NetworkPkg/Application/IpsecConfig/Delete.c b/NetworkPkg/Application/IpsecConfig/Delete.c
new file mode 100644 (file)
index 0000000..caeb1c8
--- /dev/null
@@ -0,0 +1,110 @@
+/** @file\r
+  The implementation of delete policy entry function in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Indexer.h"\r
+#include "Delete.h"\r
+#include "Match.h"\r
+#include "ForEach.h"\r
+\r
+/**\r
+  Private function to delete entry information in database.\r
+\r
+  @param[in] Selector    The pointer to EFI_IPSEC_CONFIG_SELECTOR structure.\r
+  @param[in] Data        The pointer to Data.\r
+  @param[in] Context     The pointer to DELETE_POLICY_ENTRY_CONTEXT.\r
+\r
+  @retval EFI_ABORTED    Abort the iteration.\r
+  @retval EFI_SUCCESS    Continue the iteration.\r
+**/\r
+EFI_STATUS\r
+DeletePolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR      *Selector,\r
+  IN VOID                           *Data,\r
+  IN DELETE_POLICY_ENTRY_CONTEXT    *Context\r
+  )\r
+{\r
+  if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {\r
+    Context->Status = mIpSecConfig->SetData (\r
+                                      mIpSecConfig,\r
+                                      Context->DataType,\r
+                                      Selector,\r
+                                      NULL,\r
+                                      NULL\r
+                                      );\r
+    //\r
+    // Abort the iteration after the insertion.\r
+    //\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Flush or delete entry information in the database according to datatype.\r
+\r
+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS      Delete entry information successfully.\r
+  @retval EFI_NOT_FOUND    Can't find the specified entry.\r
+  @retval Others           Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+FlushOrDeletePolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN LIST_ENTRY                    *ParamPackage\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  DELETE_POLICY_ENTRY_CONTEXT    Context;\r
+  CONST CHAR16                   *ValueStr;\r
+\r
+  //\r
+  // If user wants to remove all.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-f")) {\r
+    Status = mIpSecConfig->SetData (\r
+                             mIpSecConfig,\r
+                             DataType,\r
+                             NULL,\r
+                             NULL,\r
+                             NULL\r
+                             );\r
+  } else {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");\r
+    if (ValueStr == NULL) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);\r
+      return EFI_NOT_FOUND;\r
+    }\r
+\r
+    Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);\r
+    if (!EFI_ERROR (Status)) {\r
+      Context.DataType  = DataType;\r
+      Context.Status    = EFI_NOT_FOUND;\r
+      ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) DeletePolicyEntry, &Context);\r
+      Status = Context.Status;\r
+\r
+      if (Status == EFI_NOT_FOUND) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);\r
+      } else if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DELETE_FAILED), mHiiHandle, mAppName);\r
+      }\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/Application/IpsecConfig/Delete.h b/NetworkPkg/Application/IpsecConfig/Delete.h
new file mode 100644 (file)
index 0000000..49f3b0d
--- /dev/null
@@ -0,0 +1,42 @@
+/** @file\r
+  The internal structure and function declaration of delete policy entry function\r
+  in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __DELETE_H_\r
+#define __DELETE_H_\r
+\r
+typedef struct {\r
+  EFI_IPSEC_CONFIG_DATA_TYPE    DataType;\r
+  POLICY_ENTRY_INDEXER          Indexer;\r
+  EFI_STATUS                    Status;      //Indicate whether deletion succeeds.\r
+} DELETE_POLICY_ENTRY_CONTEXT;\r
+\r
+/**\r
+  Flush or delete entry information in the database according to datatype.\r
+\r
+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS      Delete entry information successfully.\r
+  @retval EFI_NOT_FOUND    Can't find the specified entry.\r
+  @retval Others           Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+FlushOrDeletePolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN LIST_ENTRY                    *ParamPackage\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Application/IpsecConfig/Dump.c b/NetworkPkg/Application/IpsecConfig/Dump.c
new file mode 100644 (file)
index 0000000..004ab10
--- /dev/null
@@ -0,0 +1,530 @@
+/** @file\r
+  The implementation of dump policy entry function in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Dump.h"\r
+#include "ForEach.h"\r
+#include "Helper.h"\r
+\r
+/**\r
+  Private function called to get the version infomation from an EFI_IP_ADDRESS_INFO structure.\r
+\r
+  @param[in] AddressInfo    The pointer to the EFI_IP_ADDRESS_INFO structure.\r
+\r
+  @return the value of version.\r
+**/\r
+UINTN\r
+GetVerFromAddrInfo (\r
+  IN EFI_IP_ADDRESS_INFO    *AddressInfo\r
+)\r
+{\r
+  if((AddressInfo->PrefixLength <= 32) && (AddressInfo->Address.Addr[1] == 0) &&\r
+     (AddressInfo->Address.Addr[2] == 0) && (AddressInfo->Address.Addr[3] == 0)) {\r
+    return IP_VERSION_4;\r
+  } else {\r
+    return IP_VERSION_6;\r
+  }\r
+}\r
+\r
+/**\r
+  Private function called to get the version information from a EFI_IP_ADDRESS structure.\r
+\r
+  @param[in] Address    The pointer to the EFI_IP_ADDRESS structure.\r
+\r
+  @return The value of the version.\r
+**/\r
+UINTN\r
+GetVerFromIpAddr (\r
+  IN EFI_IP_ADDRESS    *Address\r
+)\r
+{\r
+  if ((Address->Addr[1] == 0) && (Address->Addr[2] == 0) && (Address->Addr[3] == 0)) {\r
+    return IP_VERSION_4;\r
+  } else {\r
+    return IP_VERSION_6;\r
+  }\r
+}\r
+\r
+/**\r
+  Private function called to print an ASCII string in unicode char format.\r
+\r
+  @param[in] Str       The pointer to the ASCII string.\r
+  @param[in] Length    The value of the ASCII string length.\r
+**/\r
+VOID\r
+DumpAsciiString (\r
+  IN CHAR8    *Str,\r
+  IN UINTN    Length\r
+  )\r
+{\r
+  UINTN    Index;\r
+  for (Index = 0; Index < Length; Index++) {\r
+    Print (L"%c", (CHAR16) Str[Index]);\r
+  }\r
+}\r
+\r
+/**\r
+  Private function called to print EFI_IP_ADDRESS_INFO content.\r
+\r
+  @param[in] AddressInfo    The pointer to the EFI_IP_ADDRESS_INFO structure.\r
+**/\r
+VOID\r
+DumpAddressInfo (\r
+  IN EFI_IP_ADDRESS_INFO    *AddressInfo\r
+  )\r
+{\r
+  if (IP_VERSION_4 == GetVerFromAddrInfo (AddressInfo)) {\r
+    Print (\r
+      L"%d.%d.%d.%d",\r
+      (UINTN) AddressInfo->Address.v4.Addr[0],\r
+      (UINTN) AddressInfo->Address.v4.Addr[1],\r
+      (UINTN) AddressInfo->Address.v4.Addr[2],\r
+      (UINTN) AddressInfo->Address.v4.Addr[3]\r
+      );\r
+    if (AddressInfo->PrefixLength != 32) {\r
+      Print (L"/%d", (UINTN) AddressInfo->PrefixLength);\r
+    }\r
+  }\r
+\r
+  if (IP_VERSION_6 == GetVerFromAddrInfo (AddressInfo)) {\r
+    Print (\r
+      L"%x:%x:%x:%x:%x:%x:%x:%x",\r
+      (((UINT16) AddressInfo->Address.v6.Addr[0]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[1]),\r
+      (((UINT16) AddressInfo->Address.v6.Addr[2]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[3]),\r
+      (((UINT16) AddressInfo->Address.v6.Addr[4]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[5]),\r
+      (((UINT16) AddressInfo->Address.v6.Addr[6]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[7]),\r
+      (((UINT16) AddressInfo->Address.v6.Addr[8]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[9]),\r
+      (((UINT16) AddressInfo->Address.v6.Addr[10]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[11]),\r
+      (((UINT16) AddressInfo->Address.v6.Addr[12]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[13]),\r
+      (((UINT16) AddressInfo->Address.v6.Addr[14]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[15])\r
+      );\r
+    if (AddressInfo->PrefixLength != 128) {\r
+      Print (L"/%d", AddressInfo->PrefixLength);\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Private function called to print EFI_IP_ADDRESS content.\r
+\r
+  @param[in] IpAddress    The pointer to the EFI_IP_ADDRESS structure.\r
+**/\r
+VOID\r
+DumpIpAddress (\r
+  IN EFI_IP_ADDRESS    *IpAddress\r
+  )\r
+{\r
+  if (IP_VERSION_4 == GetVerFromIpAddr (IpAddress)) {\r
+    Print (\r
+      L"%d.%d.%d.%d",\r
+      (UINTN) IpAddress->v4.Addr[0],\r
+      (UINTN) IpAddress->v4.Addr[1],\r
+      (UINTN) IpAddress->v4.Addr[2],\r
+      (UINTN) IpAddress->v4.Addr[3]\r
+      );\r
+  }\r
+\r
+  if (IP_VERSION_6 == GetVerFromIpAddr (IpAddress)) {\r
+    Print (\r
+      L"%x:%x:%x:%x:%x:%x:%x:%x",\r
+      (((UINT16) IpAddress->v6.Addr[0]) << 8) | ((UINT16) IpAddress->v6.Addr[1]),\r
+      (((UINT16) IpAddress->v6.Addr[2]) << 8) | ((UINT16) IpAddress->v6.Addr[3]),\r
+      (((UINT16) IpAddress->v6.Addr[4]) << 8) | ((UINT16) IpAddress->v6.Addr[5]),\r
+      (((UINT16) IpAddress->v6.Addr[6]) << 8) | ((UINT16) IpAddress->v6.Addr[7]),\r
+      (((UINT16) IpAddress->v6.Addr[8]) << 8) | ((UINT16) IpAddress->v6.Addr[9]),\r
+      (((UINT16) IpAddress->v6.Addr[10]) << 8) | ((UINT16) IpAddress->v6.Addr[11]),\r
+      (((UINT16) IpAddress->v6.Addr[12]) << 8) | ((UINT16) IpAddress->v6.Addr[13]),\r
+      (((UINT16) IpAddress->v6.Addr[14]) << 8) | ((UINT16) IpAddress->v6.Addr[15])\r
+      );\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Private function called to print EFI_IPSEC_SPD_SELECTOR content.\r
+\r
+  @param[in] Selector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+**/\r
+VOID\r
+DumpSpdSelector (\r
+  IN EFI_IPSEC_SPD_SELECTOR    *Selector\r
+  )\r
+{\r
+  UINT32    Index;\r
+  CHAR16    *Str;\r
+\r
+  for (Index = 0; Index < Selector->LocalAddressCount; Index++) {\r
+    if (Index > 0) {\r
+      Print (L",");\r
+    }\r
+\r
+    DumpAddressInfo (&Selector->LocalAddress[Index]);\r
+  }\r
+\r
+  if (Index == 0) {\r
+    Print (L"localhost");\r
+  }\r
+\r
+  Print (L" -> ");\r
+\r
+  for (Index = 0; Index < Selector->RemoteAddressCount; Index++) {\r
+    if (Index > 0) {\r
+      Print (L",");\r
+    }\r
+\r
+    DumpAddressInfo (&Selector->RemoteAddress[Index]);\r
+  }\r
+\r
+  Str = MapIntegerToString (Selector->NextLayerProtocol, mMapIpProtocol);\r
+  if (Str != NULL) {\r
+    Print (L" %s", Str);\r
+  } else {\r
+    Print (L" proto:%d", (UINTN) Selector->NextLayerProtocol);\r
+  }\r
+\r
+  if ((Selector->NextLayerProtocol == EFI_IP4_PROTO_TCP) || (Selector->NextLayerProtocol == EFI_IP4_PROTO_UDP)) {\r
+    Print (L" port:");\r
+    if (Selector->LocalPort != EFI_IPSEC_ANY_PORT) {\r
+      Print (L"%d", Selector->LocalPort);\r
+      if (Selector->LocalPortRange != 0) {\r
+        Print (L"~%d", (UINTN) Selector->LocalPort + Selector->LocalPortRange);\r
+      }\r
+    } else {\r
+      Print (L"any");\r
+    }\r
+\r
+    Print (L" -> ");\r
+    if (Selector->RemotePort != EFI_IPSEC_ANY_PORT) {\r
+      Print (L"%d", Selector->RemotePort);\r
+      if (Selector->RemotePortRange != 0) {\r
+        Print (L"~%d", (UINTN) Selector->RemotePort + Selector->RemotePortRange);\r
+      }\r
+    } else {\r
+      Print (L"any");\r
+    }\r
+  } else if (Selector->NextLayerProtocol == EFI_IP4_PROTO_ICMP) {\r
+    Print (L" class/code:");\r
+    if (Selector->LocalPort != 0) {\r
+      Print (L"%d", (UINTN) (UINT8) Selector->LocalPort);\r
+    } else {\r
+      Print (L"any");\r
+    }\r
+\r
+    Print (L"/");\r
+    if (Selector->RemotePort != 0) {\r
+      Print (L"%d", (UINTN) (UINT8) Selector->RemotePort);\r
+    } else {\r
+      Print (L"any");\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Print EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA content.\r
+\r
+  @param[in] Selector      The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+  @param[in] Data          The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+  @param[in] EntryIndex    The pointer to the Index in SPD Database.\r
+\r
+  @retval EFI_SUCCESS    Dump SPD information successfully.\r
+**/\r
+EFI_STATUS\r
+DumpSpdEntry (\r
+  IN EFI_IPSEC_SPD_SELECTOR    *Selector,\r
+  IN EFI_IPSEC_SPD_DATA        *Data,\r
+  IN UINTN                     *EntryIndex\r
+  )\r
+{\r
+  BOOLEAN    HasPre;\r
+  CHAR16     DataName[128];\r
+  CHAR16     *String1;\r
+  CHAR16     *String2;\r
+  CHAR16     *String3;\r
+  UINT8      Index;\r
+\r
+  Print (L"%d.", (*EntryIndex)++);\r
+\r
+  //\r
+  // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400\r
+  // Protect  PF:0x34323423 Name:First Entry\r
+  // ext-sequence sequence-overflow fragcheck life:[B0,S1024,H3600]\r
+  // ESP algo1 algo2 Tunnel [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx set]\r
+  //\r
+\r
+  DumpSpdSelector (Selector);\r
+  Print (L"\n  ");\r
+\r
+  Print (L"%s ", MapIntegerToString (Data->Action, mMapIpSecAction));\r
+  Print (L"PF:%08x ", Data->PackageFlag);\r
+\r
+  Index = 0;\r
+  while (Data->Name[Index] != 0) {\r
+    DataName[Index] = (CHAR16) Data->Name[Index];\r
+    Index++;\r
+    ASSERT (Index < 128);\r
+  }\r
+  DataName[Index] = L'\0';\r
+\r
+  Print (L"Name:%s", DataName);\r
+\r
+  if (Data->Action == EfiIPsecActionProtect) {\r
+    Print (L"\n  ");\r
+    if (Data->ProcessingPolicy->ExtSeqNum) {\r
+      Print (L"ext-sequence ");\r
+    }\r
+\r
+    if (Data->ProcessingPolicy->SeqOverflow) {\r
+      Print (L"sequence-overflow ");\r
+    }\r
+\r
+    if (Data->ProcessingPolicy->FragCheck) {\r
+      Print (L"fragment-check ");\r
+    }\r
+\r
+    HasPre = FALSE;\r
+    if (Data->ProcessingPolicy->SaLifetime.ByteCount != 0) {\r
+      Print (HasPre ? L"," : L"life:[");\r
+      Print (L"%lxB", Data->ProcessingPolicy->SaLifetime.ByteCount);\r
+      HasPre = TRUE;\r
+    }\r
+\r
+    if (Data->ProcessingPolicy->SaLifetime.SoftLifetime != 0) {\r
+      Print (HasPre ? L"," : L"life:[");\r
+      Print (L"%lxs", Data->ProcessingPolicy->SaLifetime.SoftLifetime);\r
+      HasPre = TRUE;\r
+    }\r
+\r
+    if (Data->ProcessingPolicy->SaLifetime.HardLifetime != 0) {\r
+      Print (HasPre ? L"," : L"life:[");\r
+      Print (L"%lxS", Data->ProcessingPolicy->SaLifetime.HardLifetime);\r
+      HasPre = TRUE;\r
+    }\r
+\r
+    if (HasPre) {\r
+      Print (L"]");\r
+    }\r
+\r
+    if (HasPre || Data->ProcessingPolicy->ExtSeqNum ||\r
+        Data->ProcessingPolicy->SeqOverflow || Data->ProcessingPolicy->FragCheck) {\r
+      Print (L"\n  ");\r
+    }\r
+\r
+    String1 = MapIntegerToString (Data->ProcessingPolicy->Proto, mMapIpSecProtocol);\r
+    String2 = MapIntegerToString (Data->ProcessingPolicy->AuthAlgoId, mMapAuthAlgo);\r
+    String3 = MapIntegerToString (Data->ProcessingPolicy->EncAlgoId, mMapEncAlgo);\r
+    Print (\r
+      L"%s Auth:%s Encrypt:%s ",\r
+      String1,\r
+      String2,\r
+      String3\r
+      );\r
+\r
+    Print (L"%s ", MapIntegerToString (Data->ProcessingPolicy->Mode, mMapIpSecMode));\r
+    if (Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {\r
+      Print (L"[");\r
+      DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress);\r
+      Print (L" -> ");\r
+      DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);\r
+      Print (L" %s]", MapIntegerToString (Data->ProcessingPolicy->TunnelOption->DF, mMapDfOption));\r
+    }\r
+  }\r
+\r
+  Print (L"\n");\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Print EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA content.\r
+\r
+  @param[in] SaId          The pointer to the EFI_IPSEC_SA_ID structure.\r
+  @param[in] Data          The pointer to the EFI_IPSEC_SA_DATA structure.\r
+  @param[in] EntryIndex    The pointer to the Index in the SAD Database.\r
+\r
+  @retval EFI_SUCCESS    Dump SAD information successfully.\r
+**/\r
+EFI_STATUS\r
+DumpSadEntry (\r
+  IN EFI_IPSEC_SA_ID      *SaId,\r
+  IN EFI_IPSEC_SA_DATA    *Data,\r
+  IN UINTN                *EntryIndex\r
+  )\r
+{\r
+  BOOLEAN    HasPre;\r
+  CHAR16     *String1;\r
+  CHAR16     *String2;\r
+\r
+  //\r
+  // SPI:1234 ESP Destination:xxx.xxx.xxx.xxx\r
+  //  Mode:Transport SeqNum:134 AntiReplayWin:64 life:[0B,1023s,3400S] PathMTU:34\r
+  //  Auth:xxxx/password Encrypt:yyyy/password\r
+  //  xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400\r
+  //\r
+\r
+  Print (L"%d.", (*EntryIndex)++);\r
+  Print (L"0x%x %s ", (UINTN) SaId->Spi, MapIntegerToString (SaId->Proto, mMapIpSecProtocol));\r
+  Print (L"Destination:");\r
+  DumpIpAddress (&SaId->DestAddress);\r
+  Print (L"\n");\r
+\r
+  Print (\r
+    L"  Mode:%s SeqNum:%lx AntiReplayWin:%d ",\r
+    MapIntegerToString (Data->Mode, mMapIpSecMode),\r
+    Data->SNCount,\r
+    (UINTN) Data->AntiReplayWindows\r
+    );\r
+\r
+  HasPre = FALSE;\r
+  if (Data->SaLifetime.ByteCount != 0) {\r
+    Print (HasPre ? L"," : L"life:[");\r
+    Print (L"%lxB", Data->SaLifetime.ByteCount);\r
+    HasPre = TRUE;\r
+  }\r
+\r
+  if (Data->SaLifetime.SoftLifetime != 0) {\r
+    Print (HasPre ? L"," : L"life:[");\r
+    Print (L"%lxs", Data->SaLifetime.SoftLifetime);\r
+    HasPre = TRUE;\r
+  }\r
+\r
+  if (Data->SaLifetime.HardLifetime != 0) {\r
+    Print (HasPre ? L"," : L"life:[");\r
+    Print (L"%lxS", Data->SaLifetime.HardLifetime);\r
+    HasPre = TRUE;\r
+  }\r
+\r
+  if (HasPre) {\r
+    Print (L"] ");\r
+  }\r
+\r
+  Print (L"PathMTU:%d\n", (UINTN) Data->PathMTU);\r
+\r
+  if (SaId->Proto == EfiIPsecAH) {\r
+    Print (\r
+      L"  Auth:%s/%s\n",\r
+      MapIntegerToString (Data->AlgoInfo.AhAlgoInfo.AuthAlgoId, mMapAuthAlgo),\r
+      Data->AlgoInfo.AhAlgoInfo.AuthKey\r
+      );\r
+  } else {\r
+    String1 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, mMapAuthAlgo);\r
+    String2 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.EncAlgoId, mMapEncAlgo);\r
+    Print (\r
+      L"  Auth:%s/%s Encrypt:%s/%s\n",\r
+      String1,\r
+      Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+      String2,\r
+      Data->AlgoInfo.EspAlgoInfo.EncKey\r
+      );\r
+  }\r
+\r
+  if (Data->SpdSelector != NULL) {\r
+    Print (L"  ");\r
+    DumpSpdSelector (Data->SpdSelector);\r
+    Print (L"\n");\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Print EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA content.\r
+\r
+  @param[in] PadId         The pointer to the EFI_IPSEC_PAD_ID structure.\r
+  @param[in] Data          The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+  @param[in] EntryIndex    The pointer to the Index in the PAD Database.\r
+\r
+  @retval EFI_SUCCESS    Dump PAD information successfully.\r
+**/\r
+EFI_STATUS\r
+DumpPadEntry (\r
+  IN EFI_IPSEC_PAD_ID      *PadId,\r
+  IN EFI_IPSEC_PAD_DATA    *Data,\r
+  IN UINTN                 *EntryIndex\r
+  )\r
+{\r
+  CHAR16    *String1;\r
+  CHAR16    *String2;\r
+\r
+  //\r
+  // ADDR:10.23.17.34/15\r
+  // IDEv1 PreSharedSecret IKE-ID\r
+  // password\r
+  //\r
+\r
+  Print (L"%d.", (*EntryIndex)++);\r
+\r
+  if (PadId->PeerIdValid) {\r
+    Print (L"ID:%s", PadId->Id.PeerId);\r
+  } else {\r
+    Print (L"ADDR:");\r
+    DumpAddressInfo (&PadId->Id.IpAddress);\r
+  }\r
+\r
+  Print (L"\n");\r
+\r
+  String1 = MapIntegerToString (Data->AuthProtocol, mMapAuthProto);\r
+  String2 = MapIntegerToString (Data->AuthMethod, mMapAuthMethod);\r
+  Print (\r
+    L"  %s %s",\r
+    String1,\r
+    String2\r
+    );\r
+\r
+  if (Data->IkeIdFlag) {\r
+    Print (L"IKE-ID");\r
+  }\r
+\r
+  Print (L"\n");\r
+\r
+  if (Data->AuthData != NULL) {\r
+    DumpAsciiString (Data->AuthData, Data->AuthDataSize);\r
+    Print (L"\n");\r
+  }\r
+\r
+  if (Data->RevocationData != NULL) {\r
+    Print (L"  %s\n", Data->RevocationData);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+}\r
+\r
+VISIT_POLICY_ENTRY  mDumpPolicyEntry[] = {\r
+  (VISIT_POLICY_ENTRY) DumpSpdEntry,\r
+  (VISIT_POLICY_ENTRY) DumpSadEntry,\r
+  (VISIT_POLICY_ENTRY) DumpPadEntry\r
+};\r
+\r
+/**\r
+  Print all entry information in the database according to datatype.\r
+\r
+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS    Dump all information successfully.\r
+  @retval Others         Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+ListPolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN LIST_ENTRY                    *ParamPackage\r
+  )\r
+{\r
+  UINTN  EntryIndex;\r
+\r
+  EntryIndex = 0;\r
+  return ForeachPolicyEntry (DataType, mDumpPolicyEntry[DataType], &EntryIndex);\r
+}\r
+\r
diff --git a/NetworkPkg/Application/IpsecConfig/Dump.h b/NetworkPkg/Application/IpsecConfig/Dump.h
new file mode 100644 (file)
index 0000000..3c475dd
--- /dev/null
@@ -0,0 +1,34 @@
+/** @file\r
+  The function declaration of dump policy entry function in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _DUMP_H_\r
+#define _DUMP_H_\r
+\r
+/**\r
+  Print all entry information in the database according to datatype.\r
+\r
+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS    Dump all information successfully.\r
+  @retval Others         Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+ListPolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN LIST_ENTRY                    *ParamPackage\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Application/IpsecConfig/ForEach.c b/NetworkPkg/Application/IpsecConfig/ForEach.c
new file mode 100644 (file)
index 0000000..7b3b9b1
--- /dev/null
@@ -0,0 +1,115 @@
+/** @file\r
+  The implementation to go through each entry in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "ForEach.h"\r
+\r
+\r
+/**\r
+  Enumerate all entries in the database to execute specified operations according to datatype.\r
+\r
+  @param[in] DataType    The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] Routine     The pointer to the function of a specified operation.\r
+  @param[in] Context     The pointer to the context of a function.\r
+\r
+  @retval EFI_SUCCESS    Execute specified operation successfully.\r
+**/\r
+EFI_STATUS\r
+ForeachPolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN VISIT_POLICY_ENTRY            Routine,\r
+  IN VOID                          *Context\r
+  )\r
+{\r
+  EFI_STATUS                   GetNextStatus;\r
+  EFI_STATUS                   GetDataStatus;\r
+  EFI_IPSEC_CONFIG_SELECTOR    *Selector;\r
+  VOID                         *Data;\r
+  UINTN                        SelectorSize;\r
+  UINTN                        DataSize;\r
+  BOOLEAN                      FirstGetNext;\r
+\r
+  FirstGetNext = TRUE;\r
+  SelectorSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR);\r
+  Selector     = AllocateZeroPool (SelectorSize);\r
+\r
+  DataSize     = 0;\r
+  Data         = NULL;\r
+\r
+  while (TRUE) {\r
+    GetNextStatus = mIpSecConfig->GetNextSelector (\r
+                                    mIpSecConfig,\r
+                                    DataType,\r
+                                    &SelectorSize,\r
+                                    Selector\r
+                                    );\r
+    if (GetNextStatus == EFI_BUFFER_TOO_SMALL) {\r
+      gBS->FreePool (Selector);\r
+      Selector = FirstGetNext ? AllocateZeroPool (SelectorSize) : AllocatePool (SelectorSize);\r
+\r
+      GetNextStatus = mIpSecConfig->GetNextSelector (\r
+                                      mIpSecConfig,\r
+                                      DataType,\r
+                                      &SelectorSize,\r
+                                      Selector\r
+                                      );\r
+    }\r
+\r
+    if (EFI_ERROR (GetNextStatus)) {\r
+      break;\r
+    }\r
+\r
+    FirstGetNext = FALSE;\r
+\r
+    GetDataStatus = mIpSecConfig->GetData (\r
+                                    mIpSecConfig,\r
+                                    DataType,\r
+                                    Selector,\r
+                                    &DataSize,\r
+                                    Data\r
+                                    );\r
+    if (GetDataStatus == EFI_BUFFER_TOO_SMALL) {\r
+      if (Data != NULL) {\r
+        gBS->FreePool (Data);\r
+      }\r
+\r
+      Data = AllocateZeroPool (DataSize);\r
+      GetDataStatus = mIpSecConfig->GetData (\r
+                                      mIpSecConfig,\r
+                                      DataType,\r
+                                      Selector,\r
+                                      &DataSize,\r
+                                      Data\r
+                                      );\r
+    }\r
+\r
+    ASSERT_EFI_ERROR (GetDataStatus);\r
+\r
+    if (EFI_ERROR (Routine (Selector, Data, Context))) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (Data != NULL) {\r
+    gBS->FreePool (Data);\r
+  }\r
+\r
+  if (Selector != NULL) {\r
+    gBS->FreePool (Selector);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
diff --git a/NetworkPkg/Application/IpsecConfig/ForEach.h b/NetworkPkg/Application/IpsecConfig/ForEach.h
new file mode 100644 (file)
index 0000000..fc30930
--- /dev/null
@@ -0,0 +1,54 @@
+/** @file\r
+  The internal structure and function declaration of the implementation\r
+  to go through each entry in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _FOREACH_H_\r
+#define _FOREACH_H_\r
+\r
+/**\r
+  The prototype for the DumpSpdEntry()/DumpSadEntry()/DumpPadEntry().\r
+  Print EFI_IPSEC_CONFIG_SELECTOR and corresponding content.\r
+\r
+  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.\r
+  @param[in] Data        The pointer to the corresponding data.\r
+  @param[in] Context     The pointer to the Index in SPD/SAD/PAD Database.\r
+\r
+  @retval EFI_SUCCESS    Dump SPD/SAD/PAD information successfully.\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*VISIT_POLICY_ENTRY) (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR    *Selector,\r
+  IN VOID                         *Data,\r
+  IN VOID                         *Context\r
+  );\r
+\r
+/**\r
+  Enumerate all entry in the database to execute a specified operation according to datatype.\r
+\r
+  @param[in] DataType    The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] Routine     The pointer to function of a specified operation.\r
+  @param[in] Context     The pointer to the context of a function.\r
+\r
+  @retval EFI_SUCCESS    Execute specified operation successfully.\r
+**/\r
+EFI_STATUS\r
+ForeachPolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN VISIT_POLICY_ENTRY            Routine,\r
+  IN VOID                          *Context\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Application/IpsecConfig/Helper.c b/NetworkPkg/Application/IpsecConfig/Helper.c
new file mode 100644 (file)
index 0000000..5013ad9
--- /dev/null
@@ -0,0 +1,419 @@
+/** @file\r
+  The assistant function implementation for IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Helper.h"\r
+\r
+/**\r
+  Helper function called to change an input parameter in the string format to a number.\r
+\r
+  @param[in]      FlagStr         The pointer to the flag string.\r
+  @param[in]      Maximum         Greatest value number.\r
+  @param[in, out] ValuePtr        The pointer to the input parameter in string format.\r
+  @param[in]      ByteCount       The valid byte count\r
+  @param[in]      Map             The pointer to the STR2INT table.\r
+  @param[in]      ParamPackage    The pointer to the ParamPackage list.\r
+  @param[in]      FormatMask      The bit mask.\r
+                                  BIT 0 set indicates the value of a flag might be a number.\r
+                                  BIT 1 set indicates the value of a flag might be a string that needs to be looked up.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_NOT_FOUND            The input parameter can't be found.\r
+  @retval EFI_INVALID_PARAMETER    The input parameter is an invalid input.\r
+**/\r
+EFI_STATUS\r
+GetNumber (\r
+  IN     CHAR16        *FlagStr,\r
+  IN     UINT64        Maximum,\r
+  IN OUT VOID          *ValuePtr,\r
+  IN     UINTN         ByteCount,\r
+  IN     STR2INT       *Map,\r
+  IN     LIST_ENTRY    *ParamPackage,\r
+  IN     UINT32        FormatMask\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  UINT64          Value64;\r
+  BOOLEAN         Converted;\r
+  UINTN           Index;\r
+  CONST CHAR16    *ValueStr;\r
+\r
+  ASSERT (FormatMask & (FORMAT_NUMBER | FORMAT_STRING));\r
+\r
+  Converted = FALSE;\r
+  Value64   = 0;\r
+  ValueStr  = ShellCommandLineGetValue (ParamPackage, FlagStr);\r
+\r
+  if (ValueStr == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  } else {\r
+    //\r
+    // Try to convert to integer directly if MaybeNumber is TRUE.\r
+    //\r
+    if ((FormatMask & FORMAT_NUMBER) != 0) {\r
+      Value64 = StrToUInteger (ValueStr, &Status);\r
+      if (!EFI_ERROR (Status)) {\r
+        //\r
+        // Convert successfully.\r
+        //\r
+        if (Value64 > Maximum) {\r
+          //\r
+          // But the result is invalid\r
+          //\r
+          ShellPrintHiiEx (\r
+            -1,\r
+            -1,\r
+            NULL,\r
+            STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+            mHiiHandle,\r
+            mAppName,\r
+            FlagStr,\r
+            ValueStr\r
+            );\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+\r
+        Converted = TRUE;\r
+      }\r
+    }\r
+\r
+    if (!Converted && ((FormatMask & FORMAT_STRING) != 0)) {\r
+      //\r
+      // Convert falied, so use String->Integer map.\r
+      //\r
+      Value64 = MapStringToInteger (ValueStr, Map);\r
+      if (Value64 == (UINT32) -1) {\r
+        //\r
+        // Cannot find the string in the map.\r
+        //\r
+        ShellPrintHiiEx (\r
+          -1,\r
+          -1,\r
+          NULL,\r
+          STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+          mHiiHandle,\r
+          mAppName,\r
+          FlagStr,\r
+          ValueStr\r
+          );\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ACCEPT_PARAMETERS), mHiiHandle);\r
+        for (Index = 0; Map[Index].String != NULL; Index++) {\r
+          Print (L" %s", Map[Index].String);\r
+        }\r
+\r
+        Print (L"\n");\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+\r
+    CopyMem (ValuePtr, &Value64, ByteCount);\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+/**\r
+  Helper function called to convert a string containing an Ipv4 or Ipv6 Internet Protocol address\r
+  into a proper address for the EFI_IP_ADDRESS structure.\r
+\r
+  @param[in]  Ptr    The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.\r
+  @param[out] Ip     The pointer to the EFI_IP_ADDRESS structure to contain the result.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EfiInetAddr2 (\r
+  IN  CHAR16            *Ptr,\r
+  OUT EFI_IP_ADDRESS    *Ip\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+\r
+  if ((Ptr == NULL) || (Ip == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Parse the input address as Ipv4 Address first.\r
+  //\r
+  Status = NetLibStrToIp4 (Ptr, &Ip->v4);\r
+  if (!EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = NetLibStrToIp6 (Ptr, &Ip->v6);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Helper function called to calculate the prefix length associated with the string\r
+  containing an Ipv4 or Ipv6 Internet Protocol address.\r
+\r
+  @param[in]  Ptr     The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.\r
+  @param[out] Addr    The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid parameter.\r
+  @retval Others                   Other mistake case.\r
+**/\r
+EFI_STATUS\r
+EfiInetAddrRange (\r
+  IN  CHAR16                 *Ptr,\r
+  OUT EFI_IP_ADDRESS_INFO    *Addr\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+\r
+  if ((Ptr == NULL) || (Addr == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = NetLibStrToIp4 (Ptr, &Addr->Address.v4);\r
+  if (!EFI_ERROR (Status)) {\r
+    if ((UINT32)(*Addr->Address.v4.Addr) == 0) {\r
+      Addr->PrefixLength = 0;\r
+    } else {\r
+      Addr->PrefixLength = 32;\r
+    }\r
+    return Status;\r
+  }\r
+\r
+  Status = NetLibStrToIp6andPrefix (Ptr, &Addr->Address.v6, &Addr->PrefixLength);\r
+  if (!EFI_ERROR (Status) && (Addr->PrefixLength == 0xFF)) {\r
+    Addr->PrefixLength = 128;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Helper function called to calculate the port range associated with the string.\r
+\r
+  @param[in]  Ptr          The pointer to the string containing a port and range.\r
+  @param[out] Port         The pointer to the Port to contain the result.\r
+  @param[out] PortRange    The pointer to the PortRange to contain the result.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid parameter.\r
+  @retval Others                   Other mistake case.\r
+**/\r
+EFI_STATUS\r
+EfiInetPortRange (\r
+  IN  CHAR16    *Ptr,\r
+  OUT UINT16    *Port,\r
+  OUT UINT16    *PortRange\r
+  )\r
+{\r
+  CHAR16        *BreakPtr;\r
+  CHAR16        Ch;\r
+  EFI_STATUS    Status;\r
+\r
+  for (BreakPtr = Ptr; (*BreakPtr != L'\0') && (*BreakPtr != L':'); BreakPtr++) {\r
+    ;\r
+  }\r
+\r
+  Ch        = *BreakPtr;\r
+  *BreakPtr = L'\0';\r
+  *Port     = (UINT16) StrToUInteger (Ptr, &Status);\r
+  *BreakPtr = Ch;\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  *PortRange = 0;\r
+  if (*BreakPtr == L':') {\r
+    BreakPtr++;\r
+    *PortRange = (UINT16) StrToUInteger (BreakPtr, &Status);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    if (*PortRange < *Port) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    *PortRange = (UINT16) (*PortRange - *Port);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Helper function called to transfer a string to an unsigned integer.\r
+\r
+  @param[in]  Str       The pointer to the string.\r
+  @param[out] Status    The operation status.\r
+\r
+  @return The integer value of converted Str.\r
+**/\r
+UINT64\r
+StrToUInteger (\r
+  IN  CONST CHAR16    *Str,\r
+  OUT EFI_STATUS      *Status\r
+  )\r
+{\r
+  UINT64    Value;\r
+  UINT64    NewValue;\r
+  CHAR16    *StrTail;\r
+  CHAR16    Char;\r
+  UINTN     Base;\r
+  UINTN     Len;\r
+\r
+  Base    = 10;\r
+  Value   = 0;\r
+  *Status = EFI_ABORTED;\r
+\r
+  //\r
+  // Skip leading white space.\r
+  //\r
+  while ((*Str != 0) && (*Str == ' ')) {\r
+    Str++;\r
+  }\r
+  //\r
+  // For NULL Str, just return.\r
+  //\r
+  if (*Str == 0) {\r
+    return 0;\r
+  }\r
+  //\r
+  // Skip white space in tail.\r
+  //\r
+  Len     = StrLen (Str);\r
+  StrTail = (CHAR16 *) (Str + Len - 1);\r
+  while (*StrTail == ' ') {\r
+    *StrTail = 0;\r
+    StrTail--;\r
+  }\r
+\r
+  Len = StrTail - Str + 1;\r
+\r
+  //\r
+  // Check hex prefix '0x'.\r
+  //\r
+  if ((Len >= 2) && (*Str == '0') && ((*(Str + 1) == 'x') || (*(Str + 1) == 'X'))) {\r
+    Str += 2;\r
+    Len -= 2;\r
+    Base = 16;\r
+  }\r
+\r
+  if (Len == 0) {\r
+    return 0;\r
+  }\r
+  //\r
+  // Convert the string to value.\r
+  //\r
+  for (; Str <= StrTail; Str++) {\r
+\r
+    Char = *Str;\r
+\r
+    if (Base == 16) {\r
+      if (RShiftU64 (Value, 60) != 0) {\r
+        //\r
+        // Overflow here x16.\r
+        //\r
+        return 0;\r
+      }\r
+\r
+      NewValue = LShiftU64 (Value, 4);\r
+    } else {\r
+      if (RShiftU64 (Value, 61) != 0) {\r
+        //\r
+        // Overflow here x8.\r
+        //\r
+        return 0;\r
+      }\r
+\r
+      NewValue  = LShiftU64 (Value, 3);\r
+      Value     = LShiftU64 (Value, 1);\r
+      NewValue += Value;\r
+      if (NewValue < Value) {\r
+        //\r
+        // Overflow here.\r
+        //\r
+        return 0;\r
+      }\r
+    }\r
+\r
+    Value = NewValue;\r
+\r
+    if ((Base == 16) && (Char >= 'a') && (Char <= 'f')) {\r
+      Char = (CHAR16) (Char - 'a' + 'A');\r
+    }\r
+\r
+    if ((Base == 16) && (Char >= 'A') && (Char <= 'F')) {\r
+      Value += (Char - 'A') + 10;\r
+    } else if ((Char >= '0') && (Char <= '9')) {\r
+      Value += (Char - '0');\r
+    } else {\r
+      //\r
+      // Unexpected Char encountered.\r
+      //\r
+      return 0;\r
+    }\r
+  }\r
+\r
+  *Status = EFI_SUCCESS;\r
+  return Value;\r
+}\r
+\r
+/**\r
+  Helper function called to transfer a string to an unsigned integer according to the map table.\r
+\r
+  @param[in] Str    The pointer to the string.\r
+  @param[in] Map    The pointer to the map table.\r
+\r
+  @return The integer value of converted Str. If not found, then return -1.\r
+**/\r
+UINT32\r
+MapStringToInteger (\r
+  IN CONST CHAR16    *Str,\r
+  IN STR2INT         *Map\r
+  )\r
+{\r
+  STR2INT       *Item;\r
+\r
+  for (Item = Map; Item->String != NULL; Item++) {\r
+    if (StrCmp (Item->String, Str) == 0) {\r
+      return Item->Integer;\r
+    }\r
+  }\r
+\r
+  return (UINT32) -1;\r
+}\r
+\r
+/**\r
+  Helper function called to transfer an unsigned integer to a string according to the map table.\r
+\r
+  @param[in] Integer    The pointer to the string.\r
+  @param[in] Map        The pointer to the map table.\r
+\r
+  @return The converted Str. If not found, then return NULL.\r
+**/\r
+CHAR16 *\r
+MapIntegerToString (\r
+  IN UINT32     Integer,\r
+  IN STR2INT    *Map\r
+  )\r
+{\r
+  STR2INT    *Item;\r
+\r
+  for (Item = Map; Item->String != NULL; Item++) {\r
+    if (Integer == Item->Integer) {\r
+      return Item->String;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
diff --git a/NetworkPkg/Application/IpsecConfig/Helper.h b/NetworkPkg/Application/IpsecConfig/Helper.h
new file mode 100644 (file)
index 0000000..d893145
--- /dev/null
@@ -0,0 +1,143 @@
+/** @file\r
+  The assistant function declaration for IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _HELPER_H_\r
+#define _HELPER_H_\r
+\r
+#define  FORMAT_NUMBER 0x1\r
+#define  FORMAT_STRING 0x2\r
+\r
+/**\r
+  Helper function called to change input parameter in string format to number.\r
+\r
+  @param[in]      FlagStr         The pointer to the flag string.\r
+  @param[in]      Maximum         most value number.\r
+  @param[in, out] ValuePtr        The pointer to the input parameter in string format.\r
+  @param[in]      ByteCount       The valid byte count\r
+  @param[in]      Map             The pointer to the STR2INT table.\r
+  @param[in]      ParamPackage    The pointer to the ParamPackage list.\r
+  @param[in]      FormatMask      The bit mask.\r
+                                  BIT 0 set indicates the value of flag might be number.\r
+                                  BIT 1 set indicates the value of flag might be a string that needs to be looked up.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_NOT_FOUND            The input parameter can't be found.\r
+  @retval EFI_INVALID_PARAMETER    The input parameter is an invalid input.\r
+**/\r
+EFI_STATUS\r
+GetNumber (\r
+  IN     CHAR16        *FlagStr,\r
+  IN     UINT64        Maximum,\r
+  IN OUT VOID          *ValuePtr,\r
+  IN     UINTN         ByteCount,\r
+  IN     STR2INT       *Map,\r
+  IN     LIST_ENTRY    *ParamPackage,\r
+  IN     UINT32        FormatMask\r
+  );\r
+\r
+/**\r
+  Helper function called to convert a string containing an (Ipv4) Internet Protocol dotted address\r
+  into a proper address for the EFI_IP_ADDRESS structure.\r
+\r
+  @param[in]  Ptr    The pointer to the string containing an (Ipv4) Internet Protocol dotted address.\r
+  @param[out] Ip     The pointer to the Ip address structure to contain the result.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EfiInetAddr2 (\r
+  IN  CHAR16            *Ptr,\r
+  OUT EFI_IP_ADDRESS    *Ip\r
+  );\r
+\r
+/**\r
+  Helper function called to calculate the prefix length associated with the string\r
+  containing an Ipv4 or Ipv6 Internet Protocol address.\r
+\r
+  @param[in]  Ptr     The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.\r
+  @param[out] Addr    The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid parameter.\r
+  @retval Others                   Other mistake case.\r
+**/\r
+EFI_STATUS\r
+EfiInetAddrRange (\r
+  IN  CHAR16                 *Ptr,\r
+  OUT EFI_IP_ADDRESS_INFO    *Addr\r
+  );\r
+\r
+/**\r
+  Helper function called to calculate the port range associated with the string.\r
+\r
+  @param[in]  Ptr          The pointer to the string containing a port and range.\r
+  @param[out] Port         The pointer to the Port to contain the result.\r
+  @param[out] PortRange    The pointer to the PortRange to contain the result.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid parameter.\r
+  @retval Others                   Other mistake case.\r
+**/\r
+EFI_STATUS\r
+EfiInetPortRange (\r
+  IN  CHAR16    *Ptr,\r
+  OUT UINT16    *Port,\r
+  OUT UINT16    *PortRange\r
+  );\r
+\r
+/**\r
+  Helper function called to transfer a string to an unsigned integer.\r
+\r
+  @param[in]  Str       The pointer to the string.\r
+  @param[out] Status    The operation status.\r
+\r
+  @return The integer value of a converted str.\r
+**/\r
+UINT64\r
+StrToUInteger (\r
+  IN  CONST CHAR16    *Str,\r
+  OUT EFI_STATUS      *Status\r
+  );\r
+\r
+/**\r
+  Helper function called to transfer a string to an unsigned integer according to the map table.\r
+\r
+  @param[in] Str    The pointer to the string.\r
+  @param[in] Map    The pointer to the map table.\r
+\r
+  @return The integer value of converted str. If not found, then return -1.\r
+**/\r
+UINT32\r
+MapStringToInteger (\r
+  IN CONST CHAR16    *Str,\r
+  IN STR2INT         *Map\r
+  );\r
+\r
+/**\r
+  Helper function called to transfer an unsigned integer to a string according to the map table.\r
+\r
+  @param[in] Integer    The pointer to the string.\r
+  @param[in] Map        The pointer to the map table.\r
+\r
+  @return The converted str. If not found, then return NULL.\r
+**/\r
+CHAR16 *\r
+MapIntegerToString (\r
+  IN UINT32     Integer,\r
+  IN STR2INT    *Map\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Application/IpsecConfig/Indexer.c b/NetworkPkg/Application/IpsecConfig/Indexer.c
new file mode 100644 (file)
index 0000000..1762bbe
--- /dev/null
@@ -0,0 +1,248 @@
+/** @file\r
+  The implementation of construct ENTRY_INDEXER in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Indexer.h"\r
+#include "Helper.h"\r
+\r
+/**\r
+  Fill in SPD_ENTRY_INDEXER through ParamPackage list.\r
+\r
+  @param[in, out] Indexer         The pointer to the SPD_ENTRY_INDEXER structure.\r
+  @param[in]      ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS    Filled in SPD_ENTRY_INDEXER successfully.\r
+**/\r
+EFI_STATUS\r
+ConstructSpdIndexer (\r
+  IN OUT SPD_ENTRY_INDEXER    *Indexer,\r
+  IN     LIST_ENTRY           *ParamPackage\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  UINT64          Value64;\r
+  CONST CHAR16    *ValueStr;\r
+\r
+  ValueStr = NULL;\r
+\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");\r
+  } else {\r
+    ASSERT (FALSE);\r
+  }\r
+\r
+  ASSERT (ValueStr != NULL);\r
+\r
+  Value64 = StrToUInteger (ValueStr, &Status);\r
+  if (!EFI_ERROR (Status)) {\r
+    Indexer->Index = (UINTN) Value64;\r
+    Indexer->Name  = NULL;\r
+  } else {\r
+    UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) Indexer->Name);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Fill in SAD_ENTRY_INDEXER through ParamPackage list.\r
+\r
+  @param[in, out] Indexer         The pointer to the SAD_ENTRY_INDEXER structure.\r
+  @param[in]      ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS              Filled in SPD_ENTRY_INDEXER successfully.\r
+  @retval EFI_INVALID_PARAMETER    The mistaken user input in ParamPackage list.\r
+**/\r
+EFI_STATUS\r
+ConstructSadIndexer (\r
+  IN OUT SAD_ENTRY_INDEXER    *Indexer,\r
+  IN     LIST_ENTRY           *ParamPackage\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  EFI_STATUS      Status1;\r
+  UINT64          Value64;\r
+  CONST CHAR16    *ValueStr;\r
+\r
+  ValueStr = NULL;\r
+\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");\r
+  } else {\r
+    ASSERT (FALSE);\r
+  }\r
+\r
+  ASSERT (ValueStr != NULL);\r
+\r
+  Value64 = StrToUInteger (ValueStr, &Status);\r
+  if (!EFI_ERROR (Status)) {\r
+    Indexer->Index = (UINTN) Value64;\r
+    ZeroMem (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID));\r
+  } else {\r
+    if ((!ShellCommandLineGetFlag (ParamPackage, L"--lookup-spi")) ||\r
+        (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-ipsec-proto")) ||\r
+        (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-dest"))) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--lookup-spi --lookup-ipsec-proto --lookup-dest"\r
+        );\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Status = GetNumber (\r
+              L"--lookup-spi",\r
+              (UINT32) -1,\r
+              &Indexer->SaId.Spi,\r
+              sizeof (UINT32),\r
+              NULL,\r
+              ParamPackage,\r
+              FORMAT_NUMBER\r
+              );\r
+    Status1 = GetNumber (\r
+                L"--lookup-ipsec-proto",\r
+                0,\r
+                &Indexer->SaId.Proto,\r
+                sizeof (EFI_IPSEC_PROTOCOL_TYPE),\r
+                mMapIpSecProtocol,\r
+                ParamPackage,\r
+                FORMAT_STRING\r
+                );\r
+\r
+    if (EFI_ERROR (Status) || EFI_ERROR (Status1)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-dest");\r
+    ASSERT (ValueStr != NULL);\r
+\r
+    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &Indexer->SaId.DestAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--lookup-dest",\r
+        ValueStr\r
+        );\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Fill in PAD_ENTRY_INDEXER through ParamPackage list.\r
+\r
+  @param[in, out] Indexer         The pointer to the PAD_ENTRY_INDEXER structure.\r
+  @param[in]      ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS              Filled in PAD_ENTRY_INDEXER successfully.\r
+  @retval EFI_INVALID_PARAMETER    The mistaken user input in ParamPackage list.\r
+**/\r
+EFI_STATUS\r
+ConstructPadIndexer (\r
+  IN OUT PAD_ENTRY_INDEXER    *Indexer,\r
+  IN     LIST_ENTRY           *ParamPackage\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  UINT64          Value64;\r
+  CONST CHAR16    *ValueStr;\r
+\r
+  ValueStr = NULL;\r
+\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {\r
+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");\r
+  } else {\r
+    ASSERT (FALSE);\r
+  }\r
+\r
+  ASSERT (ValueStr != NULL);\r
+\r
+  Value64 = StrToUInteger (ValueStr, &Status);\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    Indexer->Index = (UINTN) Value64;\r
+    ZeroMem (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID));\r
+  } else {\r
+\r
+    if (ShellCommandLineGetFlag (ParamPackage, L"--lookup-peer-address")) {\r
+      ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-address");\r
+      ASSERT (ValueStr != NULL);\r
+\r
+      Indexer->PadId.PeerIdValid = FALSE;\r
+      Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &Indexer->PadId.Id.IpAddress);\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (\r
+          -1,\r
+          -1,\r
+          NULL,\r
+          STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+          mHiiHandle,\r
+          mAppName,\r
+          L"--lookup-peer-address",\r
+          ValueStr\r
+          );\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    } else {\r
+      ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-id");\r
+      if (ValueStr == NULL) {\r
+        ShellPrintHiiEx (\r
+          -1,\r
+          -1,\r
+          NULL,\r
+          STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+          mHiiHandle,\r
+          mAppName,\r
+          L"--lookup-peer-address --lookup-peer-id"\r
+          );\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      Indexer->PadId.PeerIdValid = TRUE;\r
+      StrnCpy ((CHAR16 *) Indexer->PadId.Id.PeerId, ValueStr, ARRAY_SIZE (Indexer->PadId.Id.PeerId) - 1);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[] = {\r
+  (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSpdIndexer,\r
+  (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSadIndexer,\r
+  (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructPadIndexer\r
+};\r
diff --git a/NetworkPkg/Application/IpsecConfig/Indexer.h b/NetworkPkg/Application/IpsecConfig/Indexer.h
new file mode 100644 (file)
index 0000000..078f38a
--- /dev/null
@@ -0,0 +1,58 @@
+/** @file\r
+  The internal structure and function declaration to construct ENTRY_INDEXER in\r
+  IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _INDEXER_H_\r
+#define _INDEXER_H_\r
+\r
+typedef struct {\r
+  UINT8    *Name;\r
+  UINTN    Index;    // Used only if Name is NULL.\r
+} SPD_ENTRY_INDEXER;\r
+\r
+typedef struct {\r
+  EFI_IPSEC_SA_ID    SaId;\r
+  UINTN              Index;\r
+} SAD_ENTRY_INDEXER;\r
+\r
+typedef struct {\r
+  EFI_IPSEC_PAD_ID    PadId;\r
+  UINTN               Index;\r
+} PAD_ENTRY_INDEXER;\r
+\r
+typedef union {\r
+  SPD_ENTRY_INDEXER    Spd;\r
+  SAD_ENTRY_INDEXER    Sad;\r
+  PAD_ENTRY_INDEXER    Pad;\r
+} POLICY_ENTRY_INDEXER;\r
+\r
+/**\r
+  The prototype for the ConstructSpdIndexer()/ConstructSadIndexer()/ConstructPadIndexer().\r
+  Fill in SPD_ENTRY_INDEXER/SAD_ENTRY_INDEXER/PAD_ENTRY_INDEXER through ParamPackage list.\r
+\r
+  @param[in, out] Indexer         The pointer to the POLICY_ENTRY_INDEXER union.\r
+  @param[in]      ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS    Filled in POLICY_ENTRY_INDEXER successfully.\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(* CONSTRUCT_POLICY_ENTRY_INDEXER) (\r
+  IN POLICY_ENTRY_INDEXER    *Indexer,\r
+  IN LIST_ENTRY              *ParamPackage\r
+);\r
+\r
+extern CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[];\r
+#endif\r
diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.c b/NetworkPkg/Application/IpsecConfig/IpSecConfig.c
new file mode 100644 (file)
index 0000000..8006d84
--- /dev/null
@@ -0,0 +1,809 @@
+/** @file\r
+  The main process for IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/HiiLib.h>\r
+\r
+#include <Protocol/IpSec.h>\r
+\r
+#include "IpSecConfig.h"\r
+#include "Dump.h"\r
+#include "Indexer.h"\r
+#include "PolicyEntryOperation.h"\r
+#include "Delete.h"\r
+#include "Helper.h"\r
+\r
+//\r
+// Used for ShellCommandLineParseEx only\r
+// and to ensure user inputs are in valid format\r
+//\r
+SHELL_PARAM_ITEM    mIpSecConfigParamList[] = {\r
+  { L"-p",                    TypeValue },\r
+  { L"-a",                    TypeValue },\r
+  { L"-i",                    TypeValue },\r
+  { L"-e",                    TypeValue },\r
+  { L"-d",                    TypeValue },\r
+  { L"-f",                    TypeFlag },\r
+  { L"-l",                    TypeFlag },\r
+  { L"-enable",               TypeFlag },\r
+  { L"-disable",              TypeFlag },\r
+  { L"-status",               TypeFlag },\r
+  { L"-?",                    TypeFlag },\r
+\r
+  //\r
+  // SPD Selector\r
+  //\r
+  { L"--local",               TypeValue },\r
+  { L"--remote",              TypeValue },\r
+  { L"--proto",               TypeValue },\r
+  { L"--local-port",          TypeValue },\r
+  { L"--remote-port",         TypeValue },\r
+  { L"--icmp-type",           TypeValue },\r
+  { L"--icmp-code",           TypeValue },\r
+\r
+  //\r
+  // SPD Data\r
+  //\r
+  { L"--name",                TypeValue },\r
+  { L"--packet-flag",         TypeValue },\r
+  { L"--action",              TypeValue },\r
+  { L"--lifebyte",            TypeValue },\r
+  { L"--lifetime-soft",       TypeValue },\r
+  { L"--lifetime",            TypeValue },\r
+  { L"--mode",                TypeValue },\r
+  { L"--tunnel-local",        TypeValue },\r
+  { L"--tunnel-remote",       TypeValue },\r
+  { L"--dont-fragment",       TypeValue },\r
+  { L"--ipsec-proto",         TypeValue },\r
+  { L"--auth-algo",           TypeValue },\r
+  { L"--encrypt-algo",        TypeValue },\r
+\r
+  { L"--ext-sequence",        TypeFlag  },\r
+  { L"--sequence-overflow",   TypeFlag  },\r
+  { L"--fragment-check",      TypeFlag  },\r
+  { L"--ext-sequence-",       TypeFlag  },\r
+  { L"--sequence-overflow-",  TypeFlag  },\r
+  { L"--fragment-check-",     TypeFlag  },\r
+\r
+  //\r
+  // SA ID\r
+  // --ipsec-proto\r
+  //\r
+  { L"--spi",                 TypeValue },\r
+  { L"--dest",                TypeValue },\r
+  { L"--lookup-spi",          TypeValue },\r
+  { L"--lookup-ipsec-proto",  TypeValue },\r
+  { L"--lookup-dest",         TypeValue },\r
+\r
+  //\r
+  // SA DATA\r
+  // --mode\r
+  // --auth-algo\r
+  // --encrypt-algo\r
+  //\r
+  { L"--sequence-number",     TypeValue },\r
+  { L"--antireplay-window",   TypeValue },\r
+  { L"--auth-key",            TypeValue },\r
+  { L"--encrypt-key",         TypeValue },\r
+  { L"--path-mtu",            TypeValue },\r
+\r
+  //\r
+  // PAD ID\r
+  //\r
+  { L"--peer-id",             TypeValue },\r
+  { L"--peer-address",        TypeValue },\r
+  { L"--auth-proto",          TypeValue },\r
+  { L"--auth-method",         TypeValue },\r
+  { L"--ike-id",              TypeValue },\r
+  { L"--ike-id-",             TypeValue },\r
+  { L"--auth-data",           TypeValue },\r
+  { L"--revocation-data",     TypeValue },\r
+  { L"--lookup-peer-id",      TypeValue },\r
+  { L"--lookup-peer-address", TypeValue },\r
+\r
+  { NULL,                     TypeMax   },\r
+};\r
+\r
+//\r
+// -P\r
+//\r
+STR2INT mMapPolicy[] = {\r
+  { L"SPD",       IPsecConfigDataTypeSpd },\r
+  { L"SAD",       IPsecConfigDataTypeSad },\r
+  { L"PAD",       IPsecConfigDataTypePad },\r
+  { NULL,         0 },\r
+};\r
+\r
+//\r
+// --proto\r
+//\r
+STR2INT mMapIpProtocol[] = {\r
+  { L"TCP",       EFI_IP4_PROTO_TCP },\r
+  { L"UDP",       EFI_IP4_PROTO_UDP },\r
+  { L"ICMP",      EFI_IP4_PROTO_ICMP },\r
+  { NULL,         0 },\r
+};\r
+\r
+//\r
+// --action\r
+//\r
+STR2INT mMapIpSecAction[] = {\r
+  { L"Bypass",    EfiIPsecActionBypass },\r
+  { L"Discard",   EfiIPsecActionDiscard },\r
+  { L"Protect",   EfiIPsecActionProtect },\r
+  { NULL,         0 },\r
+};\r
+\r
+//\r
+// --mode\r
+//\r
+STR2INT mMapIpSecMode[] = {\r
+  { L"Transport", EfiIPsecTransport },\r
+  { L"Tunnel",    EfiIPsecTunnel },\r
+  { NULL,         0 },\r
+};\r
+\r
+//\r
+// --dont-fragment\r
+//\r
+STR2INT mMapDfOption[] = {\r
+  { L"clear",     EfiIPsecTunnelClearDf },\r
+  { L"set",       EfiIPsecTunnelSetDf },\r
+  { L"copy",      EfiIPsecTunnelCopyDf },\r
+  { NULL,         0 },\r
+};\r
+\r
+//\r
+// --ipsec-proto\r
+//\r
+STR2INT mMapIpSecProtocol[] = {\r
+  { L"AH",        EfiIPsecAH },\r
+  { L"ESP",       EfiIPsecESP },\r
+  { NULL,         0 },\r
+};\r
+\r
+//\r
+// --auth-algo\r
+//\r
+STR2INT mMapAuthAlgo[] = {\r
+  { L"NONE",         EFI_IPSEC_AALG_NONE },\r
+  { L"MD5HMAC",      EFI_IPSEC_AALG_MD5HMAC },\r
+  { L"SHA1HMAC",     EFI_IPSEC_AALG_SHA1HMAC },\r
+  { L"SHA2-256HMAC", EFI_IPSEC_AALG_SHA2_256HMAC },\r
+  { L"SHA2-384HMAC", EFI_IPSEC_AALG_SHA2_384HMAC },\r
+  { L"SHA2-512HMAC", EFI_IPSEC_AALG_SHA2_512HMAC },\r
+  { L"AES-XCBC-MAC", EFI_IPSEC_AALG_AES_XCBC_MAC },\r
+  { L"NULL",         EFI_IPSEC_AALG_NULL },\r
+  { NULL,            0 },\r
+};\r
+\r
+//\r
+// --encrypt-algo\r
+//\r
+STR2INT mMapEncAlgo[] = {\r
+  { L"NONE",         EFI_IPSEC_EALG_NONE },\r
+  { L"DESCBC",       EFI_IPSEC_EALG_DESCBC },\r
+  { L"3DESCBC",      EFI_IPSEC_EALG_3DESCBC },\r
+  { L"CASTCBC",      EFI_IPSEC_EALG_CASTCBC },\r
+  { L"BLOWFISHCBC",  EFI_IPSEC_EALG_BLOWFISHCBC },\r
+  { L"NULL",         EFI_IPSEC_EALG_NULL },\r
+  { L"AESCBC",       EFI_IPSEC_EALG_AESCBC },\r
+  { L"AESCTR",       EFI_IPSEC_EALG_AESCTR },\r
+  { L"AES-CCM-ICV8", EFI_IPSEC_EALG_AES_CCM_ICV8 },\r
+  { L"AES-CCM-ICV12",EFI_IPSEC_EALG_AES_CCM_ICV12 },\r
+  { L"AES-CCM-ICV16",EFI_IPSEC_EALG_AES_CCM_ICV16 },\r
+  { L"AES-GCM-ICV8", EFI_IPSEC_EALG_AES_GCM_ICV8 },\r
+  { L"AES-GCM-ICV12",EFI_IPSEC_EALG_AES_GCM_ICV12 },\r
+  { L"AES-GCM-ICV16",EFI_IPSEC_EALG_AES_GCM_ICV16 },\r
+  { NULL,            0 },\r
+};\r
+\r
+//\r
+// --auth-proto\r
+//\r
+STR2INT mMapAuthProto[] = {\r
+  { L"IKEv1",        EfiIPsecAuthProtocolIKEv1 },\r
+  { L"IKEv2",        EfiIPsecAuthProtocolIKEv2 },\r
+  { NULL,            0 },\r
+};\r
+\r
+//\r
+// --auth-method\r
+//\r
+STR2INT mMapAuthMethod[] = {\r
+  { L"PreSharedSecret", EfiIPsecAuthMethodPreSharedSecret },\r
+  { L"Certificates",    EfiIPsecAuthMethodCertificates },\r
+  { NULL,               0 },\r
+};\r
+\r
+EFI_IPSEC_PROTOCOL           *mIpSec;\r
+EFI_IPSEC_CONFIG_PROTOCOL    *mIpSecConfig;\r
+EFI_HII_HANDLE               mHiiHandle;\r
+EFI_GUID                     mEfiIpSecConfigGuid = EFI_IPSEC_CONFIG_GUID;\r
+CHAR16                       mAppName[]          = L"IpSecConfig";\r
+\r
+//\r
+// Used for IpSecConfigRetriveCheckListByName only to check the validation of user input\r
+//\r
+VAR_CHECK_ITEM    mIpSecConfigVarCheckList[] = {\r
+  { L"-enable",              BIT(1)|BIT(0),  BIT(1),  BIT(2)|BIT(1)|BIT(0), 0 },\r
+  { L"-disable",             BIT(1)|BIT(0),  BIT(1),  BIT(2)|BIT(1)|BIT(0), 0 },\r
+  { L"-status",              BIT(1)|BIT(0),  BIT(1),  BIT(2)|BIT(1)|BIT(0), 0 },\r
+  { L"-p",                   BIT(1),         0,       BIT(2)|BIT(1)|BIT(0), 0 },\r
+\r
+  { L"-a",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },\r
+  { L"-i",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },\r
+  { L"-d",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },\r
+  { L"-e",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },\r
+  { L"-l",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },\r
+  { L"-f",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },\r
+\r
+  { L"-?",                   BIT(0),         BIT(0),  BIT(2)|BIT(1)|BIT(0), 0 },\r
+\r
+  //\r
+  // SPD Selector\r
+  //\r
+  { L"--local",              0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--remote",             0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--proto",              0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--local-port",         0,              0,       BIT(2)|BIT(1),        BIT(0) },\r
+  { L"--remote-port",        0,              0,       BIT(2)|BIT(1),        BIT(0) },\r
+  { L"--icmp-type",          0,              0,       BIT(2)|BIT(1),        BIT(1) },\r
+  { L"--icmp-code",          0,              0,       BIT(2)|BIT(1),        BIT(1) },\r
+\r
+  //\r
+  // SPD Data\r
+  //\r
+  { L"--name",               0,              0,       BIT(2),               0 },\r
+  { L"--packet-flag",        0,              0,       BIT(2),               0 },\r
+  { L"--action",             0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--lifebyte",           0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--lifetime-soft",      0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--lifetime",           0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--mode",               0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--tunnel-local",       0,              0,       BIT(2),               0 },\r
+  { L"--tunnel-remote",      0,              0,       BIT(2),               0 },\r
+  { L"--dont-fragment",      0,              0,       BIT(2),               0 },\r
+  { L"--ipsec-proto",        0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--auth-algo",          0,              0,       BIT(2)|BIT(1),        0 },\r
+  { L"--encrypt-algo",       0,              0,       BIT(2)|BIT(1),        0 },\r
+\r
+  { L"--ext-sequence",       0,              0,       BIT(2),               BIT(2) },\r
+  { L"--sequence-overflow",  0,              0,       BIT(2),               BIT(2) },\r
+  { L"--fragment-check",     0,              0,       BIT(2),               BIT(2) },\r
+  { L"--ext-sequence-",      0,              0,       BIT(2),               BIT(3) },\r
+  { L"--sequence-overflow-", 0,              0,       BIT(2),               BIT(3) },\r
+  { L"--fragment-check-",    0,              0,       BIT(2),               BIT(3) },\r
+\r
+  //\r
+  // SA ID\r
+  // --ipsec-proto\r
+  //\r
+  { L"--spi",                0,              0,       BIT(1),               0 },\r
+  { L"--dest",               0,              0,       BIT(1),               0 },\r
+  { L"--lookup-spi",         0,              0,       BIT(1),               0 },\r
+  { L"--lookup-ipsec-proto", 0,              0,       BIT(1),               0 },\r
+  { L"--lookup-dest",        0,              0,       BIT(1),               0 },\r
+\r
+  //\r
+  // SA DATA\r
+  // --mode\r
+  // --auth-algo\r
+  // --encrypt-algo\r
+  //\r
+  { L"--sequence-number",    0,              0,       BIT(1),               0 },\r
+  { L"--antireplay-window",  0,              0,       BIT(1),               0 },\r
+  { L"--auth-key",           0,              0,       BIT(1),               0 },\r
+  { L"--encrypt-key",        0,              0,       BIT(1),               0 },\r
+  { L"--path-mtu",           0,              0,       BIT(1),               0 },\r
+\r
+  //\r
+  // The example to add a PAD:\r
+  // "-A --peer-id Mike [--peer-address 10.23.2.2] --auth-proto IKE1/IKE2\r
+  //     --auth-method PreSharedSeceret/Certificate --ike-id\r
+  //     --auth-data 343343 --revocation-data 2342432"\r
+  // The example to delete a PAD:\r
+  // "-D * --lookup-peer-id Mike [--lookup-peer-address 10.23.2.2]"\r
+  // "-D 1"\r
+  // The example to edit a PAD:\r
+  // "-E * --lookup-peer-id Mike --auth-method Certificate"\r
+\r
+  //\r
+  // PAD ID\r
+  //\r
+  { L"--peer-id",            0,              0,       BIT(0),               BIT(4) },\r
+  { L"--peer-address",       0,              0,       BIT(0),               BIT(5) },\r
+  { L"--auth-proto",         0,              0,       BIT(0),               0 },\r
+  { L"--auth-method",        0,              0,       BIT(0),               0 },\r
+  { L"--IKE-ID",             0,              0,       BIT(0),               BIT(6) },\r
+  { L"--IKE-ID-",            0,              0,       BIT(0),               BIT(7) },\r
+  { L"--auth-data",          0,              0,       BIT(0),               0 },\r
+  { L"--revocation-data",    0,              0,       BIT(0),               0 },\r
+  { L"--lookup-peer-id",     0,              0,       BIT(0),               BIT(4) },\r
+  { L"--lookup-peer-address",0,              0,       BIT(0),               BIT(5) },\r
+\r
+  { NULL,                    0,              0,       0,                    0 },\r
+};\r
+\r
+/**\r
+  The function to allocate the proper sized buffer for various\r
+  EFI interfaces.\r
+\r
+  @param[in, out] Status        Current status.\r
+  @param[in, out] Buffer        Current allocated buffer, or NULL.\r
+  @param[in]      BufferSize    Current buffer size needed\r
+\r
+  @retval TRUE     If the buffer was reallocated and the caller should try the API again.\r
+  @retval FALSE    If the buffer was not reallocated successfully.\r
+**/\r
+BOOLEAN\r
+GrowBuffer (\r
+  IN OUT EFI_STATUS    *Status,\r
+  IN OUT VOID          **Buffer,\r
+  IN     UINTN         BufferSize\r
+  )\r
+{\r
+  BOOLEAN    TryAgain;\r
+\r
+  ASSERT (Status != NULL);\r
+  ASSERT (Buffer != NULL);\r
+\r
+  //\r
+  // If this is an initial request, buffer will be null with a new buffer size.\r
+  //\r
+  if ((NULL == *Buffer) && (BufferSize != 0)) {\r
+    *Status = EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  //\r
+  // If the status code is "buffer too small", resize the buffer.\r
+  //\r
+  TryAgain = FALSE;\r
+  if (*Status == EFI_BUFFER_TOO_SMALL) {\r
+\r
+    if (*Buffer != NULL) {\r
+      FreePool (*Buffer);\r
+    }\r
+\r
+    *Buffer = AllocateZeroPool (BufferSize);\r
+\r
+    if (*Buffer != NULL) {\r
+      TryAgain = TRUE;\r
+    } else {\r
+      *Status = EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If there's an error, free the buffer.\r
+  //\r
+  if (!TryAgain && EFI_ERROR (*Status) && (*Buffer != NULL)) {\r
+    FreePool (*Buffer);\r
+    *Buffer = NULL;\r
+  }\r
+\r
+  return TryAgain;\r
+}\r
+\r
+/**\r
+  Function returns an array of handles that support the requested protocol\r
+  in a buffer allocated from a pool.\r
+\r
+  @param[in]      SearchType    Specifies which handle(s) are to be returned.\r
+  @param[in]      Protocol      Provides the protocol to search by.\r
+                                This parameter is only valid for SearchType ByProtocol.\r
+\r
+  @param[in]      SearchKey     Supplies the search key depending on the SearchType.\r
+  @param[in, out] NoHandles     The number of handles returned in Buffer.\r
+  @param[out]     Buffer        A pointer to the buffer to return the requested array of\r
+                                handles that support Protocol.\r
+\r
+  @retval EFI_SUCCESS    The resulting array of handles was returned.\r
+  @retval Others         Other mistake case.\r
+**/\r
+EFI_STATUS\r
+LocateHandle (\r
+  IN     EFI_LOCATE_SEARCH_TYPE    SearchType,\r
+  IN     EFI_GUID                  *Protocol  OPTIONAL,\r
+  IN     VOID                      *SearchKey OPTIONAL,\r
+  IN OUT UINTN                     *NoHandles,\r
+     OUT EFI_HANDLE                **Buffer\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+  UINTN         BufferSize;\r
+\r
+  ASSERT (NoHandles != NULL);\r
+  ASSERT (Buffer != NULL);\r
+\r
+  //\r
+  // Initialize for GrowBuffer loop.\r
+  //\r
+  Status      = EFI_SUCCESS;\r
+  *Buffer     = NULL;\r
+  BufferSize  = 50 * sizeof (EFI_HANDLE);\r
+\r
+  //\r
+  // Call the real function.\r
+  //\r
+  while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {\r
+    Status = gBS->LocateHandle (\r
+                    SearchType,\r
+                    Protocol,\r
+                    SearchKey,\r
+                    &BufferSize,\r
+                    *Buffer\r
+                    );\r
+  }\r
+\r
+  *NoHandles = BufferSize / sizeof (EFI_HANDLE);\r
+  if (EFI_ERROR (Status)) {\r
+    *NoHandles = 0;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Find the first instance of this protocol in the system and return its interface.\r
+\r
+  @param[in]  ProtocolGuid    The guid of the protocol.\r
+  @param[out] Interface       The pointer to the first instance of the protocol.\r
+\r
+  @retval EFI_SUCCESS    A protocol instance matching ProtocolGuid was found.\r
+  @retval Others         A protocol instance matching ProtocolGuid was not found.\r
+**/\r
+EFI_STATUS\r
+LocateProtocol (\r
+  IN  EFI_GUID    *ProtocolGuid,\r
+  OUT VOID        **Interface\r
+  )\r
+\r
+{\r
+  EFI_STATUS    Status;\r
+  UINTN         NumberHandles;\r
+  UINTN         Index;\r
+  EFI_HANDLE    *Handles;\r
+\r
+  *Interface    = NULL;\r
+  Handles       = NULL;\r
+  NumberHandles = 0;\r
+\r
+  Status        = LocateHandle (ByProtocol, ProtocolGuid, NULL, &NumberHandles, &Handles);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_INFO, "LibLocateProtocol: Handle not found\n"));\r
+    return Status;\r
+  }\r
+\r
+  for (Index = 0; Index < NumberHandles; Index++) {\r
+    ASSERT (Handles != NULL);\r
+    Status = gBS->HandleProtocol (\r
+                    Handles[Index],\r
+                    ProtocolGuid,\r
+                    Interface\r
+                    );\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (Handles != NULL) {\r
+    FreePool (Handles);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Helper function called to check the conflicted flags.\r
+\r
+  @param[in] CheckList       The pointer to the VAR_CHECK_ITEM table.\r
+  @param[in] ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS              No conflicted flags.\r
+  @retval EFI_INVALID_PARAMETER    The input parameter is erroroneous or there are some conflicted flags.\r
+**/\r
+EFI_STATUS\r
+IpSecConfigRetriveCheckListByName (\r
+  IN VAR_CHECK_ITEM    *CheckList,\r
+  IN LIST_ENTRY        *ParamPackage\r
+)\r
+{\r
+\r
+  LIST_ENTRY        *Node;\r
+  VAR_CHECK_ITEM    *Item;\r
+  UINT32            Attribute1;\r
+  UINT32            Attribute2;\r
+  UINT32            Attribute3;\r
+  UINT32            Attribute4;\r
+  UINT32            Index;\r
+\r
+  Attribute1 = 0;\r
+  Attribute2 = 0;\r
+  Attribute3 = 0;\r
+  Attribute4 = 0;\r
+  Index      = 0;\r
+  Item       = mIpSecConfigVarCheckList;\r
+\r
+  if ((ParamPackage == NULL) || (CheckList == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Enumerate through the list of parameters that are input by user.\r
+  //\r
+  for (Node = GetFirstNode (ParamPackage); !IsNull (ParamPackage, Node); Node = GetNextNode (ParamPackage, Node)) {\r
+    if (((SHELL_PARAM_PACKAGE *) Node)->Name != NULL) {\r
+      //\r
+         // Enumerate the check list that defines the conflicted attributes of each flag.\r
+      //\r
+      for (; Item->VarName != NULL; Item++) {\r
+        if (StrCmp (((SHELL_PARAM_PACKAGE *) Node)->Name, Item->VarName) == 0) {\r
+          Index++;\r
+          if (Index == 1) {\r
+            Attribute1 = Item->Attribute1;\r
+            Attribute2 = Item->Attribute2;\r
+            Attribute3 = Item->Attribute3;\r
+            Attribute4 = Item->Attribute4;\r
+          } else {\r
+            Attribute1 &= Item->Attribute1;\r
+            Attribute2 |= Item->Attribute2;\r
+            Attribute3 &= Item->Attribute3;\r
+            Attribute4 |= Item->Attribute4;\r
+            if (Attribute1 != 0) {\r
+              return EFI_INVALID_PARAMETER;\r
+            }\r
+\r
+            if (Attribute2 != 0) {\r
+              if ((Index == 2) && (StrCmp (Item->VarName, L"-p") == 0)) {\r
+                continue;\r
+              }\r
+\r
+              return EFI_INVALID_PARAMETER;\r
+            }\r
+\r
+            if (Attribute3 == 0) {\r
+              return EFI_INVALID_PARAMETER;\r
+            }\r
+            if (((Attribute4 & 0xFF) == 0x03) || ((Attribute4 & 0xFF) == 0x0C) ||\r
+                ((Attribute4 & 0xFF) == 0x30) || ((Attribute4 & 0xFF) == 0xC0)) {\r
+              return EFI_INVALID_PARAMETER;\r
+            }\r
+          }\r
+          break;\r
+        }\r
+      }\r
+\r
+      Item = mIpSecConfigVarCheckList;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This is the declaration of an EFI image entry point. This entry point is\r
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+  both device drivers and bus drivers.\r
+\r
+  The entry point for IpSecConfig application that parse the command line input and call an IpSecConfig process.\r
+\r
+  @param[in] ImageHandle    The image handle of this application.\r
+  @param[in] SystemTable    The pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS    The operation completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeIpSecConfig (\r
+  IN EFI_HANDLE          ImageHandle,\r
+  IN EFI_SYSTEM_TABLE    *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  EFI_IPSEC_CONFIG_DATA_TYPE    DataType;\r
+  UINT8                         Value;\r
+  LIST_ENTRY                    *ParamPackage;\r
+  CONST CHAR16                  *ValueStr;\r
+  CHAR16                        *ProblemParam;\r
+  UINTN                         NonOptionCount;\r
+\r
+  //\r
+  // Register our string package with HII and return the handle to it.\r
+  //\r
+  mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IpSecConfigStrings, NULL);\r
+  ASSERT (mHiiHandle != NULL);\r
+\r
+  Status = ShellCommandLineParseEx (mIpSecConfigParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);\r
+  if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, ProblemParam);\r
+    goto Done;\r
+  }\r
+\r
+  Status = IpSecConfigRetriveCheckListByName (mIpSecConfigVarCheckList, ParamPackage);\r
+  if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_MISTAKEN_OPTIONS), mHiiHandle);\r
+    goto Done;\r
+  }\r
+\r
+  Status = LocateProtocol (&gEfiIpSecConfigProtocolGuid, (VOID **) &mIpSecConfig);\r
+  if (EFI_ERROR (Status) || mIpSecConfig == NULL) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName);\r
+    goto Done;\r
+  }\r
+\r
+  Status = LocateProtocol (&gEfiIpSecProtocolGuid, (VOID **) &mIpSec);\r
+  if (EFI_ERROR (Status) || mIpSec == NULL) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName);\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // Enable IPsec.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-enable")) {\r
+    if (!(mIpSec->DisabledFlag)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_ENABLE), mHiiHandle, mAppName);\r
+    } else {\r
+      //\r
+      // Set enable flag.\r
+      //\r
+      Value  = IPSEC_STATUS_ENABLED;\r
+      Status = gRT->SetVariable (\r
+                      IPSECCONFIG_STATUS_NAME,\r
+                      &gEfiIpSecConfigProtocolGuid,\r
+                      EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                      sizeof (Value),\r
+                      &Value\r
+                      );\r
+      if (!EFI_ERROR (Status)) {\r
+        mIpSec->DisabledFlag = FALSE;\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_SUCCESS), mHiiHandle, mAppName);\r
+      } else {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_FAILED), mHiiHandle, mAppName);\r
+      }\r
+    }\r
+\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // Disable IPsec.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-disable")) {\r
+    if (mIpSec->DisabledFlag) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_DISABLE), mHiiHandle, mAppName);\r
+    } else {\r
+      //\r
+      // Set disable flag; however, leave it to be disabled in the callback function of DisabledEvent.\r
+      //\r
+      gBS->SignalEvent (mIpSec->DisabledEvent);\r
+      if (mIpSec->DisabledFlag) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_SUCCESS), mHiiHandle, mAppName);\r
+      } else {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_FAILED), mHiiHandle, mAppName);\r
+      }\r
+    }\r
+\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  //IPsec Status.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-status")) {\r
+    if (mIpSec->DisabledFlag) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_DISABLE), mHiiHandle, mAppName);\r
+    } else {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_ENABLE), mHiiHandle, mAppName);\r
+    }\r
+\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // Try to get policy database type.\r
+  //\r
+  DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) -1;\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-p");\r
+  if (ValueStr != NULL) {\r
+    DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) MapStringToInteger (ValueStr, mMapPolicy);\r
+    if (DataType == -1) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle, mAppName, ValueStr);\r
+      goto Done;\r
+    }\r
+  }\r
+\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {\r
+    switch (DataType) {\r
+      case (EFI_IPSEC_CONFIG_DATA_TYPE) -1:\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_HELP), mHiiHandle);\r
+        break;\r
+\r
+      case IPsecConfigDataTypeSpd:\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SPD_HELP), mHiiHandle);\r
+        break;\r
+\r
+      case IPsecConfigDataTypeSad:\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SAD_HELP), mHiiHandle);\r
+        break;\r
+\r
+      case IPsecConfigDataTypePad:\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PAD_HELP), mHiiHandle);\r
+        break;\r
+\r
+      default:\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle);\r
+        break;\r
+    }\r
+\r
+    goto Done;\r
+  }\r
+\r
+  NonOptionCount = ShellCommandLineGetCount ();\r
+  if ((NonOptionCount - 1) > 0) {\r
+    ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32) (NonOptionCount - 1));\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_REDUNDANCY_MANY), mHiiHandle, mAppName, ValueStr);\r
+    goto Done;\r
+  }\r
+\r
+  if (DataType == -1) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_DB), mHiiHandle, mAppName);\r
+    goto Done;\r
+  }\r
+\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-a")) {\r
+    Status = AddOrInsertPolicyEntry (DataType, ParamPackage);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {\r
+    Status = AddOrInsertPolicyEntry (DataType, ParamPackage);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {\r
+    Status = EditPolicyEntry (DataType, ParamPackage);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {\r
+    Status = FlushOrDeletePolicyEntry (DataType, ParamPackage);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-f")) {\r
+    Status = FlushOrDeletePolicyEntry (DataType, ParamPackage);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-l")) {\r
+    Status = ListPolicyEntry (DataType, ParamPackage);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
+  } else {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, mAppName);\r
+    goto Done;\r
+  }\r
+\r
+Done:\r
+  ShellCommandLineFreeVarList (ParamPackage);\r
+  HiiRemovePackages (mHiiHandle);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.h b/NetworkPkg/Application/IpsecConfig/IpSecConfig.h
new file mode 100644 (file)
index 0000000..d1a7681
--- /dev/null
@@ -0,0 +1,123 @@
+/** @file\r
+  The internal structure and function declaration in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _IPSEC_CONFIG_H_\r
+#define _IPSEC_CONFIG_H_\r
+\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/ShellLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+#include <Protocol/IpSecConfig.h>\r
+\r
+#define EFI_IPSEC_CONFIG_GUID \\r
+  { \\r
+    0x9db0c3ac, 0xd9d2, 0x4f96, {0x9e, 0xd7, 0x6d, 0xa6, 0x12, 0xa4, 0xf3, 0x27} \\r
+  }\r
+\r
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))\r
+\r
+#define IPSECCONFIG_STATUS_NAME    L"IpSecStatus"\r
+\r
+#define BIT(x)   (UINT32) (1 << (x))\r
+\r
+#define IPSEC_STATUS_DISABLED    0x0\r
+#define IPSEC_STATUS_ENABLED     0x1\r
+\r
+#define EFI_IP4_PROTO_ICMP       0x1\r
+#define EFI_IP4_PROTO_TCP        0x6\r
+#define EFI_IP4_PROTO_UDP        0x11\r
+\r
+#define EFI_IPSEC_ANY_PROTOCOL    0xFFFF\r
+#define EFI_IPSEC_ANY_PORT        0\r
+\r
+typedef struct _VAR_CHECK_ITEM {\r
+  CHAR16      *VarName;\r
+  UINT32      Attribute1;\r
+  UINT32      Attribute2;\r
+  UINT32      Attribute3;\r
+  UINT32      Attribute4;\r
+} VAR_CHECK_ITEM;\r
+\r
+typedef struct _SHELL_PARAM_PACKAGE{\r
+  LIST_ENTRY     Link;\r
+  CHAR16         *Name;\r
+  ParamType      Type;\r
+  CHAR16         *Value;\r
+  UINTN          OriginalPosition;\r
+} SHELL_PARAM_PACKAGE;\r
+\r
+typedef struct _STR2INT {\r
+  CHAR16        *String;\r
+  UINT32        Integer;\r
+} STR2INT;\r
+\r
+extern EFI_IPSEC_CONFIG_PROTOCOL    *mIpSecConfig;\r
+extern EFI_HII_HANDLE               mHiiHandle;\r
+extern CHAR16                       mAppName[];\r
+\r
+//\r
+// -P\r
+//\r
+extern STR2INT mMapPolicy[];\r
+\r
+//\r
+// --proto\r
+//\r
+extern STR2INT mMapIpProtocol[];\r
+\r
+//\r
+// --action\r
+//\r
+extern STR2INT mMapIpSecAction[];\r
+\r
+//\r
+// --mode\r
+//\r
+extern STR2INT mMapIpSecMode[];\r
+\r
+//\r
+// --dont-fragment\r
+//\r
+extern STR2INT mMapDfOption[];\r
+\r
+//\r
+// --ipsec-proto\r
+//\r
+extern STR2INT mMapIpSecProtocol[];\r
+//\r
+// --auth-algo\r
+//\r
+extern STR2INT mMapAuthAlgo[];\r
+\r
+//\r
+// --encrypt-algo\r
+//\r
+extern STR2INT mMapEncAlgo[];\r
+//\r
+// --auth-proto\r
+//\r
+extern STR2INT mMapAuthProto[];\r
+\r
+//\r
+// --auth-method\r
+//\r
+extern STR2INT mMapAuthMethod[];\r
+\r
+#endif\r
diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf b/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf
new file mode 100644 (file)
index 0000000..1e0d4f4
--- /dev/null
@@ -0,0 +1,61 @@
+## @file\r
+#  Component description file for IpSecConfig6 application.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010006\r
+  BASE_NAME                      = IpSecConfig\r
+  FILE_GUID                      = 0922E604-F5EC-42ef-980D-A35E9A2B1844\r
+  MODULE_TYPE                    = UEFI_APPLICATION\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = InitializeIpSecConfig\r
+\r
+[Sources]\r
+  IpSecConfigStrings.uni\r
+  IpSecConfig.c\r
+  IpSecConfig.h\r
+  Dump.c\r
+  Dump.h\r
+  Indexer.c\r
+  Indexer.h\r
+  Match.c\r
+  Match.h\r
+  Delete.h\r
+  Delete.c\r
+  Helper.c\r
+  Helper.h\r
+  ForEach.c\r
+  ForEach.h\r
+  PolicyEntryOperation.c\r
+  PolicyEntryOperation.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  ShellPkg/ShellPkg.dec\r
+\r
+[LibraryClasses]\r
+  UefiBootServicesTableLib\r
+  UefiApplicationEntryPoint\r
+  BaseMemoryLib\r
+  ShellLib\r
+  MemoryAllocationLib\r
+  DebugLib\r
+  HiiLib\r
+  NetLib\r
+  UefiLib\r
+\r
+[Protocols]\r
+  gEfiIpSecProtocolGuid                         ##CONSUMS\r
+  gEfiIpSecConfigProtocolGuid                   ##CONSUMS\r
diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni b/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni
new file mode 100644 (file)
index 0000000..fb0e27d
Binary files /dev/null and b/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni differ
diff --git a/NetworkPkg/Application/IpsecConfig/Match.c b/NetworkPkg/Application/IpsecConfig/Match.c
new file mode 100644 (file)
index 0000000..d6595ee
--- /dev/null
@@ -0,0 +1,163 @@
+/** @file\r
+  The implementation of match policy entry function in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Indexer.h"\r
+#include "Match.h"\r
+\r
+/**\r
+  Private function to validate a buffer that should be filled with zero.\r
+\r
+  @param[in] Memory    The pointer to the buffer.\r
+  @param[in] Size      The size of the buffer.\r
+\r
+  @retval TRUE     The memory is filled with zero.\r
+  @retval FALSE    The memory isn't filled with zero.\r
+**/\r
+BOOLEAN\r
+IsMemoryZero (\r
+  IN VOID     *Memory,\r
+  IN UINTN    Size\r
+  )\r
+{\r
+  UINTN    Index;\r
+\r
+  for (Index = 0; Index < Size; Index++) {\r
+    if (*((UINT8 *) Memory + Index) != 0) {\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Find the matching SPD with Indexer.\r
+\r
+  @param[in] Selector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+  @param[in] Data        The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+  @param[in] Indexer     The pointer to the SPD_ENTRY_INDEXER structure.\r
+\r
+  @retval TRUE     The matched SPD is found.\r
+  @retval FALSE    The matched SPD is not found.\r
+**/\r
+BOOLEAN\r
+MatchSpdEntry (\r
+  IN EFI_IPSEC_SPD_SELECTOR    *Selector,\r
+  IN EFI_IPSEC_SPD_DATA        *Data,\r
+  IN SPD_ENTRY_INDEXER         *Indexer\r
+  )\r
+{\r
+  BOOLEAN    Match;\r
+\r
+  Match = FALSE;\r
+  if (Indexer->Name != NULL) {\r
+    if ((Data->Name != NULL) && (AsciiStrCmp ((CHAR8 *) Indexer->Name, (CHAR8 *) Data->Name) == 0)) {\r
+      Match = TRUE;\r
+    }\r
+  } else {\r
+    if (Indexer->Index == 0) {\r
+      Match = TRUE;\r
+    }\r
+\r
+    Indexer->Index--;\r
+  }\r
+\r
+  return Match;\r
+}\r
+\r
+/**\r
+  Find the matching SAD with Indexer.\r
+\r
+  @param[in] SaId       The pointer to the EFI_IPSEC_SA_ID structure.\r
+  @param[in] Data       The pointer to the EFI_IPSEC_SA_DATA structure.\r
+  @param[in] Indexer    The pointer to the SPD_ENTRY_INDEXER structure.\r
+\r
+  @retval TRUE     The matched SAD is found.\r
+  @retval FALSE    The matched SAD is not found.\r
+**/\r
+BOOLEAN\r
+MatchSadEntry (\r
+  IN EFI_IPSEC_SA_ID      *SaId,\r
+  IN EFI_IPSEC_SA_DATA    *Data,\r
+  IN SAD_ENTRY_INDEXER    *Indexer\r
+  )\r
+{\r
+  BOOLEAN    Match;\r
+\r
+  Match = FALSE;\r
+  if (!IsMemoryZero (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID))) {\r
+    Match = (BOOLEAN) (CompareMem (&Indexer->SaId, SaId, sizeof (EFI_IPSEC_SA_ID)) == 0);\r
+  } else {\r
+    if (Indexer->Index == 0) {\r
+      Match = TRUE;\r
+    }\r
+    Indexer->Index--;\r
+  }\r
+\r
+  return Match;\r
+}\r
+\r
+/**\r
+  Find the matching PAD with Indexer.\r
+\r
+  @param[in] PadId      The pointer to the EFI_IPSEC_PAD_ID structure.\r
+  @param[in] Data       The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+  @param[in] Indexer    The pointer to the SPD_ENTRY_INDEXER structure.\r
+\r
+  @retval TRUE     The matched PAD is found.\r
+  @retval FALSE    The matched PAD is not found.\r
+**/\r
+BOOLEAN\r
+MatchPadEntry (\r
+  IN EFI_IPSEC_PAD_ID      *PadId,\r
+  IN EFI_IPSEC_PAD_DATA    *Data,\r
+  IN PAD_ENTRY_INDEXER     *Indexer\r
+  )\r
+{\r
+  BOOLEAN                       Match;\r
+\r
+  Match = FALSE;\r
+  if (!IsMemoryZero (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID))) {\r
+    Match = (BOOLEAN) ((Indexer->PadId.PeerIdValid == PadId->PeerIdValid) &&\r
+                       ((PadId->PeerIdValid &&\r
+                         (StrCmp (\r
+                            (CONST CHAR16 *) Indexer->PadId.Id.PeerId,\r
+                            (CONST CHAR16 *) PadId->Id.PeerId\r
+                            ) == 0)) ||\r
+                        ((!PadId->PeerIdValid) &&\r
+                         (Indexer->PadId.Id.IpAddress.PrefixLength == PadId->Id.IpAddress.PrefixLength) &&\r
+                         (CompareMem (\r
+                            &Indexer->PadId.Id.IpAddress.Address,\r
+                            &PadId->Id.IpAddress.Address,\r
+                            sizeof (EFI_IP_ADDRESS)\r
+                            ) == 0))));\r
+  } else {\r
+    if (Indexer->Index == 0) {\r
+      Match = TRUE;\r
+    }\r
+\r
+    Indexer->Index--;\r
+  }\r
+\r
+  return Match;\r
+}\r
+\r
+MATCH_POLICY_ENTRY mMatchPolicyEntry[] = {\r
+  (MATCH_POLICY_ENTRY) MatchSpdEntry,\r
+  (MATCH_POLICY_ENTRY) MatchSadEntry,\r
+  (MATCH_POLICY_ENTRY) MatchPadEntry\r
+};\r
+\r
diff --git a/NetworkPkg/Application/IpsecConfig/Match.h b/NetworkPkg/Application/IpsecConfig/Match.h
new file mode 100644 (file)
index 0000000..1d73c5c
--- /dev/null
@@ -0,0 +1,41 @@
+/** @file\r
+  The internal structure and function declaration of \r
+  match policy entry function in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _MATCH_H_\r
+#define _MATCH_H_\r
+\r
+/**\r
+  The prototype for the MatchSpdEntry()/MatchSadEntry()/MatchPadEntry().\r
+  The functionality is to find the matching SPD/SAD/PAD with Indexer.\r
+\r
+  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.\r
+  @param[in] Data        The pointer to corresponding Data.\r
+  @param[in] Indexer     The pointer to the POLICY_ENTRY_INDEXER union.\r
+\r
+  @retval TRUE     The matched SPD/SAD/PAD is found.\r
+  @retval FALSE    The matched SPD/SAD/PAD is not found.\r
+**/\r
+typedef\r
+BOOLEAN\r
+(* MATCH_POLICY_ENTRY) (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR    *Selector,\r
+  IN VOID                         *Data,\r
+  IN POLICY_ENTRY_INDEXER         *Indexer\r
+  );\r
+\r
+extern MATCH_POLICY_ENTRY mMatchPolicyEntry[];\r
+\r
+#endif\r
diff --git a/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c
new file mode 100644 (file)
index 0000000..ddfbb4c
--- /dev/null
@@ -0,0 +1,2016 @@
+/** @file\r
+  The implementation of policy entry operation function in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Indexer.h"\r
+#include "Match.h"\r
+#include "Helper.h"\r
+#include "ForEach.h"\r
+#include "PolicyEntryOperation.h"\r
+\r
+/**\r
+  Fill in EFI_IPSEC_SPD_SELECTOR through ParamPackage list.\r
+\r
+  @param[out]     Selector        The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+  @param[in]      ParamPackage    The pointer to the ParamPackage list.\r
+  @param[in, out] ParamPackage    The pointer to the Mask.\r
+\r
+  @retval EFI_SUCCESS              Fill in EFI_IPSEC_SPD_SELECTOR successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CreateSpdSelector (\r
+     OUT EFI_IPSEC_SPD_SELECTOR    *Selector,\r
+  IN     LIST_ENTRY                *ParamPackage,\r
+  IN OUT UINT32                    *Mask\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  EFI_STATUS      ReturnStatus;\r
+  CONST CHAR16    *ValueStr;\r
+\r
+  Status       = EFI_SUCCESS;\r
+  ReturnStatus = EFI_SUCCESS;\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local");\r
+  if (ValueStr != NULL) {\r
+    Selector->LocalAddressCount = 1;\r
+    Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->LocalAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--local",\r
+        ValueStr\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      *Mask |= LOCAL;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote");\r
+  if (ValueStr != NULL) {\r
+    Selector->RemoteAddressCount = 1;\r
+    Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->RemoteAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--remote",\r
+        ValueStr\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      *Mask |= REMOTE;\r
+    }\r
+  }\r
+\r
+  Selector->NextLayerProtocol = EFI_IPSEC_ANY_PROTOCOL;\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+  //\r
+  Status = GetNumber (\r
+             L"--proto",\r
+             (UINT16) -1,\r
+             &Selector->NextLayerProtocol,\r
+             sizeof (UINT16),\r
+             mMapIpProtocol,\r
+             ParamPackage,\r
+             FORMAT_NUMBER | FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= PROTO;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Selector->LocalPort  = EFI_IPSEC_ANY_PORT;\r
+  Selector->RemotePort = EFI_IPSEC_ANY_PORT;\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local-port");\r
+  if (ValueStr != NULL) {\r
+    Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->LocalPort, &Selector->LocalPortRange);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--local-port",\r
+        ValueStr\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      *Mask |= LOCAL_PORT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote-port");\r
+  if (ValueStr != NULL) {\r
+    Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->RemotePort, &Selector->RemotePortRange);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--remote-port",\r
+        ValueStr\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      *Mask |= REMOTE_PORT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+  //\r
+  Status = GetNumber (\r
+             L"--icmp-type",\r
+             (UINT8) -1,\r
+             &Selector->LocalPort,\r
+             sizeof (UINT16),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= ICMP_TYPE;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+  //\r
+  Status = GetNumber (\r
+             L"--icmp-code",\r
+             (UINT8) -1,\r
+             &Selector->RemotePort,\r
+             sizeof (UINT16),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= ICMP_CODE;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return ReturnStatus;\r
+}\r
+\r
+/**\r
+  Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA through ParamPackage list.\r
+\r
+  @param[out] Selector        The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+  @param[out] Data            The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+  @param[in]  ParamPackage    The pointer to the ParamPackage list.\r
+  @param[out] Mask            The pointer to the Mask.\r
+  @param[in]  CreateNew       The switch to create new.\r
+\r
+  @retval EFI_SUCCESS              Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CreateSpdEntry (\r
+  OUT EFI_IPSEC_SPD_SELECTOR    **Selector,\r
+  OUT EFI_IPSEC_SPD_DATA        **Data,\r
+  IN  LIST_ENTRY                *ParamPackage,\r
+  OUT UINT32                    *Mask,\r
+  IN  BOOLEAN                   CreateNew\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  EFI_STATUS      ReturnStatus;\r
+  CONST CHAR16    *ValueStr;\r
+  UINTN           DataSize;\r
+\r
+  Status    = EFI_SUCCESS;\r
+  *Mask     = 0;\r
+\r
+  *Selector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR) + 2 * sizeof (EFI_IP_ADDRESS_INFO));\r
+  ASSERT (*Selector != NULL);\r
+\r
+  (*Selector)->LocalAddress  = (EFI_IP_ADDRESS_INFO *) (*Selector + 1);\r
+  (*Selector)->RemoteAddress = (*Selector)->LocalAddress + 1;\r
+\r
+  ReturnStatus = CreateSpdSelector (*Selector, ParamPackage, Mask);\r
+\r
+  //\r
+  // SPD DATA\r
+  // NOTE: Allocate enough memory and add padding for different arch.\r
+  //\r
+  DataSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SPD_DATA));\r
+  DataSize  = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_PROCESS_POLICY));\r
+  DataSize += sizeof (EFI_IPSEC_TUNNEL_OPTION);\r
+\r
+  *Data = AllocateZeroPool (DataSize);\r
+  ASSERT (*Data != NULL);\r
+\r
+  (*Data)->ProcessingPolicy               = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (\r
+                                                                           (*Data + 1),\r
+                                                                           sizeof (UINTN)\r
+                                                                           );\r
+  (*Data)->ProcessingPolicy->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER (\r
+                                                                          ((*Data)->ProcessingPolicy + 1),\r
+                                                                          sizeof (UINTN)\r
+                                                                          );\r
+\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the Name in EFI_IPSEC_SPD_DATA.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--name");\r
+  if (ValueStr != NULL) {\r
+    UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) (*Data)->Name);\r
+    *Mask |= NAME;\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the PackageFlag in EFI_IPSEC_SPD_DATA.\r
+  //\r
+  Status = GetNumber (\r
+             L"--packet-flag",\r
+             (UINT8) -1,\r
+             &(*Data)->PackageFlag,\r
+             sizeof (UINT32),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= PACKET_FLAG;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the Action in EFI_IPSEC_SPD_DATA.\r
+  //\r
+  Status = GetNumber (\r
+             L"--action",\r
+             (UINT8) -1,\r
+             &(*Data)->Action,\r
+             sizeof (UINT32),\r
+             mMapIpSecAction,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= ACTION;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the ExtSeqNum in EFI_IPSEC_SPD_DATA.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence")) {\r
+    (*Data)->ProcessingPolicy->ExtSeqNum   = TRUE;\r
+    *Mask |= EXT_SEQUENCE;\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence-")) {\r
+    (*Data)->ProcessingPolicy->ExtSeqNum   = FALSE;\r
+    *Mask |= EXT_SEQUENCE;\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the SeqOverflow in EFI_IPSEC_SPD_DATA.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow")) {\r
+    (*Data)->ProcessingPolicy->SeqOverflow = TRUE;\r
+    *Mask |= SEQUENCE_OVERFLOW;\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow-")) {\r
+    (*Data)->ProcessingPolicy->SeqOverflow = FALSE;\r
+    *Mask |= SEQUENCE_OVERFLOW;\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the FragCheck in EFI_IPSEC_SPD_DATA.\r
+  //\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check")) {\r
+    (*Data)->ProcessingPolicy->FragCheck   = TRUE;\r
+    *Mask |= FRAGMENT_CHECK;\r
+  } else if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check-")) {\r
+    (*Data)->ProcessingPolicy->FragCheck   = FALSE;\r
+    *Mask |= FRAGMENT_CHECK;\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the ProcessingPolicy in EFI_IPSEC_SPD_DATA.\r
+  //\r
+  Status = GetNumber (\r
+             L"--lifebyte",\r
+             (UINT64) -1,\r
+             &(*Data)->ProcessingPolicy->SaLifetime.ByteCount,\r
+             sizeof (UINT64),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= LIFEBYTE;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--lifetime",\r
+             (UINT64) -1,\r
+             &(*Data)->ProcessingPolicy->SaLifetime.HardLifetime,\r
+             sizeof (UINT64),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= LIFETIME;\r
+  }\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--lifetime-soft",\r
+             (UINT64) -1,\r
+             &(*Data)->ProcessingPolicy->SaLifetime.SoftLifetime,\r
+             sizeof (UINT64),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= LIFETIME_SOFT;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  (*Data)->ProcessingPolicy->Mode = EfiIPsecTransport;\r
+  Status = GetNumber (\r
+             L"--mode",\r
+             0,\r
+             &(*Data)->ProcessingPolicy->Mode,\r
+             sizeof (UINT32),\r
+             mMapIpSecMode,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= MODE;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-local");\r
+  if (ValueStr != NULL) {\r
+    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->LocalTunnelAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--tunnel-local",\r
+        ValueStr\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      *Mask |= TUNNEL_LOCAL;\r
+    }\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-remote");\r
+  if (ValueStr != NULL) {\r
+    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--tunnel-remote",\r
+        ValueStr\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      *Mask |= TUNNEL_REMOTE;\r
+    }\r
+  }\r
+\r
+  (*Data)->ProcessingPolicy->TunnelOption->DF = EfiIPsecTunnelCopyDf;\r
+  Status = GetNumber (\r
+             L"--dont-fragment",\r
+             0,\r
+             &(*Data)->ProcessingPolicy->TunnelOption->DF,\r
+             sizeof (UINT32),\r
+             mMapDfOption,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= DONT_FRAGMENT;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  (*Data)->ProcessingPolicy->Proto = EfiIPsecESP;\r
+  Status = GetNumber (\r
+             L"--ipsec-proto",\r
+             0,\r
+             &(*Data)->ProcessingPolicy->Proto,\r
+             sizeof (UINT32),\r
+             mMapIpSecProtocol,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= IPSEC_PROTO;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--encrypt-algo",\r
+             0,\r
+             &(*Data)->ProcessingPolicy->EncAlgoId,\r
+             sizeof (UINT8),\r
+             mMapEncAlgo,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= ENCRYPT_ALGO;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--auth-algo",\r
+             0,\r
+             &(*Data)->ProcessingPolicy->AuthAlgoId,\r
+             sizeof (UINT8),\r
+             mMapAuthAlgo,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= AUTH_ALGO;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Cannot check Mode against EfiIPsecTunnel, because user may want to change tunnel_remote only so the Mode is not set.\r
+  //\r
+  if ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE | DONT_FRAGMENT)) == 0) {\r
+    (*Data)->ProcessingPolicy->TunnelOption = NULL;\r
+  }\r
+\r
+  if ((*Mask & (EXT_SEQUENCE | SEQUENCE_OVERFLOW | FRAGMENT_CHECK | LIFEBYTE |\r
+                LIFETIME_SOFT | LIFETIME | MODE | TUNNEL_LOCAL | TUNNEL_REMOTE |\r
+                DONT_FRAGMENT | IPSEC_PROTO | AUTH_ALGO | ENCRYPT_ALGO)) == 0) {\r
+    if ((*Data)->Action != EfiIPsecActionProtect) {\r
+      //\r
+      // User may not provide additional parameter for Protect action, so we cannot simply set ProcessingPolicy to NULL.\r
+      //\r
+      (*Data)->ProcessingPolicy = NULL;\r
+    }\r
+  }\r
+\r
+  if (CreateNew) {\r
+    if ((*Mask & (LOCAL | REMOTE | PROTO | ACTION)) != (LOCAL | REMOTE | PROTO | ACTION)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--local --remote --proto --action"\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else if (((*Data)->Action == EfiIPsecActionProtect) &&\r
+               ((*Data)->ProcessingPolicy->Mode == EfiIPsecTunnel) &&\r
+               ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE))) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--tunnel-local --tunnel-remote"\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  return ReturnStatus;\r
+}\r
+\r
+/**\r
+  Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA through ParamPackage list.\r
+\r
+  @param[out] SaId            The pointer to the EFI_IPSEC_SA_ID structure.\r
+  @param[out] Data            The pointer to the EFI_IPSEC_SA_DATA structure.\r
+  @param[in]  ParamPackage    The pointer to the ParamPackage list.\r
+  @param[out] Mask            The pointer to the Mask.\r
+  @param[in]  CreateNew       The switch to create new.\r
+\r
+  @retval EFI_SUCCESS              Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CreateSadEntry (\r
+  OUT EFI_IPSEC_SA_ID      **SaId,\r
+  OUT EFI_IPSEC_SA_DATA    **Data,\r
+  IN  LIST_ENTRY           *ParamPackage,\r
+  OUT UINT32               *Mask,\r
+  IN  BOOLEAN              CreateNew\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  EFI_STATUS      ReturnStatus;\r
+  UINTN           AuthKeyLength;\r
+  UINTN           EncKeyLength;\r
+  CONST CHAR16    *ValueStr;\r
+  UINTN           DataSize;\r
+\r
+  Status        = EFI_SUCCESS;\r
+  ReturnStatus  = EFI_SUCCESS;\r
+  *Mask         = 0;\r
+  AuthKeyLength = 0;\r
+  EncKeyLength  = 0;\r
+\r
+  *SaId = AllocateZeroPool (sizeof (EFI_IPSEC_SA_ID));\r
+  ASSERT (*SaId != NULL);\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the Spi in EFI_IPSEC_SA_ID.\r
+  //\r
+  Status = GetNumber (L"--spi", (UINT32) -1, &(*SaId)->Spi, sizeof (UINT32), NULL, ParamPackage, FORMAT_NUMBER);\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= SPI;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the Proto in EFI_IPSEC_SA_ID.\r
+  //\r
+  Status = GetNumber (\r
+             L"--ipsec-proto",\r
+             0,\r
+             &(*SaId)->Proto,\r
+             sizeof (EFI_IPSEC_PROTOCOL_TYPE),\r
+             mMapIpSecProtocol,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= IPSEC_PROTO;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in the DestAddress in EFI_IPSEC_SA_ID.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--dest");\r
+  if (ValueStr != NULL) {\r
+    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*SaId)->DestAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--dest",\r
+        ValueStr\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      *Mask |= DEST;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in EFI_IPSEC_SA_DATA.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");\r
+  if (ValueStr != NULL) {\r
+    AuthKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");\r
+  if (ValueStr != NULL) {\r
+    EncKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);\r
+  }\r
+\r
+  //\r
+  // EFI_IPSEC_SA_DATA:\r
+  //   +------------\r
+  //   | EFI_IPSEC_SA_DATA\r
+  //   +-----------------------\r
+  //   | AuthKey\r
+  //   +-------------------------\r
+  //   | EncKey\r
+  //   +-------------------------\r
+  //   | SpdSelector\r
+  //\r
+  // Notes: To make sure the address alignment add padding after each data if needed.\r
+  //\r
+  DataSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA));\r
+  DataSize  = ALIGN_VARIABLE (DataSize + AuthKeyLength);\r
+  DataSize  = ALIGN_VARIABLE (DataSize + EncKeyLength);\r
+  DataSize  = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_SPD_SELECTOR));\r
+  DataSize  = ALIGN_VARIABLE (DataSize + sizeof (EFI_IP_ADDRESS_INFO));\r
+  DataSize += sizeof (EFI_IP_ADDRESS_INFO);\r
+\r
+\r
+\r
+  *Data = AllocateZeroPool (DataSize);\r
+  ASSERT (*Data != NULL);\r
+\r
+  (*Data)->ManualSet                    = TRUE;\r
+  (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER (((*Data) + 1), sizeof (UINTN));\r
+  (*Data)->AlgoInfo.EspAlgoInfo.EncKey  = (VOID *) ALIGN_POINTER (\r
+                                                     ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.AuthKey + AuthKeyLength),\r
+                                                     sizeof (UINTN)\r
+                                                     );\r
+  (*Data)->SpdSelector                  = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER (\r
+                                                                       ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.EncKey + EncKeyLength),\r
+                                                                       sizeof (UINTN)\r
+                                                                       );\r
+  (*Data)->SpdSelector->LocalAddress    = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (\r
+                                                                    ((UINT8 *) (*Data)->SpdSelector + sizeof (EFI_IPSEC_SPD_SELECTOR)),\r
+                                                                    sizeof (UINTN));\r
+  (*Data)->SpdSelector->RemoteAddress   = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (\r
+                                                                    (*Data)->SpdSelector->LocalAddress + 1,\r
+                                                                    sizeof (UINTN)\r
+                                                                    );\r
+\r
+  (*Data)->Mode = EfiIPsecTransport;\r
+  Status = GetNumber (\r
+             L"--mode",\r
+             0,\r
+             &(*Data)->Mode,\r
+             sizeof (EFI_IPSEC_MODE),\r
+             mMapIpSecMode,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= MODE;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // According to RFC 4303-3.3.3. The first packet sent using a given SA\r
+  // will contain a sequence number of 1.\r
+  //\r
+  (*Data)->SNCount = 1;\r
+  Status = GetNumber (\r
+             L"--sequence-number",\r
+             (UINT64) -1,\r
+             &(*Data)->SNCount,\r
+             sizeof (UINT64),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= SEQUENCE_NUMBER;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  (*Data)->AntiReplayWindows = 0;\r
+  Status = GetNumber (\r
+             L"--antireplay-window",\r
+             (UINT8) -1,\r
+             &(*Data)->AntiReplayWindows,\r
+             sizeof (UINT8),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= SEQUENCE_NUMBER;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--encrypt-algo",\r
+             0,\r
+             &(*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId,\r
+             sizeof (UINT8),\r
+             mMapEncAlgo,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= ENCRYPT_ALGO;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");\r
+  if (ValueStr != NULL ) {\r
+    (*Data)->AlgoInfo.EspAlgoInfo.EncKeyLength = EncKeyLength;\r
+    CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.EncKey, ValueStr, EncKeyLength);\r
+    *Mask |= ENCRYPT_KEY;\r
+  } else {\r
+    (*Data)->AlgoInfo.EspAlgoInfo.EncKey = NULL;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--auth-algo",\r
+             0,\r
+             &(*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId,\r
+             sizeof (UINT8),\r
+             mMapAuthAlgo,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= AUTH_ALGO;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");\r
+  if (ValueStr != NULL) {\r
+    (*Data)->AlgoInfo.EspAlgoInfo.AuthKeyLength = AuthKeyLength;\r
+    CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.AuthKey, ValueStr, AuthKeyLength);\r
+    *Mask |= AUTH_KEY;\r
+  } else {\r
+    (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = NULL;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--lifebyte",\r
+             (UINT64) -1,\r
+             &(*Data)->SaLifetime.ByteCount,\r
+             sizeof (UINT64),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= LIFEBYTE;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--lifetime",\r
+             (UINT64) -1,\r
+             &(*Data)->SaLifetime.HardLifetime,\r
+             sizeof (UINT64),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= LIFETIME;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--lifetime-soft",\r
+             (UINT64) -1,\r
+             &(*Data)->SaLifetime.SoftLifetime,\r
+             sizeof (UINT64),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= LIFETIME_SOFT;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--path-mtu",\r
+             (UINT32) -1,\r
+             &(*Data)->PathMTU,\r
+             sizeof (UINT32),\r
+             NULL,\r
+             ParamPackage,\r
+             FORMAT_NUMBER\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= PATH_MTU;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ReturnStatus = CreateSpdSelector ((*Data)->SpdSelector, ParamPackage, Mask);\r
+\r
+  if (CreateNew) {\r
+    if ((*Mask & (SPI | IPSEC_PROTO | DEST)) != (SPI | IPSEC_PROTO | DEST)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--spi --ipsec-proto --dest"\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      if ((*SaId)->Proto == EfiIPsecAH) {\r
+        if ((*Mask & AUTH_ALGO) == 0) {\r
+          ShellPrintHiiEx (\r
+            -1,\r
+            -1,\r
+            NULL,\r
+            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+            mHiiHandle,\r
+            mAppName,\r
+            L"--auth-algo"\r
+            );\r
+          ReturnStatus = EFI_INVALID_PARAMETER;\r
+        } else if ((*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId != EFI_IPSEC_AALG_NONE && (*Mask & AUTH_KEY) == 0) {\r
+          ShellPrintHiiEx (\r
+            -1,\r
+            -1,\r
+            NULL,\r
+            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+            mHiiHandle,\r
+            mAppName,\r
+            L"--auth-key"\r
+            );\r
+          ReturnStatus = EFI_INVALID_PARAMETER;\r
+        }\r
+      } else {\r
+        if ((*Mask & ENCRYPT_ALGO) == 0) {\r
+          ShellPrintHiiEx (\r
+            -1,\r
+            -1,\r
+            NULL,\r
+            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+            mHiiHandle,\r
+            mAppName,\r
+            L"--encrypt-algo"\r
+            );\r
+          ReturnStatus = EFI_INVALID_PARAMETER;\r
+        } else if ((*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (*Mask & ENCRYPT_KEY) == 0) {\r
+          ShellPrintHiiEx (\r
+            -1,\r
+            -1,\r
+            NULL,\r
+            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+            mHiiHandle,\r
+            mAppName,\r
+            L"--encrypt-key"\r
+            );\r
+          ReturnStatus = EFI_INVALID_PARAMETER;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  return ReturnStatus;\r
+}\r
+\r
+/**\r
+  Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA through ParamPackage list.\r
+\r
+  @param[out] PadId           The pointer to the EFI_IPSEC_PAD_ID structure.\r
+  @param[out] Data            The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+  @param[in]  ParamPackage    The pointer to the ParamPackage list.\r
+  @param[out] Mask            The pointer to the Mask.\r
+  @param[in]  CreateNew       The switch to create new.\r
+\r
+  @retval EFI_SUCCESS              Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CreatePadEntry (\r
+  OUT EFI_IPSEC_PAD_ID      **PadId,\r
+  OUT EFI_IPSEC_PAD_DATA    **Data,\r
+  IN  LIST_ENTRY            *ParamPackage,\r
+  OUT UINT32                *Mask,\r
+  IN  BOOLEAN               CreateNew\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  EFI_STATUS         ReturnStatus;\r
+  EFI_FILE_HANDLE    FileHandle;\r
+  UINT64             FileSize;\r
+  UINTN              AuthDataLength;\r
+  UINTN              RevocationDataLength;\r
+  UINTN              DataLength;\r
+  UINTN              Index;\r
+  CONST CHAR16       *ValueStr;\r
+  UINTN              DataSize;\r
+\r
+  Status               = EFI_SUCCESS;\r
+  ReturnStatus         = EFI_SUCCESS;\r
+  *Mask                = 0;\r
+  AuthDataLength       = 0;\r
+  RevocationDataLength = 0;\r
+\r
+  *PadId = AllocateZeroPool (sizeof (EFI_IPSEC_PAD_ID));\r
+  ASSERT (*PadId != NULL);\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_ID.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-address");\r
+  if (ValueStr != NULL) {\r
+    (*PadId)->PeerIdValid = FALSE;\r
+    Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &(*PadId)->Id.IpAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--peer-address",\r
+        ValueStr\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else {\r
+      *Mask |= PEER_ADDRESS;\r
+    }\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-id");\r
+  if (ValueStr != NULL) {\r
+    (*PadId)->PeerIdValid = TRUE;\r
+    StrnCpy ((CHAR16 *) (*PadId)->Id.PeerId, ValueStr, ARRAY_SIZE ((*PadId)->Id.PeerId) - 1);\r
+    *Mask |= PEER_ID;\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");\r
+  if (ValueStr != NULL) {\r
+    if (ValueStr[0] == L'@') {\r
+      //\r
+      // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"\r
+      //\r
+      Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (\r
+          -1,\r
+          -1,\r
+          NULL,\r
+          STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),\r
+          mHiiHandle,\r
+          mAppName,\r
+          &ValueStr[1]\r
+          );\r
+        ReturnStatus = EFI_INVALID_PARAMETER;\r
+      } else {\r
+        Status = ShellGetFileSize (FileHandle, &FileSize);\r
+        ShellCloseFile (&FileHandle);\r
+        if (EFI_ERROR (Status)) {\r
+          ShellPrintHiiEx (\r
+            -1,\r
+            -1,\r
+            NULL,\r
+            STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),\r
+            mHiiHandle,\r
+            mAppName,\r
+            &ValueStr[1]\r
+            );\r
+          ReturnStatus = EFI_INVALID_PARAMETER;\r
+        } else {\r
+          AuthDataLength = (UINTN) FileSize;\r
+        }\r
+      }\r
+    } else {\r
+      AuthDataLength = StrLen (ValueStr);\r
+    }\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");\r
+  if (ValueStr != NULL) {\r
+    RevocationDataLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);\r
+  }\r
+\r
+  //\r
+  // Allocate Buffer for Data. Add padding after each struct to make sure the alignment\r
+  // in different Arch.\r
+  //\r
+  DataSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));\r
+  DataSize  = ALIGN_VARIABLE (DataSize + AuthDataLength);\r
+  DataSize += RevocationDataLength;\r
+\r
+  *Data = AllocateZeroPool (DataSize);\r
+  ASSERT (*Data != NULL);\r
+\r
+  (*Data)->AuthData       = (VOID *) ALIGN_POINTER ((*Data + 1), sizeof (UINTN));\r
+  (*Data)->RevocationData = (VOID *) ALIGN_POINTER (((UINT8 *) (*Data + 1) + AuthDataLength), sizeof (UINTN));\r
+  (*Data)->AuthProtocol   = EfiIPsecAuthProtocolIKEv1;\r
+\r
+  //\r
+  // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_DATA.\r
+  //\r
+  Status = GetNumber (\r
+             L"--auth-proto",\r
+             0,\r
+             &(*Data)->AuthProtocol,\r
+             sizeof (EFI_IPSEC_AUTH_PROTOCOL_TYPE),\r
+             mMapAuthProto,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= AUTH_PROTO;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = GetNumber (\r
+             L"--auth-method",\r
+             0,\r
+             &(*Data)->AuthMethod,\r
+             sizeof (EFI_IPSEC_AUTH_METHOD),\r
+             mMapAuthMethod,\r
+             ParamPackage,\r
+             FORMAT_STRING\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    *Mask |= AUTH_METHOD;\r
+  }\r
+\r
+  if (Status == EFI_INVALID_PARAMETER) {\r
+    ReturnStatus = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id")) {\r
+    (*Data)->IkeIdFlag = TRUE;\r
+    *Mask |= IKE_ID;\r
+  }\r
+\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id-")) {\r
+    (*Data)->IkeIdFlag = FALSE;\r
+    *Mask |= IKE_ID;\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");\r
+  if (ValueStr != NULL) {\r
+    if (ValueStr[0] == L'@') {\r
+      //\r
+      // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"\r
+      //\r
+\r
+      Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (\r
+          -1,\r
+          -1,\r
+          NULL,\r
+          STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),\r
+          mHiiHandle,\r
+          mAppName,\r
+          &ValueStr[1]\r
+          );\r
+        ReturnStatus = EFI_INVALID_PARAMETER;\r
+        (*Data)->AuthData = NULL;\r
+      } else {\r
+        DataLength = AuthDataLength;\r
+        Status = ShellReadFile (FileHandle, &DataLength, (*Data)->AuthData);\r
+        ShellCloseFile (&FileHandle);\r
+        if (EFI_ERROR (Status)) {\r
+          ShellPrintHiiEx (\r
+            -1,\r
+            -1,\r
+            NULL,\r
+            STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),\r
+            mHiiHandle,\r
+            mAppName,\r
+            &ValueStr[1]\r
+            );\r
+          ReturnStatus = EFI_INVALID_PARAMETER;\r
+          (*Data)->AuthData = NULL;\r
+        } else {\r
+          ASSERT (DataLength == AuthDataLength);\r
+          *Mask |= AUTH_DATA;\r
+        }\r
+      }\r
+    } else {\r
+      for (Index = 0; Index < AuthDataLength; Index++) {\r
+        ((CHAR8 *) (*Data)->AuthData)[Index] = (CHAR8) ValueStr[Index];\r
+      }\r
+      (*Data)->AuthDataSize = AuthDataLength;\r
+      *Mask |= AUTH_DATA;\r
+    }\r
+  }\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");\r
+  if (ValueStr != NULL) {\r
+    CopyMem ((*Data)->RevocationData, ValueStr, RevocationDataLength);\r
+    (*Data)->RevocationDataSize = RevocationDataLength;\r
+    *Mask |= REVOCATION_DATA;\r
+  } else {\r
+    (*Data)->RevocationData = NULL;\r
+  }\r
+\r
+  if (CreateNew) {\r
+    if ((*Mask & (PEER_ID | PEER_ADDRESS)) == 0) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--peer-id --peer-address"\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    } else if ((*Mask & (AUTH_METHOD | AUTH_DATA)) != (AUTH_METHOD | AUTH_DATA)) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--auth-method --auth-data"\r
+        );\r
+      ReturnStatus = EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  return ReturnStatus;\r
+}\r
+\r
+CREATE_POLICY_ENTRY mCreatePolicyEntry[] = {\r
+  (CREATE_POLICY_ENTRY) CreateSpdEntry,\r
+  (CREATE_POLICY_ENTRY) CreateSadEntry,\r
+  (CREATE_POLICY_ENTRY) CreatePadEntry\r
+};\r
+\r
+/**\r
+  Combine old SPD entry with new SPD entry.\r
+\r
+  @param[in, out] OldSelector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+  @param[in, out] OldData        The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+  @param[in]      NewSelector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+  @param[in]      NewData        The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+  @param[in]      Mask           The pointer to the Mask.\r
+  @param[out]     CreateNew      The switch to create new.\r
+\r
+  @retval EFI_SUCCESS              Combined successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CombineSpdEntry (\r
+  IN OUT EFI_IPSEC_SPD_SELECTOR    *OldSelector,\r
+  IN OUT EFI_IPSEC_SPD_DATA        *OldData,\r
+  IN     EFI_IPSEC_SPD_SELECTOR    *NewSelector,\r
+  IN     EFI_IPSEC_SPD_DATA        *NewData,\r
+  IN     UINT32                    Mask,\r
+     OUT BOOLEAN                   *CreateNew\r
+  )\r
+{\r
+\r
+  //\r
+  // Process Selector\r
+  //\r
+  *CreateNew = FALSE;\r
+  if ((Mask & LOCAL) == 0) {\r
+    NewSelector->LocalAddressCount = OldSelector->LocalAddressCount;\r
+    NewSelector->LocalAddress      = OldSelector->LocalAddress;\r
+  } else if ((NewSelector->LocalAddressCount != OldSelector->LocalAddressCount) ||\r
+             (CompareMem (NewSelector->LocalAddress, OldSelector->LocalAddress, NewSelector->LocalAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {\r
+    *CreateNew = TRUE;\r
+  }\r
+\r
+  if ((Mask & REMOTE) == 0) {\r
+    NewSelector->RemoteAddressCount = OldSelector->RemoteAddressCount;\r
+    NewSelector->RemoteAddress      = OldSelector->RemoteAddress;\r
+  } else if ((NewSelector->RemoteAddressCount != OldSelector->RemoteAddressCount) ||\r
+             (CompareMem (NewSelector->RemoteAddress, OldSelector->RemoteAddress, NewSelector->RemoteAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {\r
+    *CreateNew = TRUE;\r
+  }\r
+\r
+  if ((Mask & PROTO) == 0) {\r
+    NewSelector->NextLayerProtocol = OldSelector->NextLayerProtocol;\r
+  } else if (NewSelector->NextLayerProtocol != OldSelector->NextLayerProtocol) {\r
+    *CreateNew = TRUE;\r
+  }\r
+\r
+  switch (NewSelector->NextLayerProtocol) {\r
+    case EFI_IP4_PROTO_TCP:\r
+    case EFI_IP4_PROTO_UDP:\r
+      if ((Mask & LOCAL_PORT) == 0) {\r
+        NewSelector->LocalPort      = OldSelector->LocalPort;\r
+        NewSelector->LocalPortRange = OldSelector->LocalPortRange;\r
+      } else if ((NewSelector->LocalPort != OldSelector->LocalPort) ||\r
+        (NewSelector->LocalPortRange != OldSelector->LocalPortRange)) {\r
+        *CreateNew = TRUE;\r
+      }\r
+\r
+      if ((Mask & REMOTE_PORT) == 0) {\r
+        NewSelector->RemotePort      = OldSelector->RemotePort;\r
+        NewSelector->RemotePortRange = OldSelector->RemotePortRange;\r
+      } else if ((NewSelector->RemotePort != OldSelector->RemotePort) ||\r
+        (NewSelector->RemotePortRange != OldSelector->RemotePortRange)) {\r
+        *CreateNew = TRUE;\r
+      }\r
+      break;\r
+\r
+    case EFI_IP4_PROTO_ICMP:\r
+      if ((Mask & ICMP_TYPE) == 0) {\r
+        NewSelector->LocalPort = OldSelector->LocalPort;\r
+      } else if (NewSelector->LocalPort != OldSelector->LocalPort) {\r
+        *CreateNew = TRUE;\r
+      }\r
+\r
+      if ((Mask & ICMP_CODE) == 0) {\r
+        NewSelector->RemotePort = OldSelector->RemotePort;\r
+      } else if (NewSelector->RemotePort != OldSelector->RemotePort) {\r
+        *CreateNew = TRUE;\r
+      }\r
+      break;\r
+  }\r
+  //\r
+  // Process Data\r
+  //\r
+  if ((Mask & NAME) != 0) {\r
+    AsciiStrCpy ((CHAR8 *) OldData->Name, (CHAR8 *) NewData->Name);\r
+  }\r
+\r
+  if ((Mask & PACKET_FLAG) != 0) {\r
+    OldData->PackageFlag = NewData->PackageFlag;\r
+  }\r
+\r
+  if ((Mask & ACTION) != 0) {\r
+    OldData->Action = NewData->Action;\r
+  }\r
+\r
+  if (OldData->Action != EfiIPsecActionProtect) {\r
+    OldData->ProcessingPolicy = NULL;\r
+  } else {\r
+    //\r
+    // Protect\r
+    //\r
+    if (OldData->ProcessingPolicy == NULL) {\r
+      //\r
+      // Just point to new data if originally NULL.\r
+      //\r
+      OldData->ProcessingPolicy = NewData->ProcessingPolicy;\r
+      if (OldData->ProcessingPolicy->Mode == EfiIPsecTunnel &&\r
+          (Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)\r
+        ) {\r
+        //\r
+        // Change to Protect action and Tunnel mode, but without providing local/remote tunnel address.\r
+        //\r
+        ShellPrintHiiEx (\r
+          -1,\r
+          -1,\r
+          NULL,\r
+          STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+          mHiiHandle,\r
+          mAppName,\r
+          L"--tunnel-local --tunnel-remote"\r
+          );\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    } else {\r
+      //\r
+      // Modify some of the data.\r
+      //\r
+      if ((Mask & EXT_SEQUENCE) != 0) {\r
+        OldData->ProcessingPolicy->ExtSeqNum = NewData->ProcessingPolicy->ExtSeqNum;\r
+      }\r
+\r
+      if ((Mask & SEQUENCE_OVERFLOW) != 0) {\r
+        OldData->ProcessingPolicy->SeqOverflow = NewData->ProcessingPolicy->SeqOverflow;\r
+      }\r
+\r
+      if ((Mask & FRAGMENT_CHECK) != 0) {\r
+        OldData->ProcessingPolicy->FragCheck = NewData->ProcessingPolicy->FragCheck;\r
+      }\r
+\r
+      if ((Mask & LIFEBYTE) != 0) {\r
+        OldData->ProcessingPolicy->SaLifetime.ByteCount = NewData->ProcessingPolicy->SaLifetime.ByteCount;\r
+      }\r
+\r
+      if ((Mask & LIFETIME_SOFT) != 0) {\r
+        OldData->ProcessingPolicy->SaLifetime.SoftLifetime = NewData->ProcessingPolicy->SaLifetime.SoftLifetime;\r
+      }\r
+\r
+      if ((Mask & LIFETIME) != 0) {\r
+        OldData->ProcessingPolicy->SaLifetime.HardLifetime = NewData->ProcessingPolicy->SaLifetime.HardLifetime;\r
+      }\r
+\r
+      if ((Mask & MODE) != 0) {\r
+        OldData->ProcessingPolicy->Mode = NewData->ProcessingPolicy->Mode;\r
+      }\r
+\r
+      if ((Mask & IPSEC_PROTO) != 0) {\r
+        OldData->ProcessingPolicy->Proto = NewData->ProcessingPolicy->Proto;\r
+      }\r
+\r
+      if ((Mask & AUTH_ALGO) != 0) {\r
+        OldData->ProcessingPolicy->AuthAlgoId = NewData->ProcessingPolicy->AuthAlgoId;\r
+      }\r
+\r
+      if ((Mask & ENCRYPT_ALGO) != 0) {\r
+        OldData->ProcessingPolicy->EncAlgoId = NewData->ProcessingPolicy->EncAlgoId;\r
+      }\r
+\r
+      if (OldData->ProcessingPolicy->Mode != EfiIPsecTunnel) {\r
+        OldData->ProcessingPolicy->TunnelOption = NULL;\r
+      } else {\r
+        if (OldData->ProcessingPolicy->TunnelOption == NULL) {\r
+          //\r
+          // Set from Transport mode to Tunnel mode, should ensure TUNNEL_LOCAL & TUNNEL_REMOTE both exists.\r
+          //\r
+          if ((Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)) {\r
+            ShellPrintHiiEx (\r
+              -1,\r
+              -1,\r
+              NULL,\r
+              STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+              mHiiHandle,\r
+              mAppName,\r
+              L"--tunnel-local --tunnel-remote"\r
+              );\r
+            return EFI_INVALID_PARAMETER;\r
+          }\r
+\r
+          OldData->ProcessingPolicy->TunnelOption = NewData->ProcessingPolicy->TunnelOption;\r
+        } else {\r
+          if ((Mask & TUNNEL_LOCAL) != 0) {\r
+            CopyMem (\r
+              &OldData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,\r
+              &NewData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,\r
+              sizeof (EFI_IP_ADDRESS)\r
+              );\r
+          }\r
+\r
+          if ((Mask & TUNNEL_REMOTE) != 0) {\r
+            CopyMem (\r
+              &OldData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,\r
+              &NewData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,\r
+              sizeof (EFI_IP_ADDRESS)\r
+              );\r
+          }\r
+\r
+          if ((Mask & DONT_FRAGMENT) != 0) {\r
+            OldData->ProcessingPolicy->TunnelOption->DF = NewData->ProcessingPolicy->TunnelOption->DF;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Combine old SAD entry with new SAD entry.\r
+\r
+  @param[in, out] OldSaId      The pointer to the EFI_IPSEC_SA_ID structure.\r
+  @param[in, out] OldData      The pointer to the EFI_IPSEC_SA_DATA structure.\r
+  @param[in]      NewSaId      The pointer to the EFI_IPSEC_SA_ID structure.\r
+  @param[in]      NewData      The pointer to the EFI_IPSEC_SA_DATA structure.\r
+  @param[in]      Mask         The pointer to the Mask.\r
+  @param[out]     CreateNew    The switch to create new.\r
+\r
+  @retval EFI_SUCCESS              Combined successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CombineSadEntry (\r
+  IN OUT EFI_IPSEC_SA_ID      *OldSaId,\r
+  IN OUT EFI_IPSEC_SA_DATA    *OldData,\r
+  IN     EFI_IPSEC_SA_ID      *NewSaId,\r
+  IN     EFI_IPSEC_SA_DATA    *NewData,\r
+  IN     UINT32               Mask,\r
+     OUT BOOLEAN              *CreateNew\r
+  )\r
+{\r
+\r
+  *CreateNew = FALSE;\r
+\r
+  if ((Mask & SPI) == 0) {\r
+    NewSaId->Spi = OldSaId->Spi;\r
+  } else if (NewSaId->Spi != OldSaId->Spi) {\r
+    *CreateNew = TRUE;\r
+  }\r
+\r
+  if ((Mask & IPSEC_PROTO) == 0) {\r
+    NewSaId->Proto = OldSaId->Proto;\r
+  } else if (NewSaId->Proto != OldSaId->Proto) {\r
+    *CreateNew = TRUE;\r
+  }\r
+\r
+  if ((Mask & DEST) == 0) {\r
+    CopyMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS));\r
+  } else if (CompareMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS)) != 0) {\r
+    *CreateNew = TRUE;\r
+  }\r
+\r
+  //\r
+  // Process SA_DATA.\r
+  //\r
+  if ((Mask & MODE) != 0) {\r
+    OldData->Mode = NewData->Mode;\r
+  }\r
+\r
+  if ((Mask & SEQUENCE_NUMBER) != 0) {\r
+    OldData->SNCount = NewData->SNCount;\r
+  }\r
+\r
+  if ((Mask & ANTIREPLAY_WINDOW) != 0) {\r
+    OldData->AntiReplayWindows = NewData->AntiReplayWindows;\r
+  }\r
+\r
+  if ((Mask & AUTH_ALGO) != 0) {\r
+    OldData->AlgoInfo.EspAlgoInfo.AuthAlgoId    = NewData->AlgoInfo.EspAlgoInfo.AuthAlgoId;\r
+  }\r
+\r
+  if ((Mask & AUTH_KEY) != 0) {\r
+    OldData->AlgoInfo.EspAlgoInfo.AuthKey       = NewData->AlgoInfo.EspAlgoInfo.AuthKey;\r
+    OldData->AlgoInfo.EspAlgoInfo.AuthKeyLength = NewData->AlgoInfo.EspAlgoInfo.AuthKeyLength;\r
+  }\r
+\r
+  if ((Mask & ENCRYPT_ALGO) != 0) {\r
+    OldData->AlgoInfo.EspAlgoInfo.EncAlgoId     = NewData->AlgoInfo.EspAlgoInfo.EncAlgoId;\r
+  }\r
+\r
+  if ((Mask & ENCRYPT_KEY) != 0) {\r
+    OldData->AlgoInfo.EspAlgoInfo.EncKey        = NewData->AlgoInfo.EspAlgoInfo.EncKey;\r
+    OldData->AlgoInfo.EspAlgoInfo.EncKeyLength  = NewData->AlgoInfo.EspAlgoInfo.EncKeyLength;\r
+  }\r
+\r
+  if (NewSaId->Proto == EfiIPsecAH) {\r
+    if ((Mask & (ENCRYPT_ALGO | ENCRYPT_KEY)) != 0) {\r
+      //\r
+      // Should not provide encrypt_* if AH.\r
+      //\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_UNWANTED_PARAMETER),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--encrypt-algo --encrypt-key"\r
+        );\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  if (NewSaId->Proto == EfiIPsecESP && OldSaId->Proto == EfiIPsecAH) {\r
+    //\r
+    // AH -> ESP\r
+    // Should provide encrypt_algo at least.\r
+    //\r
+    if ((Mask & ENCRYPT_ALGO) == 0) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--encrypt-algo"\r
+        );\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Encrypt_key should be provided if algorithm is not NONE.\r
+    //\r
+    if (NewData->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (Mask & ENCRYPT_KEY) == 0) {\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+        mHiiHandle,\r
+        mAppName,\r
+        L"--encrypt-algo"\r
+        );\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  if ((Mask & LIFEBYTE) != 0) {\r
+    OldData->SaLifetime.ByteCount    = NewData->SaLifetime.ByteCount;\r
+  }\r
+\r
+  if ((Mask & LIFETIME_SOFT) != 0) {\r
+    OldData->SaLifetime.SoftLifetime = NewData->SaLifetime.SoftLifetime;\r
+  }\r
+\r
+  if ((Mask & LIFETIME) != 0) {\r
+    OldData->SaLifetime.HardLifetime = NewData->SaLifetime.HardLifetime;\r
+  }\r
+\r
+  if ((Mask & PATH_MTU) != 0) {\r
+    OldData->PathMTU                 = NewData->PathMTU;\r
+  }\r
+  //\r
+  // Process SpdSelector.\r
+  //\r
+  if (OldData->SpdSelector == NULL) {\r
+    if ((Mask & (LOCAL | REMOTE | PROTO | LOCAL_PORT | REMOTE_PORT | ICMP_TYPE | ICMP_CODE)) != 0) {\r
+      if ((Mask & (LOCAL | REMOTE | PROTO)) != (LOCAL | REMOTE | PROTO)) {\r
+        ShellPrintHiiEx (\r
+          -1,\r
+          -1,\r
+          NULL,\r
+          STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+          mHiiHandle,\r
+          mAppName,\r
+          L"--local --remote --proto"\r
+          );\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      OldData->SpdSelector = NewData->SpdSelector;\r
+    }\r
+  } else {\r
+    if ((Mask & LOCAL) != 0) {\r
+      OldData->SpdSelector->LocalAddressCount  = NewData->SpdSelector->LocalAddressCount;\r
+      OldData->SpdSelector->LocalAddress       = NewData->SpdSelector->LocalAddress;\r
+    }\r
+\r
+    if ((Mask & REMOTE) != 0) {\r
+      OldData->SpdSelector->RemoteAddressCount = NewData->SpdSelector->RemoteAddressCount;\r
+      OldData->SpdSelector->RemoteAddress      = NewData->SpdSelector->RemoteAddress;\r
+    }\r
+\r
+    if ((Mask & PROTO) != 0) {\r
+      OldData->SpdSelector->NextLayerProtocol  = NewData->SpdSelector->NextLayerProtocol;\r
+    }\r
+\r
+    if (OldData->SpdSelector != NULL) {\r
+      switch (OldData->SpdSelector->NextLayerProtocol) {\r
+        case EFI_IP4_PROTO_TCP:\r
+        case EFI_IP4_PROTO_UDP:\r
+          if ((Mask & LOCAL_PORT) != 0) {\r
+            OldData->SpdSelector->LocalPort  = NewData->SpdSelector->LocalPort;\r
+          }\r
+\r
+          if ((Mask & REMOTE_PORT) != 0) {\r
+            OldData->SpdSelector->RemotePort = NewData->SpdSelector->RemotePort;\r
+          }\r
+          break;\r
+\r
+        case EFI_IP4_PROTO_ICMP:\r
+          if ((Mask & ICMP_TYPE) != 0) {\r
+            OldData->SpdSelector->LocalPort  = (UINT8) NewData->SpdSelector->LocalPort;\r
+          }\r
+\r
+          if ((Mask & ICMP_CODE) != 0) {\r
+            OldData->SpdSelector->RemotePort = (UINT8) NewData->SpdSelector->RemotePort;\r
+          }\r
+          break;\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Combine old PAD entry with new PAD entry.\r
+\r
+  @param[in, out] OldPadId     The pointer to the EFI_IPSEC_PAD_ID structure.\r
+  @param[in, out] OldData      The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+  @param[in]      NewPadId     The pointer to the EFI_IPSEC_PAD_ID structure.\r
+  @param[in]      NewData      The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+  @param[in]      Mask         The pointer to the Mask.\r
+  @param[out]     CreateNew    The switch to create new.\r
+\r
+  @retval EFI_SUCCESS              Combined successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CombinePadEntry (\r
+  IN OUT EFI_IPSEC_PAD_ID      *OldPadId,\r
+  IN OUT EFI_IPSEC_PAD_DATA    *OldData,\r
+  IN     EFI_IPSEC_PAD_ID      *NewPadId,\r
+  IN     EFI_IPSEC_PAD_DATA    *NewData,\r
+  IN     UINT32                Mask,\r
+     OUT BOOLEAN               *CreateNew\r
+  )\r
+{\r
+\r
+  *CreateNew = FALSE;\r
+\r
+  if ((Mask & (PEER_ID | PEER_ADDRESS)) == 0) {\r
+    CopyMem (NewPadId, OldPadId, sizeof (EFI_IPSEC_PAD_ID));\r
+  } else {\r
+    if ((Mask & PEER_ID) != 0) {\r
+      if (OldPadId->PeerIdValid) {\r
+        if (StrCmp ((CONST CHAR16 *) OldPadId->Id.PeerId, (CONST CHAR16 *) NewPadId->Id.PeerId) != 0) {\r
+          *CreateNew = TRUE;\r
+        }\r
+      } else {\r
+        *CreateNew = TRUE;\r
+      }\r
+    } else {\r
+      //\r
+      // MASK & PEER_ADDRESS\r
+      //\r
+      if (OldPadId->PeerIdValid) {\r
+        *CreateNew = TRUE;\r
+      } else {\r
+        if ((CompareMem (&OldPadId->Id.IpAddress.Address, &NewPadId->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0) ||\r
+            (OldPadId->Id.IpAddress.PrefixLength != NewPadId->Id.IpAddress.PrefixLength)) {\r
+          *CreateNew = TRUE;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  if ((Mask & AUTH_PROTO) != 0) {\r
+    OldData->AuthProtocol = NewData->AuthProtocol;\r
+  }\r
+\r
+  if ((Mask & AUTH_METHOD) != 0) {\r
+    OldData->AuthMethod = NewData->AuthMethod;\r
+  }\r
+\r
+  if ((Mask & IKE_ID) != 0) {\r
+    OldData->IkeIdFlag = NewData->IkeIdFlag;\r
+  }\r
+\r
+  if ((Mask & AUTH_DATA) != 0) {\r
+    OldData->AuthDataSize = NewData->AuthDataSize;\r
+    OldData->AuthData     = NewData->AuthData;\r
+  }\r
+\r
+  if ((Mask & REVOCATION_DATA) != 0) {\r
+    OldData->RevocationDataSize = NewData->RevocationDataSize;\r
+    OldData->RevocationData     = NewData->RevocationData;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+COMBINE_POLICY_ENTRY mCombinePolicyEntry[] = {\r
+  (COMBINE_POLICY_ENTRY) CombineSpdEntry,\r
+  (COMBINE_POLICY_ENTRY) CombineSadEntry,\r
+  (COMBINE_POLICY_ENTRY) CombinePadEntry\r
+};\r
+\r
+/**\r
+  Edit entry information in the database.\r
+\r
+  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.\r
+  @param[in] Data        The pointer to the data.\r
+  @param[in] Context     The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.\r
+\r
+  @retval EFI_SUCCESS    Continue the iteration.\r
+  @retval EFI_ABORTED    Abort the iteration.\r
+**/\r
+EFI_STATUS\r
+EditOperatePolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR    *Selector,\r
+  IN VOID                         *Data,\r
+  IN EDIT_POLICY_ENTRY_CONTEXT    *Context\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+  BOOLEAN       CreateNew;\r
+\r
+  if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {\r
+    ASSERT (Context->DataType < 3);\r
+\r
+    Status = mCombinePolicyEntry[Context->DataType] (\r
+               Selector,\r
+               Data,\r
+               Context->Selector,\r
+               Context->Data,\r
+               Context->Mask,\r
+               &CreateNew\r
+               );\r
+    if (!EFI_ERROR (Status)) {\r
+      if (CreateNew) {\r
+        //\r
+        // Insert new entry before old entry\r
+        //\r
+        Status = mIpSecConfig->SetData (\r
+                                 mIpSecConfig,\r
+                                 Context->DataType,\r
+                                 Context->Selector,\r
+                                 Data,\r
+                                 Selector\r
+                                 );\r
+        ASSERT_EFI_ERROR (Status);\r
+        //\r
+        // Delete old entry\r
+        //\r
+        Status = mIpSecConfig->SetData (\r
+                                 mIpSecConfig,\r
+                                 Context->DataType,\r
+                                 Selector,\r
+                                 NULL,\r
+                                 NULL\r
+                                 );\r
+        ASSERT_EFI_ERROR (Status);\r
+      } else {\r
+        Status = mIpSecConfig->SetData (\r
+                                 mIpSecConfig,\r
+                                 Context->DataType,\r
+                                 Context->Selector,\r
+                                 Data,\r
+                                 NULL\r
+                                 );\r
+      }\r
+    }\r
+\r
+    Context->Status = Status;\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Edit entry information in database according to datatype.\r
+\r
+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS             Edit entry information successfully.\r
+  @retval EFI_NOT_FOUND           Can't find the specified entry.\r
+  @retval Others                  Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+EditPolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN LIST_ENTRY                    *ParamPackage\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EDIT_POLICY_ENTRY_CONTEXT    Context;\r
+  CONST CHAR16                 *ValueStr;\r
+\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");\r
+  if (ValueStr == NULL) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);\r
+  if (!EFI_ERROR (Status)) {\r
+    Context.DataType = DataType;\r
+    Context.Status   = EFI_NOT_FOUND;\r
+    Status = mCreatePolicyEntry[DataType] (&Context.Selector, &Context.Data, ParamPackage, &Context.Mask, FALSE);\r
+    if (!EFI_ERROR (Status)) {\r
+      ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) EditOperatePolicyEntry, &Context);\r
+      Status = Context.Status;\r
+    }\r
+\r
+    if (Context.Selector != NULL) {\r
+      gBS->FreePool (Context.Selector);\r
+    }\r
+\r
+    if (Context.Data != NULL) {\r
+      gBS->FreePool (Context.Data);\r
+    }\r
+  }\r
+\r
+  if (Status == EFI_NOT_FOUND) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);\r
+  } else if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_EDIT_FAILED), mHiiHandle, mAppName);\r
+  }\r
+\r
+  return Status;\r
+\r
+}\r
+\r
+/**\r
+  Insert entry information in database.\r
+\r
+  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.\r
+  @param[in] Data        The pointer to the data.\r
+  @param[in] Context     The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.\r
+\r
+  @retval EFI_SUCCESS    Continue the iteration.\r
+  @retval EFI_ABORTED    Abort the iteration.\r
+**/\r
+EFI_STATUS\r
+InsertPolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR      *Selector,\r
+  IN VOID                           *Data,\r
+  IN INSERT_POLICY_ENTRY_CONTEXT    *Context\r
+  )\r
+{\r
+  //\r
+  // Found the entry which we want to insert before.\r
+  //\r
+  if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {\r
+\r
+    Context->Status = mIpSecConfig->SetData (\r
+                                      mIpSecConfig,\r
+                                      Context->DataType,\r
+                                      Context->Selector,\r
+                                      Context->Data,\r
+                                      Selector\r
+                                      );\r
+    //\r
+    // Abort the iteration after the insertion.\r
+    //\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Insert or add entry information in database according to datatype.\r
+\r
+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS             Insert or add entry information successfully.\r
+  @retval EFI_NOT_FOUND           Can't find the specified entry.\r
+  @retval EFI_BUFFER_TOO_SMALL    The entry already existed.\r
+  @retval EFI_UNSUPPORTED         The operation is not supported.\r
+  @retval Others                  Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+AddOrInsertPolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN LIST_ENTRY                    *ParamPackage\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  EFI_IPSEC_CONFIG_SELECTOR      *Selector;\r
+  VOID                           *Data;\r
+  INSERT_POLICY_ENTRY_CONTEXT    Context;\r
+  UINT32                         Mask;\r
+  UINTN                          DataSize;\r
+  CONST CHAR16                   *ValueStr;\r
+\r
+  Status = mCreatePolicyEntry[DataType] (&Selector, &Data, ParamPackage, &Mask, TRUE);\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Find if the Selector to be inserted already exists.\r
+    //\r
+    DataSize = 0;\r
+    Status = mIpSecConfig->GetData (\r
+                             mIpSecConfig,\r
+                             DataType,\r
+                             Selector,\r
+                             &DataSize,\r
+                             NULL\r
+                             );\r
+    if (Status == EFI_BUFFER_TOO_SMALL) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_EXISTS), mHiiHandle, mAppName);\r
+    } else if (ShellCommandLineGetFlag (ParamPackage, L"-a")) {\r
+      Status = mIpSecConfig->SetData (\r
+                               mIpSecConfig,\r
+                               DataType,\r
+                               Selector,\r
+                               Data,\r
+                               NULL\r
+                               );\r
+    } else {\r
+      ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");\r
+      if (ValueStr == NULL) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);\r
+        return EFI_NOT_FOUND;\r
+      }\r
+\r
+      Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);\r
+      if (!EFI_ERROR (Status)) {\r
+        Context.DataType  = DataType;\r
+        Context.Status    = EFI_NOT_FOUND;\r
+        Context.Selector  = Selector;\r
+        Context.Data      = Data;\r
+\r
+        ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) InsertPolicyEntry, &Context);\r
+        Status = Context.Status;\r
+        if (Status == EFI_NOT_FOUND) {\r
+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);\r
+        }\r
+      }\r
+    }\r
+\r
+    gBS->FreePool (Selector);\r
+    gBS->FreePool (Data);\r
+  }\r
+\r
+  if (Status == EFI_UNSUPPORTED) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_UNSUPPORT), mHiiHandle, mAppName);\r
+  } else if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_FAILED), mHiiHandle, mAppName);\r
+  }\r
+\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h
new file mode 100644 (file)
index 0000000..5161bac
--- /dev/null
@@ -0,0 +1,158 @@
+/** @file\r
+  The function declaration of policy entry operation in IpSecConfig application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _POLICY_ENTRY_OPERATION_H_\r
+#define _POLICY_ENTRY_OPERATION_H_\r
+\r
+#define LOCAL              BIT(0)\r
+#define REMOTE             BIT(1)\r
+#define PROTO              BIT(2)\r
+#define LOCAL_PORT         BIT(3)\r
+#define REMOTE_PORT        BIT(4)\r
+#define ICMP_TYPE          BIT(5)\r
+#define ICMP_CODE          BIT(6)\r
+#define NAME               BIT(7)\r
+#define PACKET_FLAG        BIT(8)\r
+#define ACTION             BIT(9)\r
+#define EXT_SEQUENCE       BIT(10)\r
+#define SEQUENCE_OVERFLOW  BIT(11)\r
+#define FRAGMENT_CHECK     BIT(12)\r
+#define LIFEBYTE           BIT(13)\r
+#define LIFETIME_SOFT      BIT(14)\r
+#define LIFETIME           BIT(15)\r
+#define MODE               BIT(16)\r
+#define TUNNEL_LOCAL       BIT(17)\r
+#define TUNNEL_REMOTE      BIT(18)\r
+#define DONT_FRAGMENT      BIT(19)\r
+#define IPSEC_PROTO        BIT(20)\r
+#define AUTH_ALGO          BIT(21)\r
+#define ENCRYPT_ALGO       BIT(22)\r
+#define SPI                BIT(23)\r
+#define DEST               BIT(24)\r
+#define SEQUENCE_NUMBER    BIT(25)\r
+#define ANTIREPLAY_WINDOW  BIT(26)\r
+#define AUTH_KEY           BIT(27)\r
+#define ENCRYPT_KEY        BIT(28)\r
+#define PATH_MTU           BIT(29)\r
+\r
+#define PEER_ID            BIT(0)\r
+#define PEER_ADDRESS       BIT(1)\r
+#define AUTH_PROTO         BIT(2)\r
+#define AUTH_METHOD        BIT(3)\r
+#define IKE_ID             BIT(4)\r
+#define AUTH_DATA          BIT(5)\r
+#define REVOCATION_DATA    BIT(6)\r
+\r
+typedef struct {\r
+  EFI_IPSEC_CONFIG_DATA_TYPE    DataType;\r
+  EFI_IPSEC_CONFIG_SELECTOR     *Selector;    // Data to be inserted.\r
+  VOID                          *Data;\r
+  UINT32                        Mask;\r
+  POLICY_ENTRY_INDEXER          Indexer;\r
+  EFI_STATUS                    Status;       // Indicate whether insertion succeeds.\r
+} EDIT_POLICY_ENTRY_CONTEXT;\r
+\r
+typedef struct {\r
+  EFI_IPSEC_CONFIG_DATA_TYPE    DataType;\r
+  EFI_IPSEC_CONFIG_SELECTOR     *Selector;    // Data to be inserted.\r
+  VOID                          *Data;\r
+  POLICY_ENTRY_INDEXER          Indexer;\r
+  EFI_STATUS                    Status;       // Indicate whether insertion succeeds.\r
+} INSERT_POLICY_ENTRY_CONTEXT;\r
+\r
+/**\r
+  The prototype for the CreateSpdEntry()/CreateSadEntry()/CreatePadEntry().\r
+  Fill in EFI_IPSEC_CONFIG_SELECTOR and corresponding data thru ParamPackage list.\r
+\r
+  @param[out] Selector        The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.\r
+  @param[out] Data            The pointer to corresponding data.\r
+  @param[in]  ParamPackage    The pointer to the ParamPackage list.\r
+  @param[out] Mask            The pointer to the Mask.\r
+  @param[in]  CreateNew       The switch to create new.\r
+\r
+  @retval EFI_SUCCESS              Filled in EFI_IPSEC_CONFIG_SELECTOR and corresponding data successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*CREATE_POLICY_ENTRY) (\r
+  OUT EFI_IPSEC_CONFIG_SELECTOR    **Selector,\r
+  OUT VOID                         **Data,\r
+  IN  LIST_ENTRY                   *ParamPackage,\r
+  OUT UINT32                       *Mask,\r
+  IN  BOOLEAN                      CreateNew\r
+  );\r
+\r
+/**\r
+  The prototype for the CombineSpdEntry()/CombineSadEntry()/CombinePadEntry().\r
+  Combine old SPD/SAD/PAD entry with new SPD/SAD/PAD entry.\r
+\r
+  @param[in, out] OldSelector    The pointer to the old EFI_IPSEC_CONFIG_SELECTOR union.\r
+  @param[in, out] OldData        The pointer to the corresponding old data.\r
+  @param[in]      NewSelector    The pointer to the new EFI_IPSEC_CONFIG_SELECTOR union.\r
+  @param[in]      NewData        The pointer to the corresponding new data.\r
+  @param[in]      Mask           The pointer to the Mask.\r
+  @param[out]     CreateNew      The switch to create new.\r
+\r
+  @retval EFI_SUCCESS              Combined successfully.\r
+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(* COMBINE_POLICY_ENTRY) (\r
+  EFI_IPSEC_CONFIG_SELECTOR    *OldSelector,\r
+  VOID                         *OldData,\r
+  EFI_IPSEC_CONFIG_SELECTOR    *NewSelector,\r
+  VOID                         *NewData,\r
+  UINT32                       Mask,\r
+  BOOLEAN                      *CreateNew\r
+  );\r
+\r
+/**\r
+  Insert or add entry information in database according to datatype.\r
+\r
+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS             Insert or add entry information successfully.\r
+  @retval EFI_NOT_FOUND           Can't find the specified entry.\r
+  @retval EFI_BUFFER_TOO_SMALL    The entry already existed.\r
+  @retval EFI_UNSUPPORTED         The operation is not supported./\r
+  @retval Others                  Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+AddOrInsertPolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN LIST_ENTRY                    *ParamPackage\r
+  );\r
+\r
+/**\r
+  Edit entry information in the database according to datatype.\r
+\r
+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in] ParamPackage    The pointer to the ParamPackage list.\r
+\r
+  @retval EFI_SUCCESS             Edit entry information successfully.\r
+  @retval EFI_NOT_FOUND           Can't find the specified entry.\r
+  @retval Others                  Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+EditPolicyEntry (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,\r
+  IN LIST_ENTRY                    *ParamPackage\r
+  );\r
+#endif\r
diff --git a/NetworkPkg/Application/Ping6/Ia32/Tsc.c b/NetworkPkg/Application/Ping6/Ia32/Tsc.c
new file mode 100644 (file)
index 0000000..e2eae99
--- /dev/null
@@ -0,0 +1,28 @@
+/** @file\r
+  The implement to read TSC in IA32 platform.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+\r
+/**\r
+  Reads and returns the current value of the Time Stamp Counter (TSC).\r
+\r
+  @return The current value of TSC.\r
+\r
+**/\r
+UINT64\r
+ReadTime ()\r
+{\r
+  return AsmReadTsc ();\r
+}\r
diff --git a/NetworkPkg/Application/Ping6/Ipf/Itc.c b/NetworkPkg/Application/Ping6/Ipf/Itc.c
new file mode 100644 (file)
index 0000000..131e5c0
--- /dev/null
@@ -0,0 +1,28 @@
+/** @file\r
+  The implement to read ITC in IA64 platform.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+\r
+/**\r
+  Reads and returns the current value of the Interval Timer Counter Register (ITC).\r
+\r
+  @return The current value of ITC.\r
+\r
+**/\r
+UINT64\r
+ReadTime ()\r
+{\r
+  return AsmReadItc ();\r
+}\r
diff --git a/NetworkPkg/Application/Ping6/Ping6.c b/NetworkPkg/Application/Ping6/Ping6.c
new file mode 100644 (file)
index 0000000..b783c5a
--- /dev/null
@@ -0,0 +1,1179 @@
+/** @file\r
+  The implementation for Ping6 application.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include <Library/ShellLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+#include <Protocol/Cpu.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Ip6Config.h>\r
+\r
+#include "Ping6.h"\r
+\r
+SHELL_PARAM_ITEM    Ping6ParamList[] = {\r
+  {\r
+    L"-l",\r
+    TypeValue\r
+  },\r
+  {\r
+    L"-n",\r
+    TypeValue\r
+  },\r
+  {\r
+    L"-s",\r
+    TypeValue\r
+  },\r
+  {\r
+    L"-?",\r
+    TypeFlag\r
+  },\r
+  {\r
+    NULL,\r
+    TypeMax\r
+  },\r
+};\r
+\r
+//\r
+// Global Variables in Ping6 application.\r
+//\r
+EFI_HII_HANDLE    mHiiHandle;\r
+CONST CHAR16      *mIp6DstString;\r
+CONST CHAR16      *mIp6SrcString;\r
+EFI_GUID          mEfiPing6Guid = EFI_PING6_GUID;\r
+UINT32            mFrequency = 0;\r
+/**\r
+  Get and caculate the frequency in tick/ms.\r
+  The result is saved in the globle variable mFrequency\r
+\r
+  @retval EFI_SUCCESS    Caculated the frequency successfully.\r
+  @retval Others         Failed to caculate the frequency.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6GetFrequency (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  EFI_CPU_ARCH_PROTOCOL    *Cpu;\r
+  UINT64                   CurrentTick;\r
+  UINT32                   TimerPeriod;\r
+\r
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = Cpu->GetTimerValue (Cpu, 0, &CurrentTick, (UINT64 *) &TimerPeriod);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // For NT32 Simulator only. 358049 is a similar value to keep timer granularity.\r
+    // Set the timer period by ourselves.\r
+    //\r
+    TimerPeriod = NTTIMERPERIOD;\r
+  }\r
+  //\r
+  // The timer period is in femtosecond (1 femtosecond is 1e-15 second).\r
+  // So 1e+12 is divided by timer period to produce the freq in tick/ms.\r
+  //\r
+  mFrequency = (UINT32) DivU64x32 (1000000000000ULL, TimerPeriod);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get and caculate the duration in ms.\r
+\r
+  @param[in]  Begin    The start point of time.\r
+  @param[in]  End      The end point of time.\r
+\r
+  @return The duration in ms.\r
+\r
+**/\r
+UINT32\r
+Ping6CalculateTick (\r
+  IN UINT64    Begin,\r
+  IN UINT64    End\r
+  )\r
+{\r
+  ASSERT (End > Begin);\r
+  return (UINT32) DivU64x32 (End - Begin, mFrequency);\r
+}\r
+\r
+/**\r
+  Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.\r
+\r
+  @param[in]    TxInfo    The pointer to PING6_ICMP6_TX_INFO.\r
+\r
+**/\r
+VOID\r
+Ping6DestroyTxInfo (\r
+  IN PING6_ICMP6_TX_INFO    *TxInfo\r
+  )\r
+{\r
+  EFI_IP6_TRANSMIT_DATA    *TxData;\r
+  EFI_IP6_FRAGMENT_DATA    *FragData;\r
+  UINTN                    Index;\r
+\r
+  ASSERT (TxInfo != NULL);\r
+\r
+  if (TxInfo->Token != NULL) {\r
+\r
+    if (TxInfo->Token->Event != NULL) {\r
+      gBS->CloseEvent (TxInfo->Token->Event);\r
+    }\r
+\r
+    TxData = TxInfo->Token->Packet.TxData;\r
+    if (TxData != NULL) {\r
+\r
+      if (TxData->OverrideData != NULL) {\r
+        FreePool (TxData->OverrideData);\r
+      }\r
+\r
+      if (TxData->ExtHdrs != NULL) {\r
+        FreePool (TxData->ExtHdrs);\r
+      }\r
+\r
+      for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
+        FragData = TxData->FragmentTable[Index].FragmentBuffer;\r
+        if (FragData != NULL) {\r
+          FreePool (FragData);\r
+        }\r
+      }\r
+    }\r
+\r
+    FreePool (TxInfo->Token);\r
+  }\r
+\r
+  FreePool (TxInfo);\r
+}\r
+\r
+/**\r
+  Match the request, and reply with SequenceNum/TimeStamp.\r
+\r
+  @param[in]    Private    The pointer to PING6_PRIVATE_DATA.\r
+  @param[in]    Packet     The pointer to ICMP6_ECHO_REQUEST_REPLY.\r
+\r
+  @retval EFI_SUCCESS      The match is successful.\r
+  @retval EFI_NOT_FOUND    The reply can't be matched with any request.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6MatchEchoReply (\r
+  IN PING6_PRIVATE_DATA          *Private,\r
+  IN ICMP6_ECHO_REQUEST_REPLY    *Packet\r
+  )\r
+{\r
+  PING6_ICMP6_TX_INFO    *TxInfo;\r
+  LIST_ENTRY             *Entry;\r
+  LIST_ENTRY             *NextEntry;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
+    TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
+\r
+    if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {\r
+      Private->RxCount++;\r
+      RemoveEntryList (&TxInfo->Link);\r
+      Ping6DestroyTxInfo (TxInfo);\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  The original intention is to send a request.\r
+  Currently, the application retransmits an icmp6 echo request packet\r
+  per second in sendnumber times that is specified by the user.\r
+  Because nothing can be done here, all things move to the timer rountine.\r
+\r
+  @param[in]    Event      A EFI_EVENT type event.\r
+  @param[in]    Context    The pointer to Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ping6OnEchoRequestSent (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+}\r
+\r
+/**\r
+  receive reply, match and print reply infomation.\r
+\r
+  @param[in]    Event      A EFI_EVENT type event.\r
+  @param[in]    Context    The pointer to context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ping6OnEchoReplyReceived (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  PING6_PRIVATE_DATA          *Private;\r
+  EFI_IP6_COMPLETION_TOKEN    *RxToken;\r
+  EFI_IP6_RECEIVE_DATA        *RxData;\r
+  ICMP6_ECHO_REQUEST_REPLY    *Reply;\r
+  UINT32                      PayLoad;\r
+  UINT32                      Rtt;\r
+  CHAR8                       Near;\r
+\r
+  Private = (PING6_PRIVATE_DATA *) Context;\r
+\r
+  if (Private->Status == EFI_ABORTED) {\r
+    return;\r
+  }\r
+\r
+  RxToken = &Private->RxToken;\r
+  RxData  = RxToken->Packet.RxData;\r
+  Reply   = RxData->FragmentTable[0].FragmentBuffer;\r
+  PayLoad = RxData->DataLength;\r
+\r
+  if (RxData->Header->NextHeader != IP6_ICMP) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (!IP6_IS_MULTICAST (&Private->DstAddress) && \r
+      !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (PayLoad != Private->BufferSize) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Check whether the reply matches the sent request before.\r
+  //\r
+  Status = Ping6MatchEchoReply (Private, Reply);\r
+  if (EFI_ERROR(Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Display statistics on this icmp6 echo reply packet.\r
+  //\r
+  Rtt  = Ping6CalculateTick (Reply->TimeStamp, ReadTime ());\r
+  if (Rtt != 0) {\r
+    Near = (CHAR8) '=';\r
+  } else {\r
+    Near = (CHAR8) '<';\r
+  }\r
+\r
+  Private->RttSum += Rtt;\r
+  Private->RttMin  = Private->RttMin > Rtt ? Rtt : Private->RttMin;\r
+  Private->RttMax  = Private->RttMax < Rtt ? Rtt : Private->RttMax;\r
+\r
+  ShellPrintHiiEx (\r
+    -1,\r
+    -1,\r
+    NULL,\r
+    STRING_TOKEN (STR_PING6_REPLY_INFO),\r
+    mHiiHandle,\r
+    PayLoad,\r
+    mIp6DstString,\r
+    Reply->SequenceNum,\r
+    RxData->Header->HopLimit,\r
+    Near,\r
+    Rtt\r
+    );\r
+\r
+ON_EXIT:\r
+\r
+  if (Private->RxCount < Private->SendNum) {\r
+    //\r
+    // Continue to receive icmp6 echo reply packets.\r
+    //\r
+    RxToken->Status = EFI_ABORTED;\r
+\r
+    Status = Private->Ip6->Receive (Private->Ip6, RxToken);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      Private->Status = EFI_ABORTED;\r
+    }\r
+  } else {\r
+    //\r
+    // All reply have already been received from the dest host.\r
+    //\r
+    Private->Status = EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Singal to recycle the each rxdata here, not at the end of process.\r
+  //\r
+  gBS->SignalEvent (RxData->RecycleSignal);\r
+}\r
+\r
+/**\r
+  Initial EFI_IP6_COMPLETION_TOKEN.\r
+\r
+  @param[in]    Private        The pointer of PING6_PRIVATE_DATA.\r
+  @param[in]    TimeStamp      The TimeStamp of request.\r
+  @param[in]    SequenceNum    The SequenceNum of request.\r
+\r
+  @return The pointer of EFI_IP6_COMPLETION_TOKEN.\r
+\r
+**/\r
+EFI_IP6_COMPLETION_TOKEN *\r
+Ping6GenerateToken (\r
+  IN PING6_PRIVATE_DATA    *Private,\r
+  IN UINT64                TimeStamp,\r
+  IN UINT16                SequenceNum\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_IP6_COMPLETION_TOKEN    *Token;\r
+  EFI_IP6_TRANSMIT_DATA       *TxData;\r
+  ICMP6_ECHO_REQUEST_REPLY    *Request;\r
+\r
+  Request = AllocateZeroPool (Private->BufferSize);\r
+\r
+  if (Request == NULL) {\r
+    return NULL;\r
+  }\r
+  //\r
+  // Assembly icmp6 echo request packet.\r
+  //\r
+  Request->Type        = ICMP_V6_ECHO_REQUEST;\r
+  Request->Code        = 0;\r
+  Request->SequenceNum = SequenceNum;\r
+  Request->TimeStamp   = TimeStamp;\r
+  Request->Identifier  = 0;\r
+  //\r
+  // Leave check sum to ip6 layer, since it has no idea of source address\r
+  // selection.\r
+  //\r
+  Request->Checksum    = 0;\r
+\r
+  TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));\r
+\r
+  if (TxData == NULL) {\r
+    FreePool (Request);\r
+    return NULL;\r
+  }\r
+  //\r
+  // Assembly ipv6 token for transmit.\r
+  //\r
+  TxData->OverrideData       = 0;\r
+  TxData->ExtHdrsLength      = 0;\r
+  TxData->ExtHdrs            = NULL;\r
+  TxData->DataLength         = Private->BufferSize;\r
+  TxData->FragmentCount      = 1;\r
+  TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request;\r
+  TxData->FragmentTable[0].FragmentLength = Private->BufferSize;\r
+\r
+  Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));\r
+\r
+  if (Token == NULL) {\r
+    FreePool (Request);\r
+    FreePool (TxData);\r
+    return NULL;\r
+  }\r
+\r
+  Token->Status         = EFI_ABORTED;\r
+  Token->Packet.TxData  = TxData;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ping6OnEchoRequestSent,\r
+                  Private,\r
+                  &Token->Event\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Request);\r
+    FreePool (TxData);\r
+    FreePool (Token);\r
+    return NULL;\r
+  }\r
+\r
+  return Token;\r
+}\r
+\r
+/**\r
+  Transmit the EFI_IP6_COMPLETION_TOKEN.\r
+\r
+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
+\r
+  @retval EFI_SUCCESS             Transmitted successfully.\r
+  @retval EFI_OUT_OF_RESOURCES    No memory is available on the platform.\r
+  @retval others                  Transmitted unsuccessfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6SendEchoRequest (\r
+  IN PING6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  PING6_ICMP6_TX_INFO    *TxInfo;\r
+\r
+  TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));\r
+\r
+  if (TxInfo == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TxInfo->TimeStamp   = ReadTime ();\r
+  TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);\r
+\r
+  TxInfo->Token       = Ping6GenerateToken (\r
+                          Private,\r
+                          TxInfo->TimeStamp,\r
+                          TxInfo->SequenceNum\r
+                          );\r
+\r
+  if (TxInfo->Token == NULL) {\r
+    Ping6DestroyTxInfo (TxInfo);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Ping6DestroyTxInfo (TxInfo);\r
+    return Status;\r
+  }\r
+\r
+  InsertTailList (&Private->TxList, &TxInfo->Link);\r
+  Private->TxCount++;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Place a completion token into the receive packet queue to receive the echo reply.\r
+\r
+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
+\r
+  @retval EFI_SUCCESS      Put the token into the receive packet queue successfully.\r
+  @retval others           Put the token into the receive packet queue unsuccessfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6ReceiveEchoReply (\r
+  IN PING6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+\r
+  ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN));\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ping6OnEchoReplyReceived,\r
+                  Private,\r
+                  &Private->RxToken.Event\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Private->RxToken.Status = EFI_NOT_READY;\r
+\r
+  return Private->Ip6->Receive (Private->Ip6, &Private->RxToken);\r
+}\r
+\r
+/**\r
+  Remove the timeout request from the list.\r
+\r
+  @param[in]    Event    A EFI_EVENT type event.\r
+  @param[in]    Context  The pointer to Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ping6OnTimerRoutine (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  PING6_PRIVATE_DATA     *Private;\r
+  PING6_ICMP6_TX_INFO    *TxInfo;\r
+  LIST_ENTRY             *Entry;\r
+  LIST_ENTRY             *NextEntry;\r
+  UINT32                 Time;\r
+\r
+  Private = (PING6_PRIVATE_DATA *) Context;\r
+\r
+  //\r
+  // Retransmit icmp6 echo request packets per second in sendnumber times.\r
+  //\r
+  if (Private->TxCount < Private->SendNum) {\r
+\r
+    Status = Ping6SendEchoRequest (Private);\r
+    if (Private->TxCount != 0){\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), mHiiHandle, Private->TxCount + 1);\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // Check whether any icmp6 echo request in the list timeout.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
+    TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
+    Time   = Ping6CalculateTick (TxInfo->TimeStamp, ReadTime ());\r
+\r
+    //\r
+    // Remove the timeout echo request from txlist.\r
+    //\r
+    if (Time > PING6_DEFAULT_TIMEOUT) {\r
+\r
+      if (EFI_ERROR (TxInfo->Token->Status)) {\r
+        Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);\r
+      }\r
+      //\r
+      // Remove the timeout icmp6 echo request from list.\r
+      //\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), mHiiHandle, TxInfo->SequenceNum);\r
+\r
+      RemoveEntryList (&TxInfo->Link);\r
+      Ping6DestroyTxInfo (TxInfo);\r
+\r
+      if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {\r
+        //\r
+        // All the left icmp6 echo request in the list timeout.\r
+        //\r
+        Private->Status = EFI_TIMEOUT;\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Create a valid IP6 instance.\r
+\r
+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
+\r
+  @retval EFI_SUCCESS              Create a valid IP6 instance successfully.\r
+  @retval EFI_ABORTED              Locate handle with ip6 service binding protocol unsuccessfully.\r
+  @retval EFI_INVALID_PARAMETER    The source address is unspecified when the destination address is a link -ocal address.\r
+  @retval EFI_OUT_OF_RESOURCES     No memory is available on the platform.\r
+  @retval EFI_NOT_FOUND            The source address is not found.\r
+**/\r
+EFI_STATUS\r
+Ping6CreateIp6Instance (\r
+  IN  PING6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINTN                            HandleIndex;\r
+  UINTN                            HandleNum;\r
+  EFI_HANDLE                       *HandleBuffer;\r
+  EFI_SERVICE_BINDING_PROTOCOL     *Ip6Sb;\r
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;\r
+  EFI_IP6_CONFIG_DATA              Ip6Config;\r
+  EFI_IP6_CONFIG_INTERFACE_INFO    *IfInfo;\r
+  UINTN                            IfInfoSize;\r
+  EFI_IPv6_ADDRESS                 *Addr;\r
+  UINTN                            AddrIndex;\r
+\r
+  HandleBuffer = NULL;\r
+  Ip6Sb        = NULL;\r
+  IfInfo       = NULL;\r
+  IfInfoSize   = 0;\r
+\r
+  //\r
+  // Locate all the handles with ip6 service binding protocol.\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  &HandleNum,\r
+                  &HandleBuffer\r
+                  );\r
+  if (EFI_ERROR (Status) || (HandleNum == 0)) {\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Source address is required when pinging a link-local address on multi-\r
+  // interfaces host.\r
+  //\r
+  if (NetIp6IsLinkLocalAddr (&Private->DstAddress) &&\r
+      NetIp6IsUnspecifiedAddr (&Private->SrcAddress) &&\r
+      (HandleNum > 1)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), mHiiHandle);\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_ERROR;\r
+  }\r
+  //\r
+  // For each ip6 protocol, check interface addresses list.\r
+  //\r
+  for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {\r
+\r
+    Ip6Sb      = NULL;\r
+    IfInfo     = NULL;\r
+    IfInfoSize = 0;\r
+\r
+    Status = gBS->HandleProtocol (\r
+                    HandleBuffer[HandleIndex],\r
+                    &gEfiIp6ServiceBindingProtocolGuid,\r
+                    (VOID **) &Ip6Sb\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {\r
+      //\r
+      // No need to match interface address.\r
+      //\r
+      break;\r
+    } else {\r
+      //\r
+      // Ip6config protocol and ip6 service binding protocol are installed\r
+      // on the same handle.\r
+      //\r
+      Status = gBS->HandleProtocol (\r
+                      HandleBuffer[HandleIndex],\r
+                      &gEfiIp6ConfigProtocolGuid,\r
+                      (VOID **) &Ip6Cfg\r
+                      );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_ERROR;\r
+      }\r
+      //\r
+      // Get the interface information size.\r
+      //\r
+      Status = Ip6Cfg->GetData (\r
+                         Ip6Cfg,\r
+                         Ip6ConfigDataTypeInterfaceInfo,\r
+                         &IfInfoSize,\r
+                         NULL\r
+                         );\r
+\r
+      if (Status != EFI_BUFFER_TOO_SMALL) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);\r
+        goto ON_ERROR;\r
+      }\r
+\r
+      IfInfo = AllocateZeroPool (IfInfoSize);\r
+\r
+      if (IfInfo == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto ON_ERROR;\r
+      }\r
+      //\r
+      // Get the interface info.\r
+      //\r
+      Status = Ip6Cfg->GetData (\r
+                         Ip6Cfg,\r
+                         Ip6ConfigDataTypeInterfaceInfo,\r
+                         &IfInfoSize,\r
+                         IfInfo\r
+                         );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);\r
+        goto ON_ERROR;\r
+      }\r
+      //\r
+      // Check whether the source address is one of the interface addresses.\r
+      //\r
+      for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {\r
+\r
+        Addr = &(IfInfo->AddressInfo[AddrIndex].Address);\r
+        if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {\r
+          //\r
+          // Match a certain interface address.\r
+          //\r
+          break;\r
+        }\r
+      }\r
+\r
+      if (AddrIndex < IfInfo->AddressInfoCount) {\r
+        //\r
+        // Found a nic handle with right interface address.\r
+        //\r
+        break;\r
+      }\r
+    }\r
+\r
+    FreePool (IfInfo);\r
+    IfInfo = NULL;\r
+  }\r
+  //\r
+  // No exact interface address matched.\r
+  //\r
+\r
+  if (HandleIndex == HandleNum) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SOURCE_NOT_FOUND), mHiiHandle, mIp6SrcString);\r
+    Status = EFI_NOT_FOUND;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Private->NicHandle = HandleBuffer[HandleIndex];\r
+\r
+  ASSERT (Ip6Sb != NULL);\r
+  Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Ip6ChildHandle,\r
+                  &gEfiIp6ProtocolGuid,\r
+                  (VOID **) &Private->Ip6,\r
+                  Private->ImageHandle,\r
+                  Private->Ip6ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));\r
+\r
+  //\r
+  // Configure the ip6 instance for icmp6 packet exchange.\r
+  //\r
+  Ip6Config.DefaultProtocol   = 58;\r
+  Ip6Config.AcceptAnyProtocol = FALSE;\r
+  Ip6Config.AcceptIcmpErrors  = TRUE;\r
+  Ip6Config.AcceptPromiscuous = FALSE;\r
+  Ip6Config.TrafficClass      = 0;\r
+  Ip6Config.HopLimit          = 128;\r
+  Ip6Config.FlowLabel         = 0;\r
+  Ip6Config.ReceiveTimeout    = 0;\r
+  Ip6Config.TransmitTimeout   = 0;\r
+\r
+  IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);\r
+\r
+  IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);\r
+\r
+  Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), mHiiHandle, Status);\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  if (HandleBuffer != NULL) {\r
+    FreePool (HandleBuffer);\r
+  }\r
+\r
+  if (IfInfo != NULL) {\r
+    FreePool (IfInfo);\r
+  }\r
+\r
+  if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {\r
+    Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Destory the IP6 instance.\r
+\r
+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+Ping6DestoryIp6Instance (\r
+  IN PING6_PRIVATE_DATA    *Private\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  EFI_SERVICE_BINDING_PROTOCOL    *Ip6Sb;\r
+\r
+  gBS->CloseProtocol (\r
+         Private->Ip6ChildHandle,\r
+         &gEfiIp6ProtocolGuid,\r
+         Private->ImageHandle,\r
+         Private->Ip6ChildHandle\r
+         );\r
+\r
+  Status = gBS->HandleProtocol (\r
+                  Private->NicHandle,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  (VOID **) &Ip6Sb\r
+                  );\r
+\r
+  if (!EFI_ERROR(Status)) {\r
+    Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);\r
+  }\r
+}\r
+\r
+/**\r
+  The Ping6 Process.\r
+\r
+  @param[in]   ImageHandle    The firmware allocated handle for the UEFI image.\r
+  @param[in]   SendNumber     The send request count.\r
+  @param[in]   BufferSize     The send buffer size.\r
+  @param[in]   SrcAddress     The source IPv6 address.\r
+  @param[in]   DstAddress     The destination IPv6 address.\r
+\r
+  @retval EFI_SUCCESS    The ping6 processed successfullly.\r
+  @retval others         The ping6 processed unsuccessfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6 (\r
+  IN EFI_HANDLE          ImageHandle,\r
+  IN UINT32              SendNumber,\r
+  IN UINT32              BufferSize,\r
+  IN EFI_IPv6_ADDRESS    *SrcAddress,\r
+  IN EFI_IPv6_ADDRESS    *DstAddress\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  EFI_INPUT_KEY          Key;\r
+  PING6_PRIVATE_DATA     *Private;\r
+  PING6_ICMP6_TX_INFO    *TxInfo;\r
+  LIST_ENTRY             *Entry;\r
+  LIST_ENTRY             *NextEntry;\r
+\r
+  Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));\r
+\r
+  ASSERT (Private != NULL);\r
+\r
+  Private->ImageHandle = ImageHandle;\r
+  Private->SendNum     = SendNumber;\r
+  Private->BufferSize  = BufferSize;\r
+  Private->RttMin      = 0xFFFF;\r
+  Private->Status      = EFI_NOT_READY;\r
+\r
+  InitializeListHead (&Private->TxList);\r
+\r
+  IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);\r
+  IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);\r
+\r
+  //\r
+  // Open and configure a ip6 instance for ping6.\r
+  //\r
+  Status = Ping6CreateIp6Instance (Private);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Print the command line itself.\r
+  //\r
+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), mHiiHandle, mIp6DstString, Private->BufferSize);\r
+  //\r
+  // Create a ipv6 token to receive the first icmp6 echo reply packet.\r
+  //\r
+  Status = Ping6ReceiveEchoReply (Private);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Create and start timer to send icmp6 echo request packet per second.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ping6OnTimerRoutine,\r
+                  Private,\r
+                  &Private->Timer\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Create a ipv6 token to send the first icmp6 echo request packet.\r
+  //\r
+  Status = Ping6SendEchoRequest (Private);\r
+  //\r
+  // EFI_NOT_READY for IPsec is enable and IKE is not established.\r
+  //\r
+  if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {\r
+    if(Status == EFI_NOT_FOUND) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), mHiiHandle, mIp6DstString);\r
+    }\r
+\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  Private->Timer,\r
+                  TimerPeriodic,\r
+                  PING6_ONE_SECOND\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Control the ping6 process by two factors:\r
+  // 1. Hot key\r
+  // 2. Private->Status\r
+  //   2.1. success means all icmp6 echo request packets get reply packets.\r
+  //   2.2. timeout means the last icmp6 echo reply request timeout to get reply.\r
+  //   2.3. noready means ping6 process is on-the-go.\r
+  //\r
+  while (Private->Status == EFI_NOT_READY) {\r
+    Private->Ip6->Poll (Private->Ip6);\r
+\r
+    //\r
+    // Terminate the ping6 process by 'esc' or 'ctl-c'.\r
+    //\r
+    Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
+\r
+    if (!EFI_ERROR(Status)) {\r
+      if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||\r
+         ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {\r
+        goto ON_STAT;\r
+      }\r
+    }\r
+  }\r
+\r
+ON_STAT:\r
+  //\r
+  // Display the statistics in all.\r
+  //\r
+  gBS->SetTimer (Private->Timer, TimerCancel, 0);\r
+\r
+  if (Private->TxCount != 0) {\r
+    ShellPrintHiiEx (\r
+      -1,\r
+      -1,\r
+      NULL,\r
+      STRING_TOKEN (STR_PING6_STAT),\r
+      mHiiHandle,\r
+      Private->TxCount,\r
+      Private->RxCount,\r
+      (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,\r
+      Private->RttSum\r
+      );\r
+  }\r
+\r
+  if (Private->RxCount != 0) {\r
+    ShellPrintHiiEx (\r
+      -1,\r
+      -1,\r
+      NULL,\r
+      STRING_TOKEN (STR_PING6_RTT),\r
+      mHiiHandle,\r
+      Private->RttMin,\r
+      Private->RttMax,\r
+      Private->RttSum / Private->RxCount\r
+      );\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (Private != NULL) {\r
+    Private->Status = EFI_ABORTED;\r
+\r
+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
+      TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
+\r
+      Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);\r
+\r
+      RemoveEntryList (&TxInfo->Link);\r
+      Ping6DestroyTxInfo (TxInfo);\r
+    }\r
+\r
+    if (Private->Timer != NULL) {\r
+      gBS->CloseEvent (Private->Timer);\r
+    }\r
+\r
+    if (Private->Ip6 != NULL) {\r
+      Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);\r
+    }\r
+\r
+    if (Private->RxToken.Event != NULL) {\r
+      gBS->CloseEvent (Private->RxToken.Event);\r
+    }\r
+\r
+    if (Private->Ip6ChildHandle != NULL) {\r
+      Ping6DestoryIp6Instance (Private);\r
+    }\r
+\r
+    FreePool (Private);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This is the declaration of an EFI image entry point. This entry point is\r
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+  both device drivers and bus drivers.\r
+\r
+  The entry point for the Ping6 application that parses the command line input and calls the Ping6 process.\r
+\r
+  @param[in] ImageHandle    The firmware allocated handle for the UEFI image.\r
+  @param[in] SystemTable    A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS               The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETETR    Input parameters combination is invalid.\r
+  @retval Others                    Some errors occur.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializePing6 (\r
+  IN  EFI_HANDLE          ImageHandle,\r
+  IN  EFI_SYSTEM_TABLE    *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  EFI_IPv6_ADDRESS    DstAddress;\r
+  EFI_IPv6_ADDRESS    SrcAddress;\r
+  UINT64              BufferSize;\r
+  UINTN               SendNumber;\r
+  LIST_ENTRY          *ParamPackage;\r
+  CONST CHAR16        *ValueStr;\r
+  CONST CHAR16        *ValueStrPtr;\r
+  UINTN               NonOptionCount;\r
+\r
+  //\r
+  // Register our string package with HII and return the handle to it.\r
+  //\r
+  mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, Ping6Strings, NULL);\r
+  ASSERT (mHiiHandle != NULL);\r
+\r
+  Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, NULL, TRUE, FALSE);\r
+  if (EFI_ERROR(Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_HELP), mHiiHandle);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  SendNumber = 10;\r
+  BufferSize = 16;\r
+\r
+  //\r
+  // Parse the paramter of count number.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");\r
+  ValueStrPtr = ValueStr;\r
+  if (ValueStr != NULL) {\r
+    SendNumber = ShellStrToUintn (ValueStrPtr);\r
+\r
+    //\r
+    // ShellStrToUintn will return 0 when input is 0 or an invalid input string.\r
+    //\r
+    if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), mHiiHandle, ValueStr);\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+  //\r
+  // Parse the paramter of buffer size.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");\r
+  ValueStrPtr = ValueStr;\r
+  if (ValueStr != NULL) {\r
+    BufferSize = ShellStrToUintn (ValueStrPtr);\r
+\r
+    //\r
+    // ShellStrToUintn will return 0 when input is 0 or an invalid input string.\r
+    //\r
+    if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), mHiiHandle, ValueStr);\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));\r
+  ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  //\r
+  // Parse the paramter of source ip address.\r
+  //\r
+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");\r
+  ValueStrPtr = ValueStr;\r
+  if (ValueStr != NULL) {\r
+    mIp6SrcString = ValueStr;\r
+    Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+  //\r
+  // Parse the paramter of destination ip address.\r
+  //\r
+  NonOptionCount = ShellCommandLineGetCount();\r
+  ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));\r
+  if (NonOptionCount != 2) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+  ValueStrPtr = ValueStr;\r
+  if (ValueStr != NULL) {\r
+    mIp6DstString = ValueStr;\r
+    Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);\r
+    if (EFI_ERROR (Status)) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+  //\r
+  // Get frequency to calculate the time from ticks.\r
+  //\r
+  Status = Ping6GetFrequency ();\r
+\r
+  if (EFI_ERROR(Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Enter into ping6 process.\r
+  //\r
+  Status = Ping6 (\r
+             ImageHandle,\r
+             (UINT32)SendNumber,\r
+             (UINT32)BufferSize,\r
+             &SrcAddress,\r
+             &DstAddress\r
+             );\r
+\r
+ON_EXIT:\r
+  ShellCommandLineFreeVarList (ParamPackage);\r
+  HiiRemovePackages (mHiiHandle);\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/Application/Ping6/Ping6.h b/NetworkPkg/Application/Ping6/Ping6.h
new file mode 100644 (file)
index 0000000..02271e1
--- /dev/null
@@ -0,0 +1,92 @@
+/** @file\r
+  The interface function declaration of shell application Ping6 (Ping for v6 series).\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _PING6_H_\r
+#define _PING6_H_\r
+\r
+#define EFI_PING6_GUID \\r
+  { \\r
+    0x3f0b2478, 0x3619, 0x46c5, {0x81, 0x50, 0xa5, 0xab, 0xdd, 0xb6, 0x6b, 0xd9} \\r
+  }\r
+\r
+#define PING6_DEFAULT_TIMEOUT      5000\r
+#define PING6_MAX_SEND_NUMBER      10000\r
+#define PING6_MAX_BUFFER_SIZE      32768\r
+#define PING6_ONE_SECOND           10000000\r
+\r
+//\r
+// A similar amount of time that passes in femtoseconds\r
+// for each increment of TimerValue. It is for NT32 only.\r
+//\r
+#define NTTIMERPERIOD    358049\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct _ICMP6_ECHO_REQUEST_REPLY {\r
+  UINT8                       Type;\r
+  UINT8                       Code;\r
+  UINT16                      Checksum;\r
+  UINT16                      Identifier;\r
+  UINT16                      SequenceNum;\r
+  UINT64                      TimeStamp;\r
+  UINT8                       Data[1];\r
+} ICMP6_ECHO_REQUEST_REPLY;\r
+\r
+#pragma pack()\r
+\r
+typedef struct _PING6_ICMP6_TX_INFO {\r
+  LIST_ENTRY                  Link;\r
+  UINT16                      SequenceNum;\r
+  UINT64                      TimeStamp;\r
+  EFI_IP6_COMPLETION_TOKEN    *Token;\r
+} PING6_ICMP6_TX_INFO;\r
+\r
+typedef struct _PING6_PRIVATE_DATA {\r
+  EFI_HANDLE                  ImageHandle;\r
+  EFI_HANDLE                  NicHandle;\r
+  EFI_HANDLE                  Ip6ChildHandle;\r
+  EFI_IP6_PROTOCOL            *Ip6;\r
+  EFI_EVENT                   Timer;\r
+\r
+  EFI_STATUS                  Status;\r
+  LIST_ENTRY                  TxList;\r
+  EFI_IP6_COMPLETION_TOKEN    RxToken;\r
+  UINT16                      RxCount;\r
+  UINT16                      TxCount;\r
+  UINT32                      RttSum;\r
+  UINT32                      RttMin;\r
+  UINT32                      RttMax;\r
+  UINT32                      SequenceNum;\r
+\r
+  EFI_IPv6_ADDRESS            SrcAddress;\r
+  EFI_IPv6_ADDRESS            DstAddress;\r
+  UINT32                      SendNum;\r
+  UINT32                      BufferSize;\r
+} PING6_PRIVATE_DATA;\r
+\r
+/**\r
+  Reads and returns the current value of register.\r
+  In IA64, the register is the Interval Timer Vector (ITV).\r
+  In X86(IA32/X64), the register is the Time Stamp Counter (TSC)\r
+\r
+  @return The current value of the register.\r
+\r
+**/\r
+UINT64\r
+ReadTime (\r
+  VOID\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Application/Ping6/Ping6.inf b/NetworkPkg/Application/Ping6/Ping6.inf
new file mode 100644 (file)
index 0000000..56b2163
--- /dev/null
@@ -0,0 +1,64 @@
+## @file\r
+#  Component description file for Ping6 application.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010006\r
+  BASE_NAME                      = Ping6\r
+  FILE_GUID                      = F35F733F-5235-4d7b-83FA-97780CEBCB20\r
+  MODULE_TYPE                    = UEFI_APPLICATION\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = InitializePing6\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF\r
+#\r
+\r
+[Sources]\r
+  Ping6.c\r
+  Ping6Strings.uni\r
+  Ping6.h\r
+\r
+[Sources.IA32]\r
+  Ia32/Tsc.c\r
+\r
+[Sources.X64]\r
+  X64/Tsc.c\r
+\r
+[Sources.IPF]\r
+  Ipf/Itc.c\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  ShellPkg/ShellPkg.dec\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  UefiBootServicesTableLib\r
+  UefiApplicationEntryPoint\r
+  BaseMemoryLib\r
+  ShellLib\r
+  MemoryAllocationLib\r
+  DebugLib\r
+  HiiLib\r
+  NetLib\r
+\r
+[Protocols]\r
+  gEfiCpuArchProtocolGuid                       ## CONSUMS\r
+  gEfiIp6ProtocolGuid                           ## CONSUMS\r
+  gEfiIp6ServiceBindingProtocolGuid             ## CONSUMS\r
+  gEfiIp6ConfigProtocolGuid                     ## CONSUMS\r
diff --git a/NetworkPkg/Application/Ping6/Ping6Strings.uni b/NetworkPkg/Application/Ping6/Ping6Strings.uni
new file mode 100644 (file)
index 0000000..c8b919c
Binary files /dev/null and b/NetworkPkg/Application/Ping6/Ping6Strings.uni differ
diff --git a/NetworkPkg/Application/Ping6/X64/Tsc.c b/NetworkPkg/Application/Ping6/X64/Tsc.c
new file mode 100644 (file)
index 0000000..b3e7bdb
--- /dev/null
@@ -0,0 +1,28 @@
+/** @file\r
+  The implement to read TSC in X64 platform.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+\r
+/**\r
+  Reads and returns the current value of Time Stamp Counter (TSC).\r
+\r
+  @return The current value of TSC\r
+\r
+**/\r
+UINT64\r
+ReadTime ()\r
+{\r
+  return AsmReadTsc ();\r
+}\r
diff --git a/NetworkPkg/Application/VConfig/VConfig.c b/NetworkPkg/Application/VConfig/VConfig.c
new file mode 100644 (file)
index 0000000..c948131
--- /dev/null
@@ -0,0 +1,668 @@
+/** @file\r
+  Shell application for VLAN configuration.\r
+\r
+  Copyright (C) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/VlanConfig.h>\r
+\r
+#include <Library/UefiApplicationEntryPoint.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/ShellLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+#define INVALID_NIC_INDEX   0xffff\r
+#define INVALID_VLAN_ID     0xffff\r
+\r
+//\r
+// This is the generated String package data for all .UNI files.\r
+// This data array is ready to be used as input of HiiAddPackages() to\r
+// create a packagelist (which contains Form packages, String packages, etc).\r
+//\r
+extern UINT8      VConfigStrings[];\r
+\r
+EFI_HANDLE        mImageHandle  = NULL;\r
+EFI_HII_HANDLE    mHiiHandle    = NULL;\r
+\r
+SHELL_PARAM_ITEM  mParamList[] = {\r
+  {\r
+    L"-l",\r
+    TypeValue\r
+  },\r
+  {\r
+    L"-a",\r
+    TypeMaxValue\r
+  },\r
+  {\r
+    L"-d",\r
+    TypeValue\r
+  },\r
+  {\r
+    NULL,\r
+    TypeMax\r
+  }\r
+};\r
+\r
+/**\r
+  Locate the network interface handle buffer.\r
+\r
+  @param[out]  NumberOfHandles Pointer to the number of handles.\r
+  @param[out]  HandleBuffer    Pointer to the buffer to store the returned handles.\r
+\r
+**/\r
+VOID\r
+LocateNicHandleBuffer (\r
+  OUT UINTN                       *NumberOfHandles,\r
+  OUT EFI_HANDLE                  **HandleBuffer\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  *NumberOfHandles  = 0;\r
+  *HandleBuffer     = NULL;\r
+\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiVlanConfigProtocolGuid,\r
+                  NULL,\r
+                  NumberOfHandles,\r
+                  HandleBuffer\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_LOCATE_FAIL), mHiiHandle, Status);\r
+  }\r
+}\r
+\r
+/**\r
+  Extract the decimal index from the network interface name.\r
+\r
+  @param[in]  Name           Name of the network interface.\r
+\r
+  @retval INVALID_NIC_INDEX  Failed to extract the network interface index.\r
+  @return others             The network interface index.\r
+\r
+**/\r
+UINTN\r
+NicNameToIndex (\r
+  IN CHAR16                   *Name\r
+  )\r
+{\r
+  CHAR16  *Str;\r
+\r
+  Str = Name + 3;\r
+  if ((StrnCmp (Name, L"eth", 3) != 0) || (*Str == 0)) {\r
+    return INVALID_NIC_INDEX;\r
+  }\r
+\r
+  while (*Str != 0) {\r
+    if ((*Str < L'0') || (*Str > L'9')) {\r
+      return INVALID_NIC_INDEX;\r
+    }\r
+\r
+    Str++;\r
+  }\r
+\r
+  return (UINT16) StrDecimalToUintn (Name + 3);\r
+}\r
+\r
+/**\r
+  Find network interface device handle by its name.\r
+\r
+  @param[in]  Name           Name of the network interface.\r
+\r
+  @retval NULL               Cannot find the network interface.\r
+  @return others             Handle of the network interface.\r
+\r
+**/\r
+EFI_HANDLE\r
+NicNameToHandle (\r
+  IN CHAR16                   *Name\r
+  )\r
+{\r
+  UINTN       NumberOfHandles;\r
+  EFI_HANDLE  *HandleBuffer;\r
+  UINTN       Index;\r
+  EFI_HANDLE  Handle;\r
+\r
+  //\r
+  // Find all NIC handles.\r
+  //\r
+  LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer);\r
+  if (NumberOfHandles == 0) {\r
+    return NULL;\r
+  }\r
+\r
+  Index = NicNameToIndex (Name);\r
+  if (Index >= NumberOfHandles) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_IF), mHiiHandle, Name);\r
+    Handle = NULL;\r
+  } else {\r
+    Handle = HandleBuffer[Index];\r
+  }\r
+\r
+  FreePool (HandleBuffer);\r
+  return Handle;\r
+}\r
+\r
+/**\r
+  Open VlanConfig protocol from a handle.\r
+\r
+  @param[in]  Handle         The handle to open the VlanConfig protocol.\r
+\r
+  @return The VlanConfig protocol interface.\r
+\r
+**/\r
+EFI_VLAN_CONFIG_PROTOCOL *\r
+OpenVlanConfigProtocol (\r
+  IN EFI_HANDLE                 Handle\r
+  )\r
+{\r
+  EFI_VLAN_CONFIG_PROTOCOL  *VlanConfig;\r
+\r
+  VlanConfig = NULL;\r
+  gBS->OpenProtocol (\r
+         Handle,\r
+         &gEfiVlanConfigProtocolGuid,\r
+         (VOID **) &VlanConfig,\r
+         mImageHandle,\r
+         Handle,\r
+         EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+         );\r
+\r
+  return VlanConfig;\r
+}\r
+\r
+/**\r
+  Close VlanConfig protocol of a handle.\r
+\r
+  @param[in]  Handle         The handle to close the VlanConfig protocol.\r
+\r
+**/\r
+VOID\r
+CloseVlanConfigProtocol (\r
+  IN EFI_HANDLE                 Handle\r
+  )\r
+{\r
+  gBS->CloseProtocol (\r
+         Handle,\r
+         &gEfiVlanConfigProtocolGuid,\r
+         mImageHandle,\r
+         Handle\r
+         );\r
+}\r
+\r
+/**\r
+  Display VLAN configuration of a network interface.\r
+\r
+  @param[in]  Handle         Handle of the network interface.\r
+  @param[in]  NicIndex       Index of the network interface.\r
+\r
+**/\r
+VOID\r
+ShowNicVlanInfo (\r
+  IN EFI_HANDLE              Handle,\r
+  IN UINTN                   NicIndex\r
+  )\r
+{\r
+  CHAR16                    *MacStr;\r
+  EFI_STATUS                Status;\r
+  UINTN                     Index;\r
+  EFI_VLAN_CONFIG_PROTOCOL  *VlanConfig;\r
+  UINT16                    NumberOfVlan;\r
+  EFI_VLAN_FIND_DATA        *VlanData;\r
+\r
+  VlanConfig = OpenVlanConfigProtocol (Handle);\r
+  if (VlanConfig == NULL) {\r
+    return ;\r
+  }\r
+\r
+  MacStr  = NULL;\r
+  Status  = NetLibGetMacString (Handle, mImageHandle, &MacStr);\r
+  if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_MAC_FAIL), mHiiHandle, Status);\r
+    goto Exit;\r
+  }\r
+\r
+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_ETH_MAC), mHiiHandle, NicIndex, MacStr);\r
+\r
+  Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData);\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_NOT_FOUND) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VLAN), mHiiHandle);\r
+    } else {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_FIND_FAIL), mHiiHandle, Status);\r
+    }\r
+\r
+    goto Exit;\r
+  }\r
+\r
+  for (Index = 0; Index < NumberOfVlan; Index++) {\r
+    ShellPrintHiiEx (\r
+      -1,\r
+      -1,\r
+      NULL,\r
+      STRING_TOKEN (STR_VCONFIG_VLAN_DISPLAY),\r
+      mHiiHandle,\r
+      VlanData[Index].VlanId,\r
+      VlanData[Index].Priority\r
+      );\r
+  }\r
+\r
+  FreePool (VlanData);\r
+\r
+Exit:\r
+  CloseVlanConfigProtocol (Handle);\r
+\r
+  if (MacStr != NULL) {\r
+    FreePool (MacStr);\r
+  }\r
+}\r
+\r
+/**\r
+  Display the VLAN configuration of all, or a specified network interface.\r
+\r
+  @param[in]  Name           Name of the network interface. If NULL, the VLAN\r
+                             configuration of all network will be displayed.\r
+\r
+**/\r
+VOID\r
+DisplayVlan (\r
+  IN CHAR16              *Name OPTIONAL\r
+  )\r
+{\r
+  UINTN       NumberOfHandles;\r
+  EFI_HANDLE  *HandleBuffer;\r
+  UINTN       Index;\r
+  EFI_HANDLE  NicHandle;\r
+\r
+  if (Name != NULL) {\r
+    //\r
+    // Display specified NIC\r
+    //\r
+    NicHandle = NicNameToHandle (Name);\r
+    if (NicHandle == NULL) {\r
+      return ;\r
+    }\r
+\r
+    ShowNicVlanInfo (NicHandle, 0);\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Find all NIC handles\r
+  //\r
+  LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer);\r
+  if (NumberOfHandles == 0) {\r
+    return ;\r
+  }\r
+\r
+  for (Index = 0; Index < NumberOfHandles; Index++) {\r
+    ShowNicVlanInfo (HandleBuffer[Index], Index);\r
+  }\r
+\r
+  FreePool (HandleBuffer);\r
+}\r
+\r
+/**\r
+  Convert a NULL-terminated unicode decimal VLAN ID string to VLAN ID.\r
+\r
+  @param[in]  String       Pointer to VLAN ID string from user input.\r
+\r
+  @retval Value translated from String, or INVALID_VLAN_ID is string is invalid.\r
+\r
+**/\r
+UINT16\r
+StrToVlanId (\r
+  IN CHAR16             *String\r
+  )\r
+{\r
+  CHAR16  *Str;\r
+\r
+  if (String == NULL) {\r
+    return INVALID_VLAN_ID;\r
+  }\r
+\r
+  Str = String;\r
+  while ((*Str >= '0') && (*Str <= '9')) {\r
+    Str++;\r
+  }\r
+\r
+  if (*Str != 0) {\r
+    return INVALID_VLAN_ID;\r
+  }\r
+\r
+  return (UINT16) StrDecimalToUintn (String);\r
+}\r
+\r
+/**\r
+  Add a VLAN device.\r
+\r
+  @param[in]  ParamStr       Parameter string from user input.\r
+\r
+**/\r
+VOID\r
+AddVlan (\r
+  IN CHAR16             *ParamStr\r
+  )\r
+{\r
+  CHAR16                    *Name;\r
+  CHAR16                    *VlanIdStr;\r
+  CHAR16                    *PriorityStr;\r
+  CHAR16                    *StrPtr;\r
+  BOOLEAN                   IsSpace;\r
+  UINTN                     VlanId;\r
+  UINTN                     Priority;\r
+  EFI_HANDLE                Handle;\r
+  EFI_HANDLE                VlanHandle;\r
+  EFI_VLAN_CONFIG_PROTOCOL  *VlanConfig;\r
+  EFI_STATUS                Status;\r
+\r
+  VlanConfig  = NULL;\r
+  Priority    = 0;\r
+\r
+  if (ParamStr == NULL) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle);\r
+    return ;\r
+  }\r
+\r
+  StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr);\r
+  if (StrPtr == NULL) {\r
+    return ;\r
+  }\r
+\r
+  Name        = StrPtr;\r
+  VlanIdStr   = NULL;\r
+  PriorityStr = NULL;\r
+  IsSpace     = FALSE;\r
+  while (*StrPtr != 0) {\r
+    if (*StrPtr == L' ') {\r
+      *StrPtr = 0;\r
+      IsSpace = TRUE;\r
+    } else {\r
+      if (IsSpace) {\r
+        //\r
+        // Start of a parameter.\r
+        //\r
+        if (VlanIdStr == NULL) {\r
+          //\r
+          // 2nd parameter is VLAN ID.\r
+          //\r
+          VlanIdStr = StrPtr;\r
+        } else if (PriorityStr == NULL) {\r
+          //\r
+          // 3rd parameter is Priority.\r
+          //\r
+          PriorityStr = StrPtr;\r
+        } else {\r
+          //\r
+          // Ignore else parameters.\r
+          //\r
+          break;\r
+        }\r
+      }\r
+\r
+      IsSpace = FALSE;\r
+    }\r
+\r
+    StrPtr++;\r
+  }\r
+\r
+  Handle = NicNameToHandle (Name);\r
+  if (Handle == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  VlanConfig = OpenVlanConfigProtocol (Handle);\r
+  if (VlanConfig == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Check VLAN ID.\r
+  //\r
+  if ((VlanIdStr == NULL) || (*VlanIdStr == 0)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle);\r
+    goto Exit;\r
+  }\r
+\r
+  VlanId = StrToVlanId (VlanIdStr);\r
+  if (VlanId > 4094) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr);\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Check Priority.\r
+  //\r
+  if ((PriorityStr != NULL) && (*PriorityStr != 0)) {\r
+    Priority = StrDecimalToUintn (PriorityStr);\r
+    if (Priority > 7) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_PRIORITY), mHiiHandle, PriorityStr);\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Set VLAN\r
+  //\r
+  Status = VlanConfig->Set (VlanConfig, (UINT16) VlanId, (UINT8) Priority);\r
+  if (EFI_ERROR (Status)) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_FAIL), mHiiHandle, Status);\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Connect the VLAN device.\r
+  //\r
+  VlanHandle = NetLibGetVlanHandle (Handle, (UINT16) VlanId);\r
+  if (VlanHandle != NULL) {\r
+    gBS->ConnectController (VlanHandle, NULL, NULL, TRUE);\r
+  }\r
+\r
+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_SUCCESS), mHiiHandle);\r
+\r
+Exit:\r
+  if (VlanConfig != NULL) {\r
+    CloseVlanConfigProtocol (Handle);\r
+  }\r
+\r
+  FreePool (Name);\r
+}\r
+\r
+/**\r
+  Remove a VLAN device.\r
+\r
+  @param[in]  ParamStr       Parameter string from user input.\r
+\r
+**/\r
+VOID\r
+DeleteVlan (\r
+  CHAR16 *ParamStr\r
+  )\r
+{\r
+  CHAR16                    *Name;\r
+  CHAR16                    *VlanIdStr;\r
+  CHAR16                    *StrPtr;\r
+  UINTN                     VlanId;\r
+  EFI_HANDLE                Handle;\r
+  EFI_VLAN_CONFIG_PROTOCOL  *VlanConfig;\r
+  EFI_STATUS                Status;\r
+  UINT16                    NumberOfVlan;\r
+  EFI_VLAN_FIND_DATA        *VlanData;\r
+\r
+  VlanConfig = NULL;\r
+\r
+  if (ParamStr == NULL) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle);\r
+    return ;\r
+  }\r
+\r
+  StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr);\r
+  if (StrPtr == NULL) {\r
+    return ;\r
+  }\r
+\r
+  Name      = StrPtr;\r
+  VlanIdStr = NULL;\r
+  while (*StrPtr != 0) {\r
+    if (*StrPtr == L'.') {\r
+      *StrPtr   = 0;\r
+      VlanIdStr = StrPtr + 1;\r
+      break;\r
+    }\r
+\r
+    StrPtr++;\r
+  }\r
+\r
+  Handle = NicNameToHandle (Name);\r
+  if (Handle == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  VlanConfig = OpenVlanConfigProtocol (Handle);\r
+  if (VlanConfig == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Check VLAN ID\r
+  //\r
+  if (VlanIdStr == NULL || *VlanIdStr == 0) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle);\r
+    goto Exit;\r
+  }\r
+\r
+  VlanId = StrToVlanId (VlanIdStr);\r
+  if (VlanId > 4094) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr);\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Delete VLAN.\r
+  //\r
+  Status = VlanConfig->Remove (VlanConfig, (UINT16) VlanId);\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_NOT_FOUND) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NOT_FOUND), mHiiHandle);\r
+    } else {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_FAIL), mHiiHandle, Status);\r
+    }\r
+\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Check whether this is the last VLAN to remove.\r
+  //\r
+  Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData);\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // This is the last VLAN to remove, try to connect the controller handle.\r
+    //\r
+    gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
+  } else {\r
+    FreePool (VlanData);\r
+  }\r
+\r
+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_SUCCESS), mHiiHandle);\r
+\r
+Exit:\r
+  if (VlanConfig != NULL) {\r
+    CloseVlanConfigProtocol (Handle);\r
+  }\r
+\r
+  FreePool (Name);\r
+}\r
+\r
+/**\r
+  The actual entry point for the application.\r
+\r
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.\r
+  @param[in] SystemTable    A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS       The entry point executed successfully.\r
+  @retval other             Some error occur when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VlanConfigMain (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  LIST_ENTRY    *List;\r
+  CONST CHAR16  *Str;\r
+\r
+  mImageHandle = ImageHandle;\r
+\r
+  //\r
+  // Register our string package to HII database.\r
+  //\r
+  mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, VConfigStrings, NULL);\r
+  if (mHiiHandle == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  List = NULL;\r
+  ShellCommandLineParseEx (mParamList, &List, NULL, FALSE, FALSE);\r
+  if (List == NULL) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle);\r
+    goto Exit;\r
+  }\r
+\r
+  if (ShellCommandLineGetFlag (List, L"-?")) {\r
+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_HELP), mHiiHandle);\r
+    goto Exit;\r
+  }\r
+\r
+  if (ShellCommandLineGetFlag (List, L"-l")) {\r
+    Str = ShellCommandLineGetValue (List, L"-l");\r
+    DisplayVlan ((CHAR16 *) Str);\r
+    goto Exit;\r
+  }\r
+\r
+  if (ShellCommandLineGetFlag (List, L"-a")) {\r
+    Str = ShellCommandLineGetValue (List, L"-a");\r
+    AddVlan ((CHAR16 *) Str);\r
+    goto Exit;\r
+  }\r
+\r
+  if (ShellCommandLineGetFlag (List, L"-d")) {\r
+    Str = ShellCommandLineGetValue (List, L"-d");\r
+    DeleteVlan ((CHAR16 *) Str);\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // No valid argument till now.\r
+  //\r
+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle);\r
+\r
+Exit:\r
+  if (List != NULL) {\r
+    ShellCommandLineFreeVarList (List);\r
+  }\r
+\r
+  //\r
+  // Remove our string package from HII database.\r
+  //\r
+  HiiRemovePackages (mHiiHandle);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/NetworkPkg/Application/VConfig/VConfig.inf b/NetworkPkg/Application/VConfig/VConfig.inf
new file mode 100644 (file)
index 0000000..e69da02
--- /dev/null
@@ -0,0 +1,47 @@
+## @file\r
+#  Component files for VLAN configuration shell application.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = VConfig\r
+  FILE_GUID                      = 87E36301-0406-44db-AAF3-9E0E591F3725\r
+  MODULE_TYPE                    = UEFI_APPLICATION\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = VlanConfigMain\r
+\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF\r
+#\r
+\r
+[Sources]\r
+  VConfigStrings.uni\r
+  VConfig.c\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  ShellPkg/ShellPkg.dec\r
+\r
+[LibraryClasses]\r
+  UefiApplicationEntryPoint\r
+  UefiBootServicesTableLib\r
+  UefiLib\r
+  ShellLib\r
+  NetLib\r
+  MemoryAllocationLib\r
+  HiiLib\r
+\r
+[Protocols]\r
+  gEfiVlanConfigProtocolGuid\r
diff --git a/NetworkPkg/Application/VConfig/VConfigStrings.uni b/NetworkPkg/Application/VConfig/VConfigStrings.uni
new file mode 100644 (file)
index 0000000..e8bdcef
Binary files /dev/null and b/NetworkPkg/Application/VConfig/VConfigStrings.uni differ
diff --git a/NetworkPkg/Dhcp6Dxe/ComponentName.c b/NetworkPkg/Dhcp6Dxe/ComponentName.c
new file mode 100644 (file)
index 0000000..314ca37
--- /dev/null
@@ -0,0 +1,312 @@
+/** @file\r
+  UEFI Component Name(2) protocol implementation for Dhcp6 driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that attempt to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that attempts to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language, from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  );\r
+\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL    gDhcp6ComponentName = {\r
+  Dhcp6ComponentNameGetDriverName,\r
+  Dhcp6ComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL   gDhcp6ComponentName2 = {\r
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Dhcp6ComponentNameGetDriverName,\r
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Dhcp6ComponentNameGetControllerName,\r
+  "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE       mDhcp6DriverNameTable[] = {\r
+  {\r
+    "eng;en",\r
+    L"DHCP6 Protocol Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+{\r
+  return LookupUnicodeString2 (\r
+           Language,\r
+           This->SupportedLanguages,\r
+           mDhcp6DriverNameTable,\r
+           DriverName,\r
+           (BOOLEAN)(This == &gDhcp6ComponentName)\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in the\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language, from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c
new file mode 100644 (file)
index 0000000..d14f169
--- /dev/null
@@ -0,0 +1,782 @@
+/** @file\r
+  Driver Binding functions and Service Binding functions\r
+  implementationfor for Dhcp6 Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gDhcp6DriverBinding = {\r
+  Dhcp6DriverBindingSupported,\r
+  Dhcp6DriverBindingStart,\r
+  Dhcp6DriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL gDhcp6ServiceBindingTemplate = {\r
+  Dhcp6ServiceBindingCreateChild,\r
+  Dhcp6ServiceBindingDestroyChild\r
+};\r
+\r
+\r
+/**\r
+  Configure the default Udp6Io to receive all the DHCP6 traffic\r
+  on this network interface.\r
+\r
+  @param[in]  UdpIo                  The pointer to Udp6Io to be configured.\r
+  @param[in]  Context                The pointer to the context.\r
+\r
+  @retval EFI_SUCCESS            The Udp6Io is successfully configured.\r
+  @retval Others                 Failed to configure the Udp6Io.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ConfigureUdpIo (\r
+  IN UDP_IO                 *UdpIo,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+  EFI_UDP6_CONFIG_DATA      *Config;\r
+\r
+  Udp6   = UdpIo->Protocol.Udp6;\r
+  Config = &(UdpIo->Config.Udp6);\r
+\r
+  ZeroMem (Config, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+  //\r
+  // Set Udp6 configure data for the Dhcp6 instance.\r
+  //\r
+  Config->AcceptPromiscuous  = FALSE;\r
+  Config->AcceptAnyPort      = FALSE;\r
+  Config->AllowDuplicatePort = FALSE;\r
+  Config->TrafficClass       = 0;\r
+  Config->HopLimit           = 128;\r
+  Config->ReceiveTimeout     = 0;\r
+  Config->TransmitTimeout    = 0;\r
+\r
+  //\r
+  // Configure an endpoint of client(0, 546), server(0, 0), the addresses\r
+  // will be overridden later. Note that we MUST not limit RemotePort.\r
+  // More details, refer to RFC 3315 section 5.2.\r
+  //\r
+  Config->StationPort        = DHCP6_PORT_CLIENT;\r
+  Config->RemotePort         = 0;\r
+\r
+  return Udp6->Configure (Udp6, Config);;\r
+}\r
+\r
+\r
+/**\r
+  Destory the Dhcp6 service. The Dhcp6 service may be partly initialized,\r
+  or partly destroyed. If a resource is destroyed, it is marked as such in\r
+  case the destroy failed and being called again later.\r
+\r
+  @param[in, out]  Service       The pointer to Dhcp6 service to be destroyed.\r
+\r
+**/\r
+VOID\r
+Dhcp6DestroyService (\r
+  IN OUT DHCP6_SERVICE          *Service\r
+  )\r
+{\r
+  //\r
+  // All children instances should have been already destoryed here.\r
+  //\r
+  ASSERT (Service->NumOfChild == 0);\r
+\r
+  if (Service->ClientId != NULL) {\r
+    FreePool (Service->ClientId);\r
+  }\r
+\r
+  if (Service->UdpIo != NULL) {\r
+    UdpIoFreeIo (Service->UdpIo);\r
+  }\r
+\r
+  FreePool (Service);\r
+}\r
+\r
+\r
+/**\r
+  Create a new Dhcp6 service for the Nic controller.\r
+\r
+  @param[in]  Controller             The controller to be installed DHCP6 service\r
+                                     binding protocol.\r
+  @param[in]  ImageHandle            The image handle of the Dhcp6 driver.\r
+  @param[out] Service                The return pointer of the new Dhcp6 service.\r
+\r
+  @retval EFI_SUCCESS            The Dhcp6 service is created successfully.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CreateService (\r
+  IN  EFI_HANDLE            Controller,\r
+  IN  EFI_HANDLE            ImageHandle,\r
+  OUT DHCP6_SERVICE         **Service\r
+  )\r
+{\r
+  DHCP6_SERVICE             *Dhcp6Srv;\r
+\r
+  *Service = NULL;\r
+  Dhcp6Srv = AllocateZeroPool (sizeof (DHCP6_SERVICE));\r
+\r
+  if (Dhcp6Srv == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Open the SNP protocol to get mode data later.\r
+  //\r
+  Dhcp6Srv->Snp = NULL;\r
+  NetLibGetSnpHandle (Controller, &Dhcp6Srv->Snp);\r
+  if (Dhcp6Srv->Snp == NULL) {\r
+    FreePool (Dhcp6Srv);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Initialize the fields of the new Dhcp6 service.\r
+  //\r
+  Dhcp6Srv->Signature       = DHCP6_SERVICE_SIGNATURE;\r
+  Dhcp6Srv->InDestory       = FALSE;\r
+  Dhcp6Srv->Controller      = Controller;\r
+  Dhcp6Srv->Image           = ImageHandle;\r
+  Dhcp6Srv->Xid             = (0xffffff & NET_RANDOM (NetRandomInitSeed ()));\r
+\r
+  CopyMem (\r
+    &Dhcp6Srv->ServiceBinding,\r
+    &gDhcp6ServiceBindingTemplate,\r
+    sizeof (EFI_SERVICE_BINDING_PROTOCOL)\r
+    );\r
+\r
+  //\r
+  // Generate client Duid in the format of Duid-llt.\r
+  //\r
+  Dhcp6Srv->ClientId        = Dhcp6GenerateClientId (Dhcp6Srv->Snp->Mode);\r
+\r
+  if (Dhcp6Srv->ClientId == NULL) {\r
+    FreePool (Dhcp6Srv);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create an Udp6Io for stateful transmit/receive of each Dhcp6 instance.\r
+  //\r
+  Dhcp6Srv->UdpIo = UdpIoCreateIo (\r
+                      Controller,\r
+                      ImageHandle,\r
+                      Dhcp6ConfigureUdpIo,\r
+                      UDP_IO_UDP6_VERSION,\r
+                      NULL\r
+                      );\r
+\r
+  if (Dhcp6Srv->UdpIo == NULL) {\r
+    FreePool (Dhcp6Srv->ClientId);\r
+    FreePool (Dhcp6Srv);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  InitializeListHead (&Dhcp6Srv->Child);\r
+\r
+  *Service = Dhcp6Srv;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Destroy the Dhcp6 instance and recycle the resources.\r
+\r
+  @param[in, out]  Instance        The pointer to the Dhcp6 instance.\r
+\r
+**/\r
+VOID\r
+Dhcp6DestroyInstance (\r
+  IN OUT DHCP6_INSTANCE         *Instance\r
+  )\r
+{\r
+  //\r
+  // Clean up the retry list first.\r
+  //\r
+  Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL);\r
+  gBS->CloseEvent (Instance->Timer);\r
+\r
+  //\r
+  // Clean up the current configure data.\r
+  //\r
+  if (Instance->Config != NULL) {\r
+    Dhcp6CleanupConfigData (Instance->Config);\r
+    FreePool (Instance->Config);\r
+  }\r
+\r
+  //\r
+  // Clean up the current Ia.\r
+  //\r
+  if (Instance->IaCb.Ia != NULL) {\r
+    if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
+      FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+    }\r
+    FreePool (Instance->IaCb.Ia);\r
+  }\r
+\r
+  if (Instance->Unicast != NULL) {\r
+    FreePool (Instance->Unicast);\r
+  }\r
+\r
+  if (Instance->AdSelect != NULL) {\r
+    FreePool (Instance->AdSelect);\r
+  }\r
+\r
+  FreePool (Instance);\r
+}\r
+\r
+\r
+/**\r
+  Create the Dhcp6 instance and initialize it.\r
+\r
+  @param[in]  Service              The pointer to the Dhcp6 service.\r
+  @param[out] Instance             The pointer to the Dhcp6 instance.\r
+\r
+  @retval EFI_SUCCESS            The Dhcp6 instance is created.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CreateInstance (\r
+  IN  DHCP6_SERVICE         *Service,\r
+  OUT DHCP6_INSTANCE        **Instance\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  DHCP6_INSTANCE            *Dhcp6Ins;\r
+\r
+  *Instance = NULL;\r
+  Dhcp6Ins  = AllocateZeroPool (sizeof (DHCP6_INSTANCE));\r
+\r
+  if (Dhcp6Ins == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Initialize the fields of the new Dhcp6 instance.\r
+  //\r
+  Dhcp6Ins->Signature       = DHCP6_INSTANCE_SIGNATURE;\r
+  Dhcp6Ins->UdpSts          = EFI_ALREADY_STARTED;\r
+  Dhcp6Ins->Service         = Service;\r
+  Dhcp6Ins->InDestory       = FALSE;\r
+  Dhcp6Ins->MediaPresent    = TRUE;\r
+\r
+  CopyMem (\r
+    &Dhcp6Ins->Dhcp6,\r
+    &gDhcp6ProtocolTemplate,\r
+    sizeof (EFI_DHCP6_PROTOCOL)\r
+    );\r
+\r
+  InitializeListHead (&Dhcp6Ins->TxList);\r
+  InitializeListHead (&Dhcp6Ins->InfList);\r
+\r
+  //\r
+  // There is a timer for each Dhcp6 instance, which is used to track the\r
+  // lease time of Ia and the retransmisson time of all sent packets.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  Dhcp6OnTimerTick,\r
+                  Dhcp6Ins,\r
+                  &Dhcp6Ins->Timer\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Dhcp6Ins);\r
+    return Status;\r
+  }\r
+\r
+  *Instance = Dhcp6Ins;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Entry point of the DHCP6 driver to install various protocols.\r
+\r
+  @param[in]  ImageHandle           The handle of the UEFI image file.\r
+  @param[in]  SystemTable           The pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval Others                Unexpected error occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverEntryPoint (\r
+  IN EFI_HANDLE                   ImageHandle,\r
+  IN EFI_SYSTEM_TABLE             *SystemTable\r
+  )\r
+{\r
+  return EfiLibInstallDriverBindingComponentName2 (\r
+           ImageHandle,\r
+           SystemTable,\r
+           &gDhcp6DriverBinding,\r
+           ImageHandle,\r
+           &gDhcp6ComponentName,\r
+           &gDhcp6ComponentName2\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle. This service\r
+  is called by the EFI boot service ConnectController(). In\r
+  order to make drivers as small as possible, there are a few calling\r
+  restrictions for this service. ConnectController() must\r
+  follow these calling restrictions. If any other agent wishes to call\r
+  Supported() it must also follow these calling restrictions.\r
+\r
+  @param[in]  This                The pointer to the driver binding protocol.\r
+  @param[in]  ControllerHandle    The handle of device to be tested.\r
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child\r
+                                  device to be started.\r
+\r
+  @retval EFI_SUCCESS         This driver supports this device.\r
+  @retval Others              This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  return gBS->OpenProtocol (\r
+                ControllerHandle,\r
+                &gEfiUdp6ServiceBindingProtocolGuid,\r
+                NULL,\r
+                This->DriverBindingHandle,\r
+                ControllerHandle,\r
+                EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                );\r
+}\r
+\r
+\r
+/**\r
+  Start this driver on ControllerHandle. This service is called by the\r
+  EFI boot service ConnectController(). In order to make\r
+  drivers as small as possible, there are a few calling restrictions for\r
+  this service. ConnectController() must follow these\r
+  calling restrictions. If any other agent wishes to call Start() it\r
+  must also follow these calling restrictions.\r
+\r
+  @param[in]  This                 The pointer to the driver binding protocol.\r
+  @param[in]  ControllerHandle     The handle of device to be started.\r
+  @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child\r
+                                   device to be started.\r
+\r
+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  DHCP6_SERVICE                   *Service;\r
+\r
+  //\r
+  // Check the Dhcp6 serivce whether already started.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiDhcp6ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  //\r
+  // Create and initialize the Dhcp6 service.\r
+  //\r
+  Status = Dhcp6CreateService (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &Service\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  ASSERT (Service != NULL);\r
+\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &ControllerHandle,\r
+                  &gEfiDhcp6ServiceBindingProtocolGuid,\r
+                  &Service->ServiceBinding,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Dhcp6DestroyService (Service);\r
+    return Status;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Stop this driver on ControllerHandle. This service is called by the\r
+  EFI boot service DisconnectController(). In order to\r
+  make drivers as small as possible, there are a few calling\r
+  restrictions for this service. DisconnectController()\r
+  must follow these calling restrictions. If any other agent wishes\r
+  to call Stop() it must also follow these calling restrictions.\r
+\r
+  @param[in]  This              Protocol instance pointer.\r
+  @param[in]  ControllerHandle  Handle of device to stop driver on\r
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
+                                children is zero stop the entire bus driver.\r
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCESS           This driver is removed ControllerHandle\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+  @retval other                 This driver was not removed from this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer   OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  EFI_TPL                          OldTpl;\r
+  EFI_HANDLE                       NicHandle;\r
+  EFI_SERVICE_BINDING_PROTOCOL     *ServiceBinding;\r
+  DHCP6_SERVICE                    *Service;\r
+  DHCP6_INSTANCE                   *Instance;\r
+\r
+  //\r
+  // Find and check the Nic handle by the controller handle.\r
+  //\r
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);\r
+\r
+  if (NicHandle == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  NicHandle,\r
+                  &gEfiDhcp6ServiceBindingProtocolGuid,\r
+                  (VOID **) &ServiceBinding,\r
+                  This->DriverBindingHandle,\r
+                  NicHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Service = DHCP6_SERVICE_FROM_THIS (ServiceBinding);\r
+\r
+  if (Service->InDestory) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (NumberOfChildren == 0) {\r
+    //\r
+    // Destory the service itself if no child instance left.\r
+    //\r
+    Service->InDestory = TRUE;\r
+\r
+    Status = gBS->UninstallProtocolInterface (\r
+                    NicHandle,\r
+                    &gEfiDhcp6ServiceBindingProtocolGuid,\r
+                    ServiceBinding\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      Service->InDestory = FALSE;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Dhcp6DestroyService (Service);\r
+\r
+  } else {\r
+    //\r
+    // Destory all the children instances before destory the service.\r
+    //\r
+    while (!IsListEmpty (&Service->Child)) {\r
+      Instance = NET_LIST_HEAD (&Service->Child, DHCP6_INSTANCE, Link);\r
+      ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);\r
+    }\r
+    //\r
+    // Any of child failed to be destroyed.\r
+    //\r
+    if (Service->NumOfChild != 0) {\r
+      Status = EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Creates a child handle and installs a protocol.\r
+\r
+  The CreateChild() function installs a protocol on ChildHandle.\r
+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+  @param[in]      This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+                              then a new handle is created. If it is a pointer to an existing\r
+                              UEFI handle, then the protocol is added to the existing UEFI handle.\r
+\r
+  @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+  @retval other                 The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ServiceBindingCreateChild (\r
+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN OUT EFI_HANDLE                    *ChildHandle\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  EFI_TPL                          OldTpl;\r
+  DHCP6_SERVICE                    *Service;\r
+  DHCP6_INSTANCE                   *Instance;\r
+  VOID                             *Udp6;\r
+\r
+  if (This == NULL || ChildHandle == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Service = DHCP6_SERVICE_FROM_THIS (This);\r
+\r
+  Status  = Dhcp6CreateInstance (Service, &Instance);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  ASSERT (Instance != NULL);\r
+\r
+  //\r
+  // Start the timer when the instance is ready to use.\r
+  //\r
+  Status = gBS->SetTimer (\r
+                  Instance->Timer,\r
+                  TimerPeriodic,\r
+                  TICKS_PER_SECOND\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Install the DHCP6 protocol onto ChildHandle.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiDhcp6ProtocolGuid,\r
+                  &Instance->Dhcp6,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Instance->Handle = *ChildHandle;\r
+\r
+  //\r
+  // Open the UDP6 protocol BY_CHILD.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Service->UdpIo->UdpHandle,\r
+                  &gEfiUdp6ProtocolGuid,\r
+                  (VOID **) &Udp6,\r
+                  gDhcp6DriverBinding.DriverBindingHandle,\r
+                  Instance->Handle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           Instance->Handle,\r
+           &gEfiDhcp6ProtocolGuid,\r
+           &Instance->Dhcp6,\r
+           NULL\r
+           );\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Add into the children list of its parent service.\r
+  //\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  InsertTailList (&Service->Child, &Instance->Link);\r
+  Service->NumOfChild++;\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  Dhcp6DestroyInstance (Instance);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Destroys a child handle with a protocol installed on it.\r
+\r
+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+  last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+  @param[in]  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in]  ChildHandle Handle of the child to destroy\r
+\r
+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.\r
+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.\r
+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+  @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle\r
+                                because its services are being used.\r
+  @retval other                 The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  EFI_TPL                          OldTpl;\r
+  EFI_DHCP6_PROTOCOL               *Dhcp6;\r
+  DHCP6_SERVICE                    *Service;\r
+  DHCP6_INSTANCE                   *Instance;\r
+\r
+  if (This == NULL || ChildHandle == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Retrieve the private context data structures\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ChildHandle,\r
+                  &gEfiDhcp6ProtocolGuid,\r
+                  (VOID **) &Dhcp6,\r
+                  gDhcp6DriverBinding.DriverBindingHandle,\r
+                  ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Instance = DHCP6_INSTANCE_FROM_THIS (Dhcp6);\r
+  Service  = DHCP6_SERVICE_FROM_THIS (This);\r
+\r
+  if (Instance->Service != Service) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Instance->InDestory) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Instance->InDestory = TRUE;\r
+\r
+  Status = gBS->CloseProtocol (\r
+                  Service->UdpIo->UdpHandle,\r
+                  &gEfiUdp6ProtocolGuid,\r
+                  gDhcp6DriverBinding.DriverBindingHandle,\r
+                  ChildHandle\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Instance->InDestory = FALSE;\r
+    gBS->RestoreTPL (OldTpl);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Uninstall the MTFTP6 protocol first to enable a top down destruction.\r
+  //\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  ChildHandle,\r
+                  &gEfiDhcp6ProtocolGuid,\r
+                  Dhcp6\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Instance->InDestory = FALSE;\r
+    gBS->RestoreTPL (OldTpl);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Remove it from the children list of its parent service.\r
+  //\r
+  RemoveEntryList (&Instance->Link);\r
+  Service->NumOfChild--;\r
+\r
+  Dhcp6DestroyInstance (Instance);\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h
new file mode 100644 (file)
index 0000000..5a88be4
--- /dev/null
@@ -0,0 +1,155 @@
+/** @file\r
+  Driver Binding functions and Service Binding functions\r
+  declaration for Dhcp6 Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_DHCP6_DRIVER_H__\r
+#define __EFI_DHCP6_DRIVER_H__\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+\r
+extern EFI_COMPONENT_NAME_PROTOCOL  gDhcp6ComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2;\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle. This service\r
+  is called by the EFI boot service ConnectController(). In\r
+  order to make drivers as small as possible, there are a few calling\r
+  restrictions for this service. ConnectController() must\r
+  follow these calling restrictions. If any other agent wishes to call\r
+  Supported(), it must also follow these calling restrictions.\r
+\r
+  @param[in]  This                Protocol instance pointer.\r
+  @param[in]  ControllerHandle    Handle of device to test.\r
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child\r
+                                  device to start.\r
+\r
+  @retval EFI_SUCCESS         This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED This driver is already running on this device.\r
+  @retval other               This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+/**\r
+  Start this driver on ControllerHandle. This service is called by the\r
+  EFI boot service ConnectController(). In order to make\r
+  drivers as small as possible, there are a few calling restrictions for\r
+  this service. ConnectController() must follow these\r
+  calling restrictions. If any other agent wishes to call Start(), it\r
+  must also follow these calling restrictions.\r
+\r
+  @param[in]  This                 Protocol instance pointer.\r
+  @param[in]  ControllerHandle     Handle of device to bind driver to.\r
+  @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child\r
+                                   device to start.\r
+\r
+  @retval EFI_SUCCESS          This driver is added to ControllerHandle.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+/**\r
+  Stop this driver on ControllerHandle. This service is called by the\r
+  EFI boot service DisconnectController(). In order to\r
+  make drivers as small as possible, there are a few calling\r
+  restrictions for this service. DisconnectController()\r
+  must follow these calling restrictions. If any other agent wishes\r
+  to call Stop() it must also follow these calling restrictions.\r
+\r
+  @param[in]  This              Protocol instance pointer.\r
+  @param[in]  ControllerHandle  Handle of device to stop driver on.\r
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If the number of\r
+                                children is zero, stop the entire bus driver.\r
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCESS       This driver is removed ControllerHandle.\r
+  @retval other             This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer      OPTIONAL\r
+  );\r
+\r
+/**\r
+  Creates a child handle and installs a protocol.\r
+\r
+  The CreateChild() function installs a protocol on ChildHandle.\r
+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+  @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param  ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+                      then a new handle is created. If it is a pointer to an existing UEFI handle,\r
+                      then the protocol is added to the existing UEFI handle.\r
+\r
+  @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create\r
+                                the child.\r
+  @retval other                 The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ServiceBindingCreateChild (\r
+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN OUT EFI_HANDLE                    *ChildHandle\r
+  );\r
+\r
+/**\r
+  Destroys a child handle with a protocol installed on it.\r
+\r
+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+  last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+  @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param  ChildHandle Handle of the child to destroy.\r
+\r
+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.\r
+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.\r
+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+  @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle\r
+                                because its services are being used.\r
+  @retval other                 The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf b/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf
new file mode 100644 (file)
index 0000000..f10b07a
--- /dev/null
@@ -0,0 +1,69 @@
+## @file\r
+#  Component description file for Dhcp6 module.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = Dhcp6Dxe\r
+  FILE_GUID                      = 95E3669D-34BE-4775-A651-7EA41B69D89E\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = Dhcp6DriverEntryPoint\r
+  UNLOAD_IMAGE                   = NetLibDefaultUnload\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+#  DRIVER_BINDING                =  gDhcp6DriverBinding\r
+#  COMPONENT_NAME                =  gDhcp6ComponentName\r
+#  COMPONENT_NAME2               =  gDhcp6ComponentName2\r
+#\r
+\r
+[Sources]\r
+  Dhcp6Driver.c\r
+  Dhcp6Driver.h\r
+  Dhcp6Impl.c\r
+  Dhcp6Impl.h\r
+  Dhcp6Io.c\r
+  Dhcp6Io.h\r
+  Dhcp6Utility.c\r
+  Dhcp6Utility.h\r
+  ComponentName.c\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  UefiLib\r
+  BaseLib\r
+  BaseMemoryLib\r
+  MemoryAllocationLib\r
+  UefiDriverEntryPoint\r
+  UefiBootServicesTableLib\r
+  UefiRuntimeServicesTableLib\r
+  DebugLib\r
+  NetLib\r
+  UdpIoLib\r
+\r
+\r
+[Protocols]\r
+  gEfiUdp6ServiceBindingProtocolGuid\r
+  gEfiUdp6ProtocolGuid\r
+  gEfiDhcp6ServiceBindingProtocolGuid\r
+  gEfiDhcp6ProtocolGuid\r
+\r
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c
new file mode 100644 (file)
index 0000000..3e73976
--- /dev/null
@@ -0,0 +1,1220 @@
+/** @file\r
+  This EFI_DHCP6_PROTOCOL interface implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+//\r
+// Well-known multi-cast address defined in section-24.1 of rfc-3315\r
+//\r
+//   ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2\r
+//   ALL_DHCP_Servers address:                  FF05::1:3\r
+//\r
+EFI_IPv6_ADDRESS   mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};\r
+EFI_IPv6_ADDRESS   mAllDhcpServersAddress         = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}};\r
+\r
+EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = {\r
+  EfiDhcp6GetModeData,\r
+  EfiDhcp6Configure,\r
+  EfiDhcp6Start,\r
+  EfiDhcp6InfoRequest,\r
+  EfiDhcp6RenewRebind,\r
+  EfiDhcp6Decline,\r
+  EfiDhcp6Release,\r
+  EfiDhcp6Stop,\r
+  EfiDhcp6Parse\r
+};\r
+\r
+/**\r
+  Starts the DHCPv6 standard S.A.R.R. process.\r
+\r
+  The Start() function starts the DHCPv6 standard process. This function can\r
+  be called only when the state of Dhcp6 instance is in the Dhcp6Init state.\r
+  If the DHCP process completes successfully, the state of the Dhcp6 instance\r
+  will be transferred through Dhcp6Selecting and Dhcp6Requesting to the\r
+  Dhcp6Bound state.\r
+  Refer to rfc-3315 for precise state transitions during this process. At the\r
+  time when each event occurs in this process, the callback function that was set\r
+  by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this\r
+  opportunity to control the process.\r
+\r
+  @param[in]  This              The pointer to Dhcp6 protocol.\r
+\r
+  @retval EFI_SUCCESS           The DHCPv6 standard process has started, or it has\r
+                                completed when CompletionEvent is NULL.\r
+  @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Child instance hasn't been configured.\r
+  @retval EFI_INVALID_PARAMETER This is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_TIMEOUT           The DHCPv6 configuration process failed because no\r
+                                response was received from the server within the\r
+                                specified timeout value.\r
+  @retval EFI_ABORTED           The user aborted the DHCPv6 process.\r
+  @retval EFI_ALREADY_STARTED   Some other Dhcp6 instance already started the DHCPv6\r
+                                standard process.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+  @retval EFI_NO_MEDIA          There was a media error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Start (\r
+  IN EFI_DHCP6_PROTOCOL        *This\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_TPL                      OldTpl;\r
+  DHCP6_INSTANCE               *Instance;\r
+  DHCP6_SERVICE                *Service;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+  Service  = Instance->Service;\r
+\r
+  //\r
+  // The instance hasn't been configured.\r
+  //\r
+  if (Instance->Config == NULL) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+  //\r
+  // The instance has already been started.\r
+  //\r
+  if (Instance->IaCb.Ia->State != Dhcp6Init) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+  //\r
+  // Need to clear initial time to make sure that elapsed-time\r
+  // is set to 0 for first Solicit.\r
+  //\r
+  Instance->StartTime = 0;\r
+\r
+  //\r
+  // Send the solicit message to start S.A.R.R process.\r
+  //\r
+  Status = Dhcp6SendSolicitMsg (Instance);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Register receive callback for the stateful exchange process.\r
+  //\r
+  Status = UdpIoRecvDatagram(\r
+             Service->UdpIo,\r
+             Dhcp6ReceivePacket,\r
+             Service,\r
+             0\r
+             );\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  //\r
+  // Poll udp out of the net tpl if synchronous call.\r
+  //\r
+  if (Instance->Config->IaInfoEvent == NULL) {\r
+\r
+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+    }\r
+    return Instance->UdpSts;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stops the DHCPv6 standard S.A.R.R. process.\r
+\r
+  The Stop() function is used to stop the DHCPv6 standard process. After this\r
+  function is called successfully, the state of Dhcp6 instance is transferred\r
+  into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called\r
+  before DHCPv6 standard process can be started again. This function can be\r
+  called when the Dhcp6 instance is in any state.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+\r
+  @retval EFI_SUCCESS           The Dhcp6 instance is now in the Dhcp6Init state.\r
+  @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Stop (\r
+  IN EFI_DHCP6_PROTOCOL        *This\r
+  )\r
+{\r
+  EFI_TPL                      OldTpl;\r
+  EFI_STATUS                   Status;\r
+  EFI_UDP6_PROTOCOL            *Udp6;\r
+  DHCP6_INSTANCE               *Instance;\r
+  DHCP6_SERVICE                *Service;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+  Service  = Instance->Service;\r
+  Udp6     = Service->UdpIo->Protocol.Udp6;\r
+  Status   = EFI_SUCCESS;\r
+\r
+  //\r
+  // The instance hasn't been configured.\r
+  //\r
+  if (Instance->Config == NULL) {\r
+    return Status;\r
+  }\r
+\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+  //\r
+  // The instance has already been stopped.\r
+  //\r
+  if (Instance->IaCb.Ia->State == Dhcp6Init ||\r
+      Instance->IaCb.Ia->State == Dhcp6Selecting ||\r
+      Instance->IaCb.Ia->State == Dhcp6Requesting\r
+      ) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Release the current ready Ia.\r
+  //\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Instance->UdpSts = EFI_ALREADY_STARTED;\r
+  Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia);\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  //\r
+  // Poll udp out of the net tpl if synchoronus call.\r
+  //\r
+  if (Instance->Config->IaInfoEvent == NULL) {\r
+    ASSERT (Udp6 != NULL);\r
+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+      Udp6->Poll (Udp6);\r
+    }\r
+    Status = Instance->UdpSts;\r
+  }\r
+\r
+  //\r
+  // Clean up the session data for the released Ia.\r
+  //\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Dhcp6CleanupSession (Instance, EFI_SUCCESS);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Returns the current operating mode data for the Dhcp6 instance.\r
+\r
+  The GetModeData() function returns the current operating mode and\r
+  cached data packet for the Dhcp6 instance.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+  @param[out] Dhcp6ModeData     The pointer to the Dhcp6 mode data.\r
+  @param[out] Dhcp6ConfigData   The pointer to the Dhcp6 configure data.\r
+\r
+  @retval EFI_SUCCESS           The mode data was returned.\r
+  @retval EFI_INVALID_PARAMETER This is NULL.\r
+  @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Protocol instance was not\r
+                                configured.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6GetModeData (\r
+  IN  EFI_DHCP6_PROTOCOL       *This,\r
+  OUT EFI_DHCP6_MODE_DATA      *Dhcp6ModeData      OPTIONAL,\r
+  OUT EFI_DHCP6_CONFIG_DATA    *Dhcp6ConfigData    OPTIONAL\r
+  )\r
+{\r
+  EFI_TPL                      OldTpl;\r
+  EFI_DHCP6_IA                 *Ia;\r
+  DHCP6_INSTANCE               *Instance;\r
+  DHCP6_SERVICE                *Service;\r
+  UINT32                       IaSize;\r
+  UINT32                       IdSize;\r
+\r
+  if (This == NULL || (Dhcp6ModeData == NULL && Dhcp6ConfigData == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+  Service  = Instance->Service;\r
+\r
+  if (Instance->Config == NULL && Dhcp6ConfigData != NULL) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  ASSERT (Service->ClientId != NULL);\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // User needs a copy of instance config data.\r
+  //\r
+  if (Dhcp6ConfigData != NULL) {\r
+    ZeroMem (Dhcp6ConfigData, sizeof(EFI_DHCP6_CONFIG_DATA));\r
+    //\r
+    // Duplicate config data, including all reference buffers.\r
+    //\r
+    if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) {\r
+      goto ON_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // User need a copy of instance mode data.\r
+  //\r
+  if (Dhcp6ModeData != NULL) {\r
+    ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA));\r
+    //\r
+    // Duplicate a copy of EFI_DHCP6_DUID for client Id.\r
+    //\r
+    IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length);\r
+\r
+    Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize);\r
+    if (Dhcp6ModeData->ClientId == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    CopyMem (\r
+      Dhcp6ModeData->ClientId,\r
+      Service->ClientId,\r
+      IdSize\r
+      );\r
+\r
+    Ia = Instance->IaCb.Ia;\r
+    if (Ia != NULL) {\r
+      //\r
+      // Duplicate a copy of EFI_DHCP6_IA for configured Ia.\r
+      //\r
+      IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+\r
+      Dhcp6ModeData->Ia = AllocateZeroPool (IaSize);\r
+      if (Dhcp6ModeData->Ia == NULL) {\r
+        goto ON_ERROR;\r
+      }\r
+\r
+      CopyMem (\r
+        Dhcp6ModeData->Ia,\r
+        Ia,\r
+        IaSize\r
+        );\r
+\r
+      //\r
+      // Duplicate a copy of reply packet if has.\r
+      //\r
+      if (Ia->ReplyPacket != NULL) {\r
+        Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size);\r
+        if (Dhcp6ModeData->Ia->ReplyPacket == NULL) {\r
+          goto ON_ERROR;\r
+        }\r
+        CopyMem (\r
+          Dhcp6ModeData->Ia->ReplyPacket,\r
+          Ia->ReplyPacket,\r
+          Ia->ReplyPacket->Size\r
+          );\r
+      }\r
+    }\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  if (Dhcp6ConfigData != NULL) {\r
+    Dhcp6CleanupConfigData (Dhcp6ConfigData);\r
+  }\r
+  if (Dhcp6ModeData != NULL) {\r
+    Dhcp6CleanupModeData (Dhcp6ModeData);\r
+  }\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_OUT_OF_RESOURCES;\r
+}\r
+\r
+\r
+/**\r
+  Initializes, changes, or resets the operational settings for the Dhcp6 instance.\r
+\r
+  The Configure() function is used to initialize or clean up the configuration\r
+  data of the Dhcp6 instance:\r
+  - When Dhcp6CfgData is not NULL and Configure() is called successfully, the\r
+    configuration data will be initialized in the Dhcp6 instance, and the state\r
+    of the configured IA will be transferred into Dhcp6Init.\r
+  - When Dhcp6CfgData is NULL and Configure() is called successfully, the\r
+    configuration data will be cleaned up and no IA will be associated with\r
+    the Dhcp6 instance.\r
+  To update the configuration data for an Dhcp6 instance, the original data\r
+  must be cleaned up before setting the new configuration data.\r
+\r
+  @param[in]  This                   The pointer to the Dhcp6 protocol\r
+  @param[in]  Dhcp6CfgData           The pointer to the EFI_DHCP6_CONFIG_DATA.\r
+\r
+  @retval EFI_SUCCESS           The Dhcp6 is configured successfully with the\r
+                                Dhcp6Init state, or cleaned up the original\r
+                                configuration setting.\r
+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance was already configured.\r
+                                The Dhcp6 instance has already started the\r
+                                DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.\r
+  @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Configure (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN EFI_DHCP6_CONFIG_DATA     *Dhcp6CfgData    OPTIONAL\r
+  )\r
+{\r
+  EFI_TPL                      OldTpl;\r
+  EFI_STATUS                   Status;\r
+  LIST_ENTRY                   *Entry;\r
+  DHCP6_INSTANCE               *Other;\r
+  DHCP6_INSTANCE               *Instance;\r
+  DHCP6_SERVICE                *Service;\r
+  UINTN                        Index;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+  Service  = Instance->Service;\r
+\r
+  //\r
+  // Check the parameter of configure data.\r
+  //\r
+  if (Dhcp6CfgData != NULL) {\r
+    if (Dhcp6CfgData->OptionCount > 0 && Dhcp6CfgData->OptionList == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    if (Dhcp6CfgData->OptionList != NULL) {\r
+      for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) {\r
+        if (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId ||\r
+            Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit ||\r
+            Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept ||\r
+            Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana ||\r
+            Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata\r
+            ) {\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+      }\r
+    }\r
+\r
+    if (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA &&\r
+        Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA\r
+        ) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (Dhcp6CfgData->IaInfoEvent == NULL && Dhcp6CfgData->SolicitRetransmission == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (Dhcp6CfgData->SolicitRetransmission != NULL &&\r
+        Dhcp6CfgData->SolicitRetransmission->Mrc == 0 &&\r
+        Dhcp6CfgData->SolicitRetransmission->Mrd == 0\r
+        ) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Make sure the (IaId, IaType) is unique over all the instances.\r
+    //\r
+    NET_LIST_FOR_EACH (Entry, &Service->Child) {\r
+      Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link);\r
+      if (Other->IaCb.Ia != NULL &&\r
+          Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type &&\r
+          Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId\r
+          ) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (Dhcp6CfgData != NULL) {\r
+    //\r
+    // It's not allowed to configure one instance twice without configure null.\r
+    //\r
+    if (Instance->Config != NULL) {\r
+      gBS->RestoreTPL (OldTpl);\r
+      return EFI_ACCESS_DENIED;\r
+    }\r
+\r
+    //\r
+    // Duplicate config data including all reference buffers.\r
+    //\r
+    Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA));\r
+    if (Instance->Config == NULL) {\r
+      gBS->RestoreTPL (OldTpl);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData);\r
+    if (EFI_ERROR(Status)) {\r
+      FreePool (Instance->Config);\r
+      gBS->RestoreTPL (OldTpl);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    //\r
+    // Initialize the Ia descriptor from the config data, and leave the other\r
+    // fields of the Ia as default value 0.\r
+    //\r
+    Instance->IaCb.Ia = AllocateZeroPool (sizeof(EFI_DHCP6_IA));\r
+    if (Instance->IaCb.Ia == NULL) {\r
+      Dhcp6CleanupConfigData (Instance->Config);\r
+      FreePool (Instance->Config);\r
+      gBS->RestoreTPL (OldTpl);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    CopyMem (\r
+      &Instance->IaCb.Ia->Descriptor,\r
+      &Dhcp6CfgData->IaDescriptor,\r
+      sizeof(EFI_DHCP6_IA_DESCRIPTOR)\r
+      );\r
+\r
+  } else {\r
+\r
+    if (Instance->Config == NULL) {\r
+      ASSERT (Instance->IaCb.Ia == NULL);\r
+      gBS->RestoreTPL (OldTpl);\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    //\r
+    // It's not allowed to configure a started instance as null.\r
+    //\r
+    if (Instance->IaCb.Ia->State != Dhcp6Init) {\r
+      gBS->RestoreTPL (OldTpl);\r
+      return EFI_ACCESS_DENIED;\r
+    }\r
+\r
+    Dhcp6CleanupConfigData (Instance->Config);\r
+    FreePool (Instance->Config);\r
+    Instance->Config = NULL;\r
+\r
+    FreePool (Instance->IaCb.Ia);\r
+    Instance->IaCb.Ia = NULL;\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Request configuration information without the assignment of any\r
+  Ia addresses of the client.\r
+\r
+  The InfoRequest() function is used to request configuration information\r
+  without the assignment of any IPv6 address of the client. The client sends\r
+  out an Information Request packet to obtain the required configuration\r
+  information, and DHCPv6 server responds with a Reply packet containing\r
+  the information for the client. The received Reply packet will be passed\r
+  to the user by ReplyCallback function. If the user returns EFI_NOT_READY from\r
+  ReplyCallback, the Dhcp6 instance will continue to receive other Reply\r
+  packets unless timeout according to the Retransmission parameter.\r
+  Otherwise, the Information Request exchange process will be finished\r
+  successfully if user returns EFI_SUCCESS from ReplyCallback.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+  @param[in]  SendClientId      If TRUE, the DHCPv6 protocol instance will build Client\r
+                                Identifier option and include it into Information Request\r
+                                packet. Otherwise, Client Identifier option will not be included.\r
+  @param[in]  OptionRequest     The pointer to the buffer of option request options.\r
+  @param[in]  OptionCount       The option number in the OptionList.\r
+  @param[in]  OptionList        The list of appended options.\r
+  @param[in]  Retransmission    The pointer to the retransmission of the message.\r
+  @param[in]  TimeoutEvent      The event of timeout.\r
+  @param[in]  ReplyCallback     The callback function when the reply was received.\r
+  @param[in]  CallbackContext   The pointer to the parameter passed to the callback.\r
+\r
+  @retval EFI_SUCCESS           The DHCPv6 information request exchange process\r
+                                completed when TimeoutEvent is NULL. Information\r
+                                Request packet has been sent to DHCPv6 server when\r
+                                TimeoutEvent is not NULL.\r
+  @retval EFI_NO_RESPONSE       The DHCPv6 information request exchange process failed\r
+                                because of no response, or not all requested-options\r
+                                are responded by DHCPv6 servers when Timeout happened.\r
+  @retval EFI_ABORTED           The DHCPv6 information request exchange process was aborted\r
+                                by user.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6InfoRequest (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN BOOLEAN                   SendClientId,\r
+  IN EFI_DHCP6_PACKET_OPTION   *OptionRequest,\r
+  IN UINT32                    OptionCount,\r
+  IN EFI_DHCP6_PACKET_OPTION   *OptionList[]    OPTIONAL,\r
+  IN EFI_DHCP6_RETRANSMISSION  *Retransmission,\r
+  IN EFI_EVENT                 TimeoutEvent     OPTIONAL,\r
+  IN EFI_DHCP6_INFO_CALLBACK   ReplyCallback,\r
+  IN VOID                      *CallbackContext OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_TPL                      OldTpl;\r
+  DHCP6_INSTANCE               *Instance;\r
+  DHCP6_SERVICE                *Service;\r
+  DHCP6_INF_CB                 *InfCb;\r
+  UINTN                        Index;\r
+\r
+  if (This == NULL || OptionRequest == NULL || Retransmission == NULL || ReplyCallback == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Retransmission != NULL && Retransmission->Mrc == 0 && Retransmission->Mrd == 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (OptionCount > 0 && OptionList == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (OptionList != NULL) {\r
+    for (Index = 0; Index < OptionCount; Index++) {\r
+      if (OptionList[Index]->OpCode == Dhcp6OptClientId || OptionList[Index]->OpCode == Dhcp6OptRequestOption) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+\r
+  Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+  Service  = Instance->Service;\r
+\r
+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+  //\r
+  // Create and initialize the control block for the info-request.\r
+  //\r
+  InfCb = AllocateZeroPool (sizeof(DHCP6_INF_CB));\r
+\r
+  if (InfCb == NULL) {\r
+    gBS->RestoreTPL (OldTpl);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  InfCb->ReplyCallback   = ReplyCallback;\r
+  InfCb->CallbackContext = CallbackContext;\r
+  InfCb->TimeoutEvent    = TimeoutEvent;\r
+\r
+  InsertTailList (&Instance->InfList, &InfCb->Link);\r
+\r
+  //\r
+  // Send the info-request message to start exchange process.\r
+  //\r
+  Status = Dhcp6SendInfoRequestMsg (\r
+             Instance,\r
+             InfCb,\r
+             SendClientId,\r
+             OptionRequest,\r
+             OptionCount,\r
+             OptionList,\r
+             Retransmission\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Register receive callback for the stateless exchange process.\r
+  //\r
+  Status = UdpIoRecvDatagram(\r
+             Service->UdpIo,\r
+             Dhcp6ReceivePacket,\r
+             Service,\r
+             0\r
+             );\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  //\r
+  // Poll udp out of the net tpl if synchoronus call.\r
+  //\r
+  if (TimeoutEvent == NULL) {\r
+\r
+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+    }\r
+    return Instance->UdpSts;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  RemoveEntryList (&InfCb->Link);\r
+  FreePool (InfCb);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Manually extend the valid and preferred lifetimes for the IPv6 addresses\r
+  of the configured IA and update other configuration parameters by sending a\r
+  Renew or Rebind packet.\r
+\r
+  The RenewRebind() function is used to manually extend the valid and preferred\r
+  lifetimes for the IPv6 addresses of the configured IA, and update other\r
+  configuration parameters by sending Renew or Rebind packet.\r
+  - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,\r
+    it sends Renew packet to the previously DHCPv6 server and transfer the\r
+    state of the configured IA to Dhcp6Renewing. If valid Reply packet received,\r
+    the state transfers to Dhcp6Bound and the valid and preferred timer restarts.\r
+    If fails, the state transfers to Dhcp6Bound, but the timer continues.\r
+  - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,\r
+    it will send a Rebind packet. If valid Reply packet is received, the state transfers\r
+    to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state\r
+    transfers to Dhcp6Init, and the IA can't be used.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+  @param[in]  RebindRequest     If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.\r
+                                Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.\r
+\r
+  @retval EFI_SUCCESS           The DHCPv6 renew/rebind exchange process has\r
+                                completed and at least one IPv6 address of the\r
+                                configured IA has been bound again when\r
+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL.\r
+                                The EFI DHCPv6 Protocol instance has sent Renew\r
+                                or Rebind packet when\r
+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.\r
+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the\r
+                                state of the configured IA is not in Dhcp6Bound.\r
+  @retval EFI_ALREADY_STARTED   The state of the configured IA has already entered\r
+                                Dhcp6Renewing when RebindRequest is FALSE.\r
+                                The state of the configured IA has already entered\r
+                                Dhcp6Rebinding when RebindRequest is TRUE.\r
+  @retval EFI_ABORTED           The DHCPv6 renew/rebind exchange process aborted\r
+                                by the user.\r
+  @retval EFI_NO_RESPONSE       The DHCPv6 renew/rebind exchange process failed\r
+                                because of no response.\r
+  @retval EFI_NO_MAPPING        No IPv6 address has been bound to the configured\r
+                                IA after the DHCPv6 renew/rebind exchange process.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6RenewRebind (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN BOOLEAN                   RebindRequest\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_TPL                      OldTpl;\r
+  DHCP6_INSTANCE               *Instance;\r
+  DHCP6_SERVICE                *Service;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+  Service  = Instance->Service;\r
+\r
+  //\r
+  // The instance hasn't been configured.\r
+  //\r
+  if (Instance->Config == NULL) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+  //\r
+  // The instance has already entered renewing or rebinding state.\r
+  //\r
+  if ((Instance->IaCb.Ia->State == Dhcp6Rebinding && RebindRequest) ||\r
+      (Instance->IaCb.Ia->State == Dhcp6Renewing && !RebindRequest)\r
+      ) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  if (Instance->IaCb.Ia->State != Dhcp6Bound) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+  //\r
+  // Send renew/rebind message to start exchange process.\r
+  //\r
+  Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Register receive callback for the stateful exchange process.\r
+  //\r
+  Status = UdpIoRecvDatagram(\r
+             Service->UdpIo,\r
+             Dhcp6ReceivePacket,\r
+             Service,\r
+             0\r
+             );\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  //\r
+  // Poll udp out of the net tpl if synchoronus call.\r
+  //\r
+  if (Instance->Config->IaInfoEvent == NULL) {\r
+\r
+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+    }\r
+    return Instance->UdpSts;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Inform that one or more addresses assigned by a server are already\r
+  in use by another node.\r
+\r
+  The Decline() function is used to manually decline the assignment of\r
+  IPv6 addresses, which have been already used by another node. If all\r
+  IPv6 addresses of the configured IA are declined through this function,\r
+  the state of the IA will switch through Dhcp6Declining to Dhcp6Init.\r
+  Otherwise, the state of the IA will restore to Dhcp6Bound after the\r
+  declining process. The Decline() can only be called when the IA is in\r
+  Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,\r
+  this function is a blocking operation. It will return after the\r
+  declining process finishes, or aborted by user.\r
+\r
+  @param[in]  This              The pointer to EFI_DHCP6_PROTOCOL.\r
+  @param[in]  AddressCount      The number of declining addresses.\r
+  @param[in]  Addresses         The pointer to the buffer stored the declining\r
+                                addresses.\r
+\r
+  @retval EFI_SUCCESS           The DHCPv6 decline exchange process completed\r
+                                when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.\r
+                                The Dhcp6 instance sent Decline packet when\r
+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL.\r
+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the\r
+                                state of the configured IA is not in Dhcp6Bound.\r
+  @retval EFI_ABORTED           The DHCPv6 decline exchange process aborted by user.\r
+  @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with\r
+                                the configured IA for this instance.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Decline (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN UINT32                    AddressCount,\r
+  IN EFI_IPv6_ADDRESS          *Addresses\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_TPL                      OldTpl;\r
+  EFI_DHCP6_IA                 *DecIa;\r
+  DHCP6_INSTANCE               *Instance;\r
+  DHCP6_SERVICE                *Service;\r
+\r
+  if (This == NULL || AddressCount == 0 || Addresses == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+  Service  = Instance->Service;\r
+\r
+  //\r
+  // The instance hasn't been configured.\r
+  //\r
+  if (Instance->Config == NULL) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+  if (Instance->IaCb.Ia->State != Dhcp6Bound) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Check whether all the declined addresses belongs to the configured Ia.\r
+  //\r
+  Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);\r
+\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+  //\r
+  // Deprive of all the declined addresses from the configured Ia, and create a\r
+  // DeclineIa used to create decline message.\r
+  //\r
+  DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);\r
+\r
+  if (DecIa == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Send the decline message to start exchange process.\r
+  //\r
+  Status = Dhcp6SendDeclineMsg (Instance, DecIa);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Register receive callback for the stateful exchange process.\r
+  //\r
+  Status = UdpIoRecvDatagram(\r
+             Service->UdpIo,\r
+             Dhcp6ReceivePacket,\r
+             Service,\r
+             0\r
+             );\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  FreePool (DecIa);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  //\r
+  // Poll udp out of the net tpl if synchoronus call.\r
+  //\r
+  if (Instance->Config->IaInfoEvent == NULL) {\r
+\r
+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+    }\r
+    return Instance->UdpSts;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  if (DecIa != NULL) {\r
+    FreePool (DecIa);\r
+  }\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Release one or more addresses associated with the configured Ia\r
+  for current instance.\r
+\r
+  The Release() function is used to manually release one or more\r
+  IPv6 addresses. If AddressCount is zero, it will release all IPv6\r
+  addresses of the configured IA. If all IPv6 addresses of the IA are\r
+  released through this function, the state of the IA will switch\r
+  through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the\r
+  IA will restore to Dhcp6Bound after the releasing process.\r
+  The Release() can only be called when the IA is in Dhcp6Bound state.\r
+  If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is\r
+  a blocking operation. It will return after the releasing process\r
+  finishes, or is aborted by user.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+  @param[in]  AddressCount      The number of releasing addresses.\r
+  @param[in]  Addresses         The pointer to the buffer stored the releasing\r
+                                addresses.\r
+\r
+  @retval EFI_SUCCESS           The DHCPv6 release exchange process\r
+                                completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent\r
+                                was NULL. The Dhcp6 instance was sent Release\r
+                                packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent\r
+                                was not NULL.\r
+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the\r
+                                state of the configured IA is not in Dhcp6Bound.\r
+  @retval EFI_ABORTED           The DHCPv6 release exchange process aborted by user.\r
+  @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with\r
+                                the configured IA for this instance.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Release (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN UINT32                    AddressCount,\r
+  IN EFI_IPv6_ADDRESS          *Addresses\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_TPL                      OldTpl;\r
+  EFI_DHCP6_IA                 *RelIa;\r
+  DHCP6_INSTANCE               *Instance;\r
+  DHCP6_SERVICE                *Service;\r
+\r
+  if (This == NULL || (AddressCount != 0 && Addresses == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+  Service  = Instance->Service;\r
+\r
+  //\r
+  // The instance hasn't been configured.\r
+  //\r
+  if (Instance->Config == NULL) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+  if (Instance->IaCb.Ia->State != Dhcp6Bound) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Check whether all the released addresses belongs to the configured Ia.\r
+  //\r
+  Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);\r
+\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+  //\r
+  // Deprive of all the released addresses from the configured Ia, and create a\r
+  // ReleaseIa used to create release message.\r
+  //\r
+  RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);\r
+\r
+  if (RelIa == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Send the release message to start exchange process.\r
+  //\r
+  Status = Dhcp6SendReleaseMsg (Instance, RelIa);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Register receive callback for the stateful exchange process.\r
+  //\r
+  Status = UdpIoRecvDatagram(\r
+             Service->UdpIo,\r
+             Dhcp6ReceivePacket,\r
+             Service,\r
+             0\r
+             );\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  FreePool (RelIa);\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  //\r
+  // Poll udp out of the net tpl if synchoronus call.\r
+  //\r
+  if (Instance->Config->IaInfoEvent == NULL) {\r
+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+    }\r
+    return Instance->UdpSts;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  if (RelIa != NULL) {\r
+    FreePool (RelIa);\r
+  }\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Parse the option data in the Dhcp6 packet.\r
+\r
+  The Parse() function is used to retrieve the option list in the DHCPv6 packet.\r
+\r
+  @param[in]      This              The pointer to the Dhcp6 protocol.\r
+  @param[in]      Packet            The pointer to the Dhcp6 packet.\r
+  @param[in, out] OptionCount       The number of option in the packet.\r
+  @param[out]     PacketOptionList  The array of pointers to each option in the packet.\r
+\r
+  @retval EFI_SUCCESS           The packet was successfully parsed.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_BUFFER_TOO_SMALL  *OptionCount is smaller than the number of options\r
+                                that were found in the Packet.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Parse (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN EFI_DHCP6_PACKET          *Packet,\r
+  IN OUT UINT32                *OptionCount,\r
+  OUT EFI_DHCP6_PACKET_OPTION  *PacketOptionList[]  OPTIONAL\r
+  )\r
+{\r
+  UINT32                       OptCnt;\r
+  UINT32                       OptLen;\r
+  UINT16                       DataLen;\r
+  UINT8                        *Start;\r
+  UINT8                        *End;\r
+\r
+  if (This == NULL || Packet == NULL || OptionCount == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (*OptionCount != 0 && PacketOptionList == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Packet->Length > Packet->Size || Packet->Length < sizeof (EFI_DHCP6_HEADER)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  //  The format of Dhcp6 option:\r
+  //\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |          option-code          |   option-len (option data)    |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                          option-data                          |\r
+  //    |                      (option-len octets)                      |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  OptCnt = 0;\r
+  OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER);\r
+  Start  = Packet->Dhcp6.Option;\r
+  End    = Start + OptLen;\r
+\r
+  //\r
+  // Calculate the number of option in the packet.\r
+  //\r
+  while (Start < End) {\r
+    DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;\r
+    Start  += (NTOHS (DataLen) + 4);\r
+    OptCnt++;\r
+  }\r
+\r
+  //\r
+  // It will return buffer too small if pass-in option count is smaller than the\r
+  // actual count of options in the packet.\r
+  //\r
+  if (OptCnt > *OptionCount) {\r
+    *OptionCount = OptCnt;\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  ZeroMem (\r
+    PacketOptionList,\r
+    (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *))\r
+    );\r
+\r
+  OptCnt = 0;\r
+  Start  = Packet->Dhcp6.Option;\r
+\r
+  while (Start < End) {\r
+\r
+    PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *) Start;\r
+    DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;\r
+    Start  += (NTOHS (DataLen) + 4);\r
+    OptCnt++;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h
new file mode 100644 (file)
index 0000000..8fb1dfa
--- /dev/null
@@ -0,0 +1,597 @@
+/** @file\r
+  Dhcp6 internal data structure and definition declaration.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_DHCP6_IMPL_H__\r
+#define __EFI_DHCP6_IMPL_H__\r
+\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Dhcp6.h>\r
+#include <Protocol/Udp6.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DriverBinding.h>\r
+\r
+#include <Library/UdpIoLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+\r
+typedef struct _DHCP6_IA_CB    DHCP6_IA_CB;\r
+typedef struct _DHCP6_INF_CB   DHCP6_INF_CB;\r
+typedef struct _DHCP6_TX_CB    DHCP6_TX_CB;\r
+typedef struct _DHCP6_SERVICE  DHCP6_SERVICE;\r
+typedef struct _DHCP6_INSTANCE DHCP6_INSTANCE;\r
+\r
+#include "Dhcp6Utility.h"\r
+#include "Dhcp6Io.h"\r
+#include "Dhcp6Driver.h"\r
+\r
+#define DHCP6_SERVICE_SIGNATURE   SIGNATURE_32 ('D', 'H', '6', 'S')\r
+#define DHCP6_INSTANCE_SIGNATURE  SIGNATURE_32 ('D', 'H', '6', 'I')\r
+\r
+//\r
+// Transmit parameters of solicit message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_SOL_MAX_DELAY       1\r
+#define DHCP6_SOL_IRT             1\r
+#define DHCP6_SOL_MRC             0\r
+#define DHCP6_SOL_MRT             120\r
+#define DHCP6_SOL_MRD             0\r
+//\r
+// Transmit parameters of request message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_REQ_IRT             1\r
+#define DHCP6_REQ_MRC             10\r
+#define DHCP6_REQ_MRT             30\r
+#define DHCP6_REQ_MRD             0\r
+//\r
+// Transmit parameters of confirm message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_CNF_MAX_DELAY       1\r
+#define DHCP6_CNF_IRT             1\r
+#define DHCP6_CNF_MRC             0\r
+#define DHCP6_CNF_MRT             4\r
+#define DHCP6_CNF_MRD             10\r
+//\r
+// Transmit parameters of renew message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_REN_IRT             10\r
+#define DHCP6_REN_MRC             0\r
+#define DHCP6_REN_MRT             600\r
+#define DHCP6_REN_MRD             0\r
+//\r
+// Transmit parameters of rebind message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_REB_IRT             10\r
+#define DHCP6_REB_MRC             0\r
+#define DHCP6_REB_MRT             600\r
+#define DHCP6_REB_MRD             0\r
+//\r
+// Transmit parameters of information request message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_INF_MAX_DELAY       1\r
+#define DHCP6_INF_IRT             1\r
+#define DHCP6_INF_MRC             0\r
+#define DHCP6_INF_MRT             120\r
+#define DHCP6_INF_MRD             0\r
+//\r
+// Transmit parameters of release message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_REL_IRT             1\r
+#define DHCP6_REL_MRC             5\r
+#define DHCP6_REL_MRT             0\r
+#define DHCP6_REL_MRD             0\r
+//\r
+// Transmit parameters of decline message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_DEC_IRT             1\r
+#define DHCP6_DEC_MRC             5\r
+#define DHCP6_DEC_MRT             0\r
+#define DHCP6_DEC_MRD             0\r
+\r
+#define DHCP6_PACKET_ALL          0\r
+#define DHCP6_PACKET_STATEFUL     1\r
+#define DHCP6_PACKET_STATELESS    2\r
+\r
+#define DHCP6_BASE_PACKET_SIZE    1024\r
+\r
+#define DHCP6_PORT_CLIENT         546\r
+#define DHCP6_PORT_SERVER         547\r
+\r
+#define DHCP6_INSTANCE_FROM_THIS(Instance) CR ((Instance), DHCP6_INSTANCE, Dhcp6, DHCP6_INSTANCE_SIGNATURE)\r
+#define DHCP6_SERVICE_FROM_THIS(Service)   CR ((Service), DHCP6_SERVICE, ServiceBinding, DHCP6_SERVICE_SIGNATURE)\r
+\r
+extern EFI_IPv6_ADDRESS           mAllDhcpRelayAndServersAddress;\r
+extern EFI_IPv6_ADDRESS           mAllDhcpServersAddress;\r
+extern EFI_DHCP6_PROTOCOL         gDhcp6ProtocolTemplate;\r
+\r
+//\r
+// Enumeration of Dhcp6 message type, refers to section-5.3 of rfc-3315.\r
+//\r
+typedef enum {\r
+  Dhcp6MsgSolicit               = 1,\r
+  Dhcp6MsgAdvertise             = 2,\r
+  Dhcp6MsgRequest               = 3,\r
+  Dhcp6MsgConfirm               = 4,\r
+  Dhcp6MsgRenew                 = 5,\r
+  Dhcp6MsgRebind                = 6,\r
+  Dhcp6MsgReply                 = 7,\r
+  Dhcp6MsgRelease               = 8,\r
+  Dhcp6MsgDecline               = 9,\r
+  Dhcp6MsgReconfigure           = 10,\r
+  Dhcp6MsgInfoRequest           = 11\r
+} DHCP6_MSG_TYPE;\r
+\r
+//\r
+// Enumeration of option code in Dhcp6 packet, refers to section-24.3 of rfc-3315.\r
+//\r
+typedef enum {\r
+  Dhcp6OptClientId              = 1,\r
+  Dhcp6OptServerId              = 2,\r
+  Dhcp6OptIana                  = 3,\r
+  Dhcp6OptIata                  = 4,\r
+  Dhcp6OptIaAddr                = 5,\r
+  Dhcp6OptRequestOption         = 6,\r
+  Dhcp6OptPreference            = 7,\r
+  Dhcp6OptElapsedTime           = 8,\r
+  Dhcp6OptReplayMessage         = 9,\r
+  Dhcp6OptAuthentication        = 11,\r
+  Dhcp6OptServerUnicast         = 12,\r
+  Dhcp6OptStatusCode            = 13,\r
+  Dhcp6OptRapidCommit           = 14,\r
+  Dhcp6OptUserClass             = 15,\r
+  Dhcp6OptVendorClass           = 16,\r
+  Dhcp6OptVendorInfo            = 17,\r
+  Dhcp6OptInterfaceId           = 18,\r
+  Dhcp6OptReconfigMessage       = 19,\r
+  Dhcp6OptReconfigureAccept     = 20\r
+} DHCP6_OPT_CODE;\r
+\r
+//\r
+// Enumeration of status code recorded by IANA, refers to section-24.4 of rfc-3315.\r
+//\r
+typedef enum {\r
+  Dhcp6StsSuccess               = 0,\r
+  Dhcp6StsUnspecFail            = 1,\r
+  Dhcp6StsNoAddrsAvail          = 2,\r
+  Dhcp6StsNoBinding             = 3,\r
+  Dhcp6StsNotOnLink             = 4,\r
+  Dhcp6StsUseMulticast          = 5\r
+} DHCP6_STS_CODE;\r
+\r
+//\r
+// Enumeration of Duid type recorded by IANA, refers to section-24.5 of rfc-3315.\r
+//\r
+typedef enum {\r
+  Dhcp6DuidTypeLlt              = 1,\r
+  Dhcp6DuidTypeEn               = 2,\r
+  Dhcp6DuidTypeLl               = 3\r
+} DHCP6_DUID_TYPE;\r
+\r
+//\r
+// Control block for each IA.\r
+//\r
+struct _DHCP6_IA_CB {\r
+  EFI_DHCP6_IA                  *Ia;\r
+  UINT32                        T1;\r
+  UINT32                        T2;\r
+  UINT32                        AllExpireTime;\r
+  UINT32                        LeaseTime;\r
+};\r
+\r
+//\r
+// Control block for each transmitted message.\r
+//\r
+struct _DHCP6_TX_CB {\r
+  LIST_ENTRY                    Link;\r
+  UINT32                        Xid;\r
+  EFI_DHCP6_PACKET              *TxPacket;\r
+  EFI_DHCP6_RETRANSMISSION      RetryCtl;\r
+  UINT32                        RetryCnt;\r
+  UINT32                        RetryExp;\r
+  UINT32                        RetryLos;\r
+  UINT32                        TickTime;\r
+  UINT16                        *Elapsed;\r
+};\r
+\r
+//\r
+// Control block for each info-request message.\r
+//\r
+struct _DHCP6_INF_CB {\r
+  LIST_ENTRY                    Link;\r
+  UINT32                        Xid;\r
+  EFI_DHCP6_INFO_CALLBACK       ReplyCallback;\r
+  VOID                          *CallbackContext;\r
+  EFI_EVENT                     TimeoutEvent;\r
+};\r
+\r
+//\r
+// Control block for Dhcp6 instance, it's per configuration data.\r
+//\r
+struct _DHCP6_INSTANCE {\r
+  UINT32                        Signature;\r
+  EFI_HANDLE                    Handle;\r
+  DHCP6_SERVICE                 *Service;\r
+  LIST_ENTRY                    Link;\r
+  EFI_DHCP6_PROTOCOL            Dhcp6;\r
+  EFI_EVENT                     Timer;\r
+  EFI_DHCP6_CONFIG_DATA         *Config;\r
+  EFI_DHCP6_IA                  *CacheIa;\r
+  DHCP6_IA_CB                   IaCb;\r
+  LIST_ENTRY                    TxList;\r
+  LIST_ENTRY                    InfList;\r
+  EFI_DHCP6_PACKET              *AdSelect;\r
+  UINT8                         AdPref;\r
+  EFI_IPv6_ADDRESS              *Unicast;\r
+  EFI_STATUS                    UdpSts;\r
+  BOOLEAN                       InDestory;\r
+  BOOLEAN                       MediaPresent;\r
+  UINT64                        StartTime;\r
+};\r
+\r
+//\r
+// Control block for Dhcp6 service, it's per Nic handle.\r
+//\r
+struct _DHCP6_SERVICE {\r
+  UINT32                        Signature;\r
+  EFI_HANDLE                    Controller;\r
+  EFI_HANDLE                    Image;\r
+  EFI_SERVICE_BINDING_PROTOCOL  ServiceBinding;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL   *Snp;\r
+  EFI_DHCP6_DUID                *ClientId;\r
+  UDP_IO                        *UdpIo;\r
+  UINT32                        Xid;\r
+  LIST_ENTRY                    Child;\r
+  UINTN                         NumOfChild;\r
+  BOOLEAN                       InDestory;\r
+};\r
+\r
+/**\r
+  Starts the DHCPv6 standard S.A.R.R. process.\r
+\r
+  The Start() function starts the DHCPv6 standard process. This function can\r
+  be called only when the state of Dhcp6 instance is in the Dhcp6Init state.\r
+  If the DHCP process completes successfully, the state of the Dhcp6 instance\r
+  will be transferred through Dhcp6Selecting and Dhcp6Requesting to the\r
+  Dhcp6Bound state.\r
+  Refer to rfc-3315 for precise state transitions during this process. At the\r
+  time when each event occurs in this process, the callback function that was set\r
+  by EFI_DHCP6_PROTOCOL.Configure() will be called and the user can take this\r
+  opportunity to control the process.\r
+\r
+  @param[in]  This              The pointer to Dhcp6 protocol.\r
+\r
+  @retval EFI_SUCCESS           The DHCPv6 standard process has started, or it\r
+                                completed when CompletionEvent was NULL.\r
+  @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Child instance hasn't been configured.\r
+  @retval EFI_INVALID_PARAMETER This is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_TIMEOUT           The DHCPv6 configuration process failed because no\r
+                                response was received from the server within the\r
+                                specified timeout value.\r
+  @retval EFI_ABORTED           The user aborted the DHCPv6 process.\r
+  @retval EFI_ALREADY_STARTED   Some other Dhcp6 instance already started the DHCPv6\r
+                                standard process.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Start (\r
+  IN EFI_DHCP6_PROTOCOL        *This\r
+  );\r
+\r
+/**\r
+  Stops the DHCPv6 standard S.A.R.R. process.\r
+\r
+  The Stop() function is used to stop the DHCPv6 standard process. After this\r
+  function is called successfully, the state of Dhcp6 instance is transferred\r
+  into the Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called\r
+  before DHCPv6 standard process can be started again. This function can be\r
+  called when the Dhcp6 instance is in any state.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+\r
+  @retval EFI_SUCCESS           The Dhcp6 instance is now in the Dhcp6Init state.\r
+  @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Stop (\r
+  IN EFI_DHCP6_PROTOCOL        *This\r
+  );\r
+\r
+/**\r
+  Returns the current operating mode data for the Dhcp6 instance.\r
+\r
+  The GetModeData() function returns the current operating mode and\r
+  cached data packet for the Dhcp6 instance.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+  @param[out] Dhcp6ModeData     The pointer to the Dhcp6 mode data.\r
+  @param[out] Dhcp6ConfigData   The pointer to the Dhcp6 configure data.\r
+\r
+  @retval EFI_SUCCESS           The mode data was returned.\r
+  @retval EFI_INVALID_PARAMETER This is NULL.\r
+  @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Protocol instance has not\r
+                                been configured when Dhcp6ConfigData is\r
+                                not NULL.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6GetModeData (\r
+  IN  EFI_DHCP6_PROTOCOL       *This,\r
+  OUT EFI_DHCP6_MODE_DATA      *Dhcp6ModeData      OPTIONAL,\r
+  OUT EFI_DHCP6_CONFIG_DATA    *Dhcp6ConfigData    OPTIONAL\r
+  );\r
+\r
+/**\r
+  Initializes, changes, or resets the operational settings for the Dhcp6 instance.\r
+\r
+  The Configure() function is used to initialize or clean up the configuration\r
+  data of the Dhcp6 instance:\r
+  - When Dhcp6CfgData is not NULL and Configure() is called successfully, the\r
+    configuration data will be initialized in the Dhcp6 instance and the state\r
+    of the configured IA will be transferred into Dhcp6Init.\r
+  - When Dhcp6CfgData is NULL and Configure() is called successfully, the\r
+    configuration data will be cleaned up and no IA will be associated with\r
+    the Dhcp6 instance.\r
+  To update the configuration data for an Dhcp6 instance, the original data\r
+  must be cleaned up before setting the new configuration data.\r
+\r
+  @param[in]  This                   The pointer to the Dhcp6 protocol\r
+  @param[in]  Dhcp6CfgData           The pointer to the EFI_DHCP6_CONFIG_DATA.\r
+\r
+  @retval EFI_SUCCESS           The Dhcp6 is configured successfully with the\r
+                                Dhcp6Init state, or cleaned up the original\r
+                                configuration setting.\r
+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance has been already configured\r
+                                when Dhcp6CfgData is not NULL.\r
+                                The Dhcp6 instance has already started the\r
+                                DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.\r
+  @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Configure (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN EFI_DHCP6_CONFIG_DATA     *Dhcp6CfgData    OPTIONAL\r
+  );\r
+\r
+/**\r
+  Request configuration information without the assignment of any\r
+  Ia addresses of the client.\r
+\r
+  The InfoRequest() function is used to request configuration information\r
+  without the assignment of any IPv6 address of the client. Client sends\r
+  out Information Request packet to obtain the required configuration\r
+  information, and DHCPv6 server responds with Reply packet containing\r
+  the information for the client. The received Reply packet will be passed\r
+  to the user by ReplyCallback function. If user returns EFI_NOT_READY from\r
+  ReplyCallback, the Dhcp6 instance will continue to receive other Reply\r
+  packets unless timeout according to the Retransmission parameter.\r
+  Otherwise, the Information Request exchange process will be finished\r
+  successfully if user returns EFI_SUCCESS from ReplyCallback.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+  @param[in]  SendClientId      If TRUE, the DHCPv6 protocol instance will build Client\r
+                                Identifier option and include it into Information Request\r
+                                packet. Otherwise, Client Identifier option will not be included.\r
+  @param[in]  OptionRequest     The pointer to the buffer of option request options.\r
+  @param[in]  OptionCount       The option number in the OptionList.\r
+  @param[in]  OptionList        The list of appended options.\r
+  @param[in]  Retransmission    The pointer to the retransmission of the message.\r
+  @param[in]  TimeoutEvent      The event of timeout.\r
+  @param[in]  ReplyCallback     The callback function when a reply was received.\r
+  @param[in]  CallbackContext   The pointer to the parameter passed to the callback.\r
+\r
+  @retval EFI_SUCCESS           The DHCPv6 information request exchange process\r
+                                completed when TimeoutEvent is NULL. Information\r
+                                Request packet has been sent to DHCPv6 server when\r
+                                TimeoutEvent is not NULL.\r
+  @retval EFI_NO_RESPONSE       The DHCPv6 information request exchange process failed\r
+                                because of no response, or not all requested-options\r
+                                are responded to by DHCPv6 servers when Timeout happened.\r
+  @retval EFI_ABORTED           The DHCPv6 information request exchange process was aborted\r
+                                by the user.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6InfoRequest (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN BOOLEAN                   SendClientId,\r
+  IN EFI_DHCP6_PACKET_OPTION   *OptionRequest,\r
+  IN UINT32                    OptionCount,\r
+  IN EFI_DHCP6_PACKET_OPTION   *OptionList[]    OPTIONAL,\r
+  IN EFI_DHCP6_RETRANSMISSION  *Retransmission,\r
+  IN EFI_EVENT                 TimeoutEvent     OPTIONAL,\r
+  IN EFI_DHCP6_INFO_CALLBACK   ReplyCallback,\r
+  IN VOID                      *CallbackContext OPTIONAL\r
+  );\r
+\r
+/**\r
+  Manually extend the valid and preferred lifetimes for the IPv6 addresses\r
+  of the configured IA and update other configuration parameters by sending\r
+  Renew or Rebind packet.\r
+\r
+  The RenewRebind() function is used to manually extend the valid and preferred\r
+  lifetimes for the IPv6 addresses of the configured IA and update other\r
+  configuration parameters by sending a Renew or Rebind packet.\r
+  - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,\r
+    it will send Renew packet to the previously DHCPv6 server and transfer the\r
+    state of the configured IA to Dhcp6Renewing. If valid Reply packet received,\r
+    the state transfers to Dhcp6Bound and the valid and preferred timer restarts.\r
+    If fails, the state transfers to Dhcp6Bound but the timer continues.\r
+  - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,\r
+    it will send a Rebind packet. If a valid Reply packet is received, the state transfers\r
+    to Dhcp6Bound, and the valid and preferred timer restarts. If it fails, the state\r
+    transfers to Dhcp6Init, and the IA can't be used.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+  @param[in]  RebindRequest     If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.\r
+                                Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.\r
+\r
+  @retval EFI_SUCCESS           The DHCPv6 renew/rebind exchange process\r
+                                completed and at least one IPv6 address of the\r
+                                configured IA was bound again when\r
+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.\r
+                                The EFI DHCPv6 Protocol instance has sent Renew\r
+                                or Rebind packet when\r
+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.\r
+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the\r
+                                state of the configured IA is not in Dhcp6Bound.\r
+  @retval EFI_ALREADY_STARTED   The state of the configured IA has already entered\r
+                                Dhcp6Renewing when RebindRequest is FALSE.\r
+                                The state of the configured IA has already entered\r
+                                Dhcp6Rebinding when RebindRequest is TRUE.\r
+  @retval EFI_ABORTED           The DHCPv6 renew/rebind exchange process aborted\r
+                                by user.\r
+  @retval EFI_NO_RESPONSE       The DHCPv6 renew/rebind exchange process failed\r
+                                because of no response.\r
+  @retval EFI_NO_MAPPING        No IPv6 address has been bound to the configured\r
+                                IA after the DHCPv6 renew/rebind exchange process.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6RenewRebind (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN BOOLEAN                   RebindRequest\r
+  );\r
+\r
+/**\r
+  Inform that one or more addresses assigned by a server are already\r
+  in use by another node.\r
+\r
+  The Decline() function is used to manually decline the assignment of\r
+  IPv6 addresses, which have been already used by another node. If all\r
+  IPv6 addresses of the configured IA are declined through this function,\r
+  the state of the IA will switch through Dhcp6Declining to Dhcp6Init.\r
+  Otherwise, the state of the IA will restore to Dhcp6Bound after the\r
+  declining process. The Decline() can only be called when the IA is in\r
+  Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,\r
+  this function is a blocking operation. It will return after the\r
+  declining process finishes, or aborted by user.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+  @param[in]  AddressCount      The number of declining addresses.\r
+  @param[in]  Addresses         The pointer to the buffer stored the declining\r
+                                addresses.\r
+\r
+  @retval EFI_SUCCESS           The DHCPv6 decline exchange process completed\r
+                                when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.\r
+                                The Dhcp6 instance has sent Decline packet when\r
+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.\r
+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the\r
+                                state of the configured IA is not in Dhcp6Bound.\r
+  @retval EFI_ABORTED           The DHCPv6 decline exchange process was aborted by the user.\r
+  @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with\r
+                                the configured IA for this instance.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Decline (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN UINT32                    AddressCount,\r
+  IN EFI_IPv6_ADDRESS          *Addresses\r
+  );\r
+\r
+/**\r
+  Release one or more addresses associated with the configured Ia\r
+  for the current instance.\r
+\r
+  The Release() function is used to manually release the one or more\r
+  IPv6 address. If AddressCount is zero, it will release all IPv6\r
+  addresses of the configured IA. If all IPv6 addresses of the IA are\r
+  released through this function, the state of the IA will switch\r
+  through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the\r
+  IA will restore to Dhcp6Bound after the releasing process.\r
+  The Release() can only be called when the IA is in a Dhcp6Bound state.\r
+  If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is\r
+  a blocking operation. It will return after the releasing process\r
+  finishes, or aborted by user.\r
+\r
+  @param[in]  This              The pointer to the Dhcp6 protocol.\r
+  @param[in]  AddressCount      The number of releasing addresses.\r
+  @param[in]  Addresses         The pointer to the buffer stored the releasing\r
+                                addresses.\r
+  @retval EFI_SUCCESS           The DHCPv6 release exchange process has\r
+                                completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent\r
+                                is NULL. The Dhcp6 instance has sent Release\r
+                                packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent\r
+                                is not NULL.\r
+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the\r
+                                state of the configured IA is not in Dhcp6Bound.\r
+  @retval EFI_ABORTED           The DHCPv6 release exchange process was aborted by the user.\r
+  @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with\r
+                                the configured IA for this instance.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Release (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN UINT32                    AddressCount,\r
+  IN EFI_IPv6_ADDRESS          *Addresses\r
+  );\r
+\r
+/**\r
+  Parse the option data in the Dhcp6 packet.\r
+\r
+  The Parse() function is used to retrieve the option list in the DHCPv6 packet.\r
+\r
+  @param[in]      This              The pointer to the Dhcp6 protocol.\r
+  @param[in]      Packet            The pointer to the Dhcp6 packet.\r
+  @param[in, out] OptionCount       The number of option in the packet.\r
+  @param[out]     PacketOptionList  The array of pointers to the each option in the packet.\r
+\r
+  @retval EFI_SUCCESS           The packet was successfully parsed.\r
+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+  @retval EFI_BUFFER_TOO_SMALL  *OptionCount is smaller than the number of options\r
+                                that were found in the Packet.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Parse (\r
+  IN EFI_DHCP6_PROTOCOL        *This,\r
+  IN EFI_DHCP6_PACKET          *Packet,\r
+  IN OUT UINT32                *OptionCount,\r
+  OUT EFI_DHCP6_PACKET_OPTION  *PacketOptionList[]  OPTIONAL\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c
new file mode 100644 (file)
index 0000000..761f9c2
--- /dev/null
@@ -0,0 +1,2965 @@
+/** @file\r
+  Dhcp6 internal functions implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+\r
+/**\r
+  Enqueue the packet into the retry list in case of timeout.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  Packet          The pointer to the Dhcp6 packet to retry.\r
+  @param[in]  Elapsed         The pointer to the elapsed time value in the packet.\r
+  @param[in]  RetryCtl        The pointer to the transmission control of the packet.\r
+                              This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS           Successfully enqueued the packet into the retry list according\r
+                                to its message type.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected message type.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6EnqueueRetry (\r
+  IN DHCP6_INSTANCE            *Instance,\r
+  IN EFI_DHCP6_PACKET          *Packet,\r
+  IN UINT16                    *Elapsed,\r
+  IN EFI_DHCP6_RETRANSMISSION  *RetryCtl     OPTIONAL\r
+  )\r
+{\r
+  DHCP6_TX_CB                  *TxCb;\r
+  DHCP6_IA_CB                  *IaCb;\r
+\r
+  ASSERT (Packet != NULL);\r
+\r
+  IaCb = &Instance->IaCb;\r
+  TxCb = AllocateZeroPool (sizeof (DHCP6_TX_CB));\r
+\r
+  if (TxCb == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Save tx packet pointer, and it will be destoryed when reply received.\r
+  //\r
+  TxCb->TxPacket = Packet;\r
+  TxCb->Xid      = Packet->Dhcp6.Header.TransactionId;\r
+\r
+  //\r
+  // Save pointer to elapsed-time value so we can update it on retransmits.\r
+  //\r
+  TxCb->Elapsed  = Elapsed;\r
+\r
+  //\r
+  // Calculate the retransmission according to the the message type.\r
+  //\r
+  switch (Packet->Dhcp6.Header.MessageType) {\r
+  case Dhcp6MsgSolicit:\r
+    //\r
+    // Calculate the retransmission threshold value for solicit packet.\r
+    // Use the default value by rfc-3315 if user doesn't configure.\r
+    //\r
+    if (RetryCtl == NULL) {\r
+      TxCb->RetryCtl.Irt = DHCP6_SOL_IRT;\r
+      TxCb->RetryCtl.Mrc = DHCP6_SOL_MRC;\r
+      TxCb->RetryCtl.Mrt = DHCP6_SOL_MRT;\r
+      TxCb->RetryCtl.Mrd = DHCP6_SOL_MRD;\r
+    } else {\r
+      TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_SOL_IRT;\r
+      TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_SOL_MRC;\r
+      TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_SOL_MRT;\r
+      TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_SOL_MRD;\r
+    }\r
+\r
+    TxCb->RetryExp       = Dhcp6CalculateExpireTime (\r
+                             TxCb->RetryCtl.Irt,\r
+                             TRUE,\r
+                             FALSE\r
+                             );\r
+    break;\r
+\r
+  case Dhcp6MsgRequest:\r
+    //\r
+    // Calculate the retransmission threshold value for request packet.\r
+    //\r
+    TxCb->RetryCtl.Irt = DHCP6_REQ_IRT;\r
+    TxCb->RetryCtl.Mrc = DHCP6_REQ_MRC;\r
+    TxCb->RetryCtl.Mrt = DHCP6_REQ_MRT;\r
+    TxCb->RetryCtl.Mrd = DHCP6_REQ_MRD;\r
+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (\r
+                           TxCb->RetryCtl.Irt,\r
+                           TRUE,\r
+                           TRUE\r
+                           );\r
+    break;\r
+\r
+  case Dhcp6MsgConfirm:\r
+    //\r
+    // Calculate the retransmission threshold value for confirm packet.\r
+    //\r
+    TxCb->RetryCtl.Irt = DHCP6_CNF_IRT;\r
+    TxCb->RetryCtl.Mrc = DHCP6_CNF_MRC;\r
+    TxCb->RetryCtl.Mrt = DHCP6_CNF_MRT;\r
+    TxCb->RetryCtl.Mrd = DHCP6_CNF_MRD;\r
+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (\r
+                           TxCb->RetryCtl.Irt,\r
+                           TRUE,\r
+                           TRUE\r
+                           );\r
+    break;\r
+\r
+  case Dhcp6MsgRenew:\r
+    //\r
+    // Calculate the retransmission threshold value for renew packet.\r
+    //\r
+    TxCb->RetryCtl.Irt = DHCP6_REB_IRT;\r
+    TxCb->RetryCtl.Mrc = DHCP6_REB_MRC;\r
+    TxCb->RetryCtl.Mrt = DHCP6_REB_MRT;\r
+    TxCb->RetryCtl.Mrd = IaCb->T2 - IaCb->T1;\r
+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (\r
+                           TxCb->RetryCtl.Irt,\r
+                           TRUE,\r
+                           TRUE\r
+                           );\r
+    break;\r
+\r
+  case Dhcp6MsgRebind:\r
+    //\r
+    // Calculate the retransmission threshold value for rebind packet.\r
+    //\r
+    TxCb->RetryCtl.Irt = DHCP6_REN_IRT;\r
+    TxCb->RetryCtl.Mrc = DHCP6_REN_MRC;\r
+    TxCb->RetryCtl.Mrt = DHCP6_REN_MRT;\r
+    TxCb->RetryCtl.Mrd = IaCb->AllExpireTime - IaCb->T2;\r
+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (\r
+                           TxCb->RetryCtl.Irt,\r
+                           TRUE,\r
+                           TRUE\r
+                           );\r
+    break;\r
+\r
+  case Dhcp6MsgDecline:\r
+    //\r
+    // Calculate the retransmission threshold value for decline packet.\r
+    //\r
+    TxCb->RetryCtl.Irt = DHCP6_DEC_IRT;\r
+    TxCb->RetryCtl.Mrc = DHCP6_DEC_MRC;\r
+    TxCb->RetryCtl.Mrt = DHCP6_DEC_MRT;\r
+    TxCb->RetryCtl.Mrd = DHCP6_DEC_MRD;\r
+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (\r
+                           TxCb->RetryCtl.Irt,\r
+                           TRUE,\r
+                           TRUE\r
+                           );\r
+    break;\r
+\r
+  case Dhcp6MsgRelease:\r
+    //\r
+    // Calculate the retransmission threshold value for release packet.\r
+    //\r
+    TxCb->RetryCtl.Irt = DHCP6_REL_IRT;\r
+    TxCb->RetryCtl.Mrc = DHCP6_REL_MRC;\r
+    TxCb->RetryCtl.Mrt = DHCP6_REL_MRT;\r
+    TxCb->RetryCtl.Mrd = DHCP6_REL_MRD;\r
+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (\r
+                           TxCb->RetryCtl.Irt,\r
+                           TRUE,\r
+                           TRUE\r
+                           );\r
+    break;\r
+\r
+  case Dhcp6MsgInfoRequest:\r
+    //\r
+    // Calculate the retransmission threshold value for info-request packet.\r
+    // Use the default value by rfc-3315 if user doesn't configure.\r
+    //\r
+    if (RetryCtl == NULL) {\r
+      TxCb->RetryCtl.Irt = DHCP6_INF_IRT;\r
+      TxCb->RetryCtl.Mrc = DHCP6_INF_MRC;\r
+      TxCb->RetryCtl.Mrt = DHCP6_INF_MRT;\r
+      TxCb->RetryCtl.Mrd = DHCP6_INF_MRD;\r
+    } else {\r
+      TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_INF_IRT;\r
+      TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_INF_MRC;\r
+      TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_INF_MRT;\r
+      TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_INF_MRD;\r
+    }\r
+\r
+    TxCb->RetryExp       = Dhcp6CalculateExpireTime (\r
+                             TxCb->RetryCtl.Irt,\r
+                             TRUE,\r
+                             TRUE\r
+                             );\r
+    break;\r
+\r
+  default:\r
+    //\r
+    // Unexpected message type.\r
+    //\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Insert into the retransmit list of the instance.\r
+  //\r
+  InsertTailList (&Instance->TxList, &TxCb->Link);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Dequeue the packet from retry list if reply received or timeout at last.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  PacketXid       The packet transaction id to match.\r
+  @param[in]  NeedSignal      If TRUE, then an timeout event need be signaled when it is existed.\r
+                              Otherwise, this parameter is ignored.\r
+\r
+  @retval EFI_SUCCESS         Successfully dequeued the packet into retry list .\r
+  @retval EFI_NOT_FOUND       There is no xid matched in retry list.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6DequeueRetry (\r
+  IN DHCP6_INSTANCE         *Instance,\r
+  IN UINT32                 PacketXid,\r
+  IN BOOLEAN                NeedSignal\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *NextEntry;\r
+  DHCP6_TX_CB               *TxCb;\r
+  DHCP6_INF_CB              *InfCb;\r
+\r
+  //\r
+  // Seek the retransmit node in the retransmit list by packet xid.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {\r
+\r
+    TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);\r
+    ASSERT(TxCb->TxPacket);\r
+\r
+    if (TxCb->Xid == PacketXid) {\r
+\r
+      if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {\r
+\r
+        //\r
+        // Seek the info-request node in the info-request list by packet xid.\r
+        //\r
+        NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {\r
+\r
+          InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);\r
+\r
+          if (InfCb->Xid == PacketXid) {\r
+            //\r
+            // Remove the info-request node, and signal the event if timeout.\r
+            //\r
+            if (InfCb->TimeoutEvent != NULL && NeedSignal) {\r
+              gBS->SignalEvent (InfCb->TimeoutEvent);\r
+            }\r
+\r
+            RemoveEntryList (&InfCb->Link);\r
+            FreePool (InfCb);\r
+          }\r
+        }\r
+      }\r
+      //\r
+      // Remove the retransmit node.\r
+      //\r
+      RemoveEntryList (&TxCb->Link);\r
+      ASSERT(TxCb->TxPacket);\r
+      FreePool (TxCb->TxPacket);\r
+      FreePool (TxCb);\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+  Clean up the specific nodes in the retry list.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  Scope           The scope of cleanup nodes.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupRetry (\r
+  IN DHCP6_INSTANCE         *Instance,\r
+  IN UINT32                 Scope\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *NextEntry;\r
+  DHCP6_TX_CB               *TxCb;\r
+  DHCP6_INF_CB              *InfCb;\r
+\r
+  //\r
+  // Clean up all the stateful messages from the retransmit list.\r
+  //\r
+  if (Scope == DHCP6_PACKET_STATEFUL || Scope == DHCP6_PACKET_ALL) {\r
+\r
+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {\r
+\r
+      TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);\r
+      ASSERT(TxCb->TxPacket);\r
+\r
+      if (TxCb->TxPacket->Dhcp6.Header.MessageType != Dhcp6MsgInfoRequest) {\r
+        RemoveEntryList (&TxCb->Link);\r
+        FreePool (TxCb->TxPacket);\r
+        FreePool (TxCb);\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Clean up all the stateless messages from the retransmit list.\r
+  //\r
+  if (Scope == DHCP6_PACKET_STATELESS || Scope == DHCP6_PACKET_ALL) {\r
+\r
+    //\r
+    // Clean up all the retransmit list for stateless messages.\r
+    //\r
+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {\r
+\r
+      TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);\r
+      ASSERT(TxCb->TxPacket);\r
+\r
+      if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {\r
+        RemoveEntryList (&TxCb->Link);\r
+        FreePool (TxCb->TxPacket);\r
+        FreePool (TxCb);\r
+      }\r
+    }\r
+\r
+    //\r
+    // Clean up all the info-request messages list.\r
+    //\r
+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {\r
+\r
+      InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);\r
+\r
+      if (InfCb->TimeoutEvent != NULL) {\r
+        gBS->SignalEvent (InfCb->TimeoutEvent);\r
+      }\r
+      RemoveEntryList (&InfCb->Link);\r
+      FreePool (InfCb);\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Clean up the session of the instance stateful exchange.\r
+\r
+  @param[in, out]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]       Status          The return status from udp.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupSession (\r
+  IN OUT DHCP6_INSTANCE          *Instance,\r
+  IN     EFI_STATUS              Status\r
+  )\r
+{\r
+  UINTN                          Index;\r
+  EFI_DHCP6_IA                   *Ia;\r
+\r
+  ASSERT(Instance->Config);\r
+  ASSERT(Instance->IaCb.Ia);\r
+\r
+  //\r
+  // Clean up the retransmit list for stateful messages.\r
+  //\r
+  Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATEFUL);\r
+\r
+  if (Instance->Unicast != NULL) {\r
+    FreePool (Instance->Unicast);\r
+  }\r
+\r
+  if (Instance->AdSelect != NULL) {\r
+    FreePool (Instance->AdSelect);\r
+  }\r
+\r
+  if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
+    FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+  }\r
+\r
+  //\r
+  // Reinitialize the Ia fields of the instance.\r
+  //\r
+  Instance->UdpSts                  = Status;\r
+  Instance->AdSelect                = NULL;\r
+  Instance->AdPref                  = 0;\r
+  Instance->Unicast                 = NULL;\r
+  Instance->IaCb.T1                 = 0;\r
+  Instance->IaCb.T2                 = 0;\r
+  Instance->IaCb.AllExpireTime      = 0;\r
+  Instance->IaCb.LeaseTime          = 0;\r
+\r
+  //\r
+  // Clear start time\r
+  //\r
+  Instance->StartTime               = 0;\r
+\r
+  Ia                                = Instance->IaCb.Ia;\r
+  Ia->State                         = Dhcp6Init;\r
+  Ia->ReplyPacket                   = NULL;\r
+\r
+  //\r
+  // Set the addresses as zero lifetime, and then the notify\r
+  // function in Ip6Config will remove these timeout address.\r
+  //\r
+  for (Index = 0; Index < Ia->IaAddressCount; Index++) {\r
+    Ia->IaAddress[Index].PreferredLifetime = 0;\r
+    Ia->IaAddress[Index].ValidLifetime     = 0;\r
+  }\r
+\r
+  //\r
+  //\r
+  // Signal the Ia information updated event to informal user.\r
+  //\r
+  if (Instance->Config->IaInfoEvent != NULL) {\r
+    gBS->SignalEvent (Instance->Config->IaInfoEvent);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Callback to user when Dhcp6 transmit/receive occurs.\r
+\r
+  @param[in]      Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]      Event           The current Dhcp6 event.\r
+  @param[in, out] Packet          The pointer to the packet sending or received.\r
+\r
+  @retval EFI_SUCCESS           The user function returns success.\r
+  @retval EFI_NOT_READY         Direct the caller to continue collecting the offer.\r
+  @retval EFI_ABORTED           The user function ask it to abort.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6CallbackUser (\r
+  IN     DHCP6_INSTANCE       *Instance,\r
+  IN     EFI_DHCP6_EVENT      Event,\r
+  IN OUT EFI_DHCP6_PACKET     **Packet\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_DHCP6_PACKET            *NewPacket;\r
+  EFI_DHCP6_CALLBACK          Callback;\r
+  VOID                        *Context;\r
+\r
+  ASSERT (Packet != NULL);\r
+  ASSERT (Instance->Config != NULL);\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+  NewPacket = NULL;\r
+  Status    = EFI_SUCCESS;\r
+  Callback  = Instance->Config->Dhcp6Callback;\r
+  Context   = Instance->Config->CallbackContext;\r
+\r
+  //\r
+  // Callback to user with the new message if has.\r
+  //\r
+  if (Callback != NULL) {\r
+\r
+    Status = Callback (\r
+               &Instance->Dhcp6,\r
+               Context,\r
+               Instance->IaCb.Ia->State,\r
+               Event,\r
+               *Packet,\r
+               &NewPacket\r
+               );\r
+    //\r
+    // Updated the new packet from user to replace the original one.\r
+    //\r
+    if (NewPacket != NULL) {\r
+      ASSERT (*Packet != NULL);\r
+      FreePool (*Packet);\r
+      *Packet = NewPacket;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Update Ia according to the new reply message.\r
+\r
+  @param[in, out]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]       Packet          The pointer to reply messages.\r
+\r
+  @retval EFI_SUCCESS         Updated the Ia information successfully.\r
+  @retval EFI_DEVICE_ERROR    An unexpected error.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6UpdateIaInfo (\r
+  IN OUT DHCP6_INSTANCE           *Instance,\r
+  IN     EFI_DHCP6_PACKET         *Packet\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_DHCP6_STATE             State;\r
+  UINT8                       *Option;\r
+  UINT8                       *IaInnerOpt;\r
+  UINT16                      IaInnerLen;\r
+  UINT16                      StsCode;\r
+  UINT32                      T1;\r
+  UINT32                      T2;\r
+\r
+  ASSERT (Instance->Config != NULL);\r
+  //\r
+  // If the reply was received in reponse to a solicit with rapid commit option,\r
+  // request, renew or rebind message, the client updates the information it has\r
+  // recorded about IAs from the IA options contained in the reply message:\r
+  //   1. record the T1 and T2 times\r
+  //   2. add any new addresses in the IA\r
+  //   3. discard any addresses from the IA, that have a valid lifetime of 0\r
+  //   4. update lifetimes for any addresses that alread recorded\r
+  //   5. leave unchanged any information about addresses\r
+  //\r
+  // See details in the section-18.1.8 of rfc-3315.\r
+  //\r
+  State  = Dhcp6Init;\r
+  Option = Dhcp6SeekIaOption (\r
+             Packet->Dhcp6.Option,\r
+             Packet->Length - sizeof (EFI_DHCP6_HEADER),\r
+             &Instance->Config->IaDescriptor\r
+             );\r
+  if (Option == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // The format of the IA_NA option is:\r
+  //\r
+  //     0                   1                   2                   3\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |          OPTION_IA_NA         |          option-len           |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                        IAID (4 octets)                        |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                              T1                               |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                              T2                               |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                                                               |\r
+  //    .                         IA_NA-options                         .\r
+  //    .                                                               .\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+  // The format of the IA_TA option is:\r
+  //\r
+  //     0                   1                   2                   3\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |         OPTION_IA_TA          |          option-len           |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                        IAID (4 octets)                        |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                                                               |\r
+  //    .                         IA_TA-options                         .\r
+  //    .                                                               .\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  //\r
+  // sizeof (option-code + option-len + IaId)           = 8\r
+  // sizeof (option-code + option-len + IaId + T1)      = 12\r
+  // sizeof (option-code + option-len + IaId + T1 + T2) = 16\r
+  //\r
+  // The inner options still start with 2 bytes option-code and 2 bytes option-len.\r
+  //\r
+  if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {\r
+    T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8)));\r
+    T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12)));\r
+    IaInnerOpt = Option + 16;\r
+    IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12);\r
+  } else {\r
+    T1 = 0;\r
+    T2 = 0;\r
+    IaInnerOpt = Option + 8;\r
+    IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 4);\r
+  }\r
+\r
+  //\r
+  // The format of the Status Code option is:\r
+  //\r
+  //   0                   1                   2                   3\r
+  //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //  |       OPTION_STATUS_CODE      |         option-len            |\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //  |          status-code          |                               |\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |\r
+  //  .                                                               .\r
+  //  .                        status-message                         .\r
+  //  .                                                               .\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  //\r
+  // sizeof (option-code + option-len) = 4\r
+  //\r
+  StsCode = Dhcp6StsSuccess;\r
+  Option  = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);\r
+\r
+  if (Option != NULL) {\r
+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));\r
+    if (StsCode != Dhcp6StsSuccess) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Generate control block for the Ia.\r
+  //\r
+  Status = Dhcp6GenerateIaCb (\r
+             Instance,\r
+             IaInnerOpt,\r
+             IaInnerLen,\r
+             T1,\r
+             T2\r
+             );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+\r
+/**\r
+  Seek StatusCode Option in package. A Status Code option may appear in the\r
+  options field of a DHCP message and/or in the options field of another option.\r
+  See details in section 22.13, RFC3315.\r
+\r
+  @param[in]       Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]       Packet          The pointer to reply messages.\r
+  @param[out]      Option          The pointer to status code option.\r
+\r
+  @retval EFI_SUCCESS              Seek status code option successfully.\r
+  @retval EFI_DEVICE_ERROR         An unexpected error.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SeekStsOption (\r
+  IN     DHCP6_INSTANCE           *Instance,\r
+  IN     EFI_DHCP6_PACKET         *Packet,\r
+  OUT    UINT8                    **Option\r
+  )\r
+{\r
+  UINT8                       *IaInnerOpt;\r
+  UINT16                      IaInnerLen;\r
+  UINT16                      StsCode;\r
+\r
+  //\r
+  // Seek StatusCode option directly in DHCP message body. That is, search in\r
+  // non-encapsulated option fields.\r
+  //\r
+  *Option = Dhcp6SeekOption (\r
+              Packet->Dhcp6.Option,\r
+              Packet->Length - 4,\r
+              Dhcp6OptStatusCode\r
+              );\r
+\r
+  if (*Option != NULL) {\r
+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));\r
+    if (StsCode != Dhcp6StsSuccess) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Seek in encapsulated options, IA_NA and IA_TA.\r
+  //\r
+  *Option = Dhcp6SeekIaOption (\r
+              Packet->Dhcp6.Option,\r
+              Packet->Length - sizeof (EFI_DHCP6_HEADER),\r
+              &Instance->Config->IaDescriptor\r
+              );\r
+  if (*Option == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // The format of the IA_NA option is:\r
+  //\r
+  //     0                   1                   2                   3\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |          OPTION_IA_NA         |          option-len           |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                        IAID (4 octets)                        |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                              T1                               |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                              T2                               |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                                                               |\r
+  //    .                         IA_NA-options                         .\r
+  //    .                                                               .\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+  // The format of the IA_TA option is:\r
+  //\r
+  //     0                   1                   2                   3\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |         OPTION_IA_TA          |          option-len           |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                        IAID (4 octets)                        |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                                                               |\r
+  //    .                         IA_TA-options                         .\r
+  //    .                                                               .\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  //\r
+  // sizeof (option-code + option-len + IaId)           = 8\r
+  // sizeof (option-code + option-len + IaId + T1)      = 12\r
+  // sizeof (option-code + option-len + IaId + T1 + T2) = 16\r
+  //\r
+  // The inner options still start with 2 bytes option-code and 2 bytes option-len.\r
+  //\r
+  if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {\r
+    IaInnerOpt = *Option + 16;\r
+    IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 12);\r
+  } else {\r
+    IaInnerOpt = *Option + 8;\r
+    IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 4);\r
+  }\r
+\r
+  //\r
+  // The format of the Status Code option is:\r
+  //\r
+  //   0                   1                   2                   3\r
+  //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //  |       OPTION_STATUS_CODE      |         option-len            |\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //  |          status-code          |                               |\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |\r
+  //  .                                                               .\r
+  //  .                        status-message                         .\r
+  //  .                                                               .\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  //\r
+  // sizeof (option-code + option-len) = 4\r
+  //\r
+  *Option  = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);\r
+  if (*Option != NULL) {\r
+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));\r
+    if (StsCode != Dhcp6StsSuccess) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Transmit Dhcp6 message by udpio.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  Packet          The pointer to transmit message.\r
+  @param[in]  Elapsed         The pointer to the elapsed time value to fill in.\r
+\r
+  @retval EFI_SUCCESS           Successfully transmitted the packet.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval Others                Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6TransmitPacket (\r
+  IN DHCP6_INSTANCE         *Instance,\r
+  IN EFI_DHCP6_PACKET       *Packet,\r
+  IN UINT16                 *Elapsed\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  NET_BUF                   *Wrap;\r
+  NET_FRAGMENT              Frag;\r
+  UDP_END_POINT             EndPt;\r
+  DHCP6_SERVICE             *Service;\r
+\r
+  Service = Instance->Service;\r
+\r
+  //\r
+  // Wrap it into a netbuf then send it.\r
+  //\r
+  Frag.Bulk = (UINT8 *) &Packet->Dhcp6.Header;\r
+  Frag.Len  = Packet->Length;\r
+\r
+  //\r
+  // Do not register free packet here, which will be handled in retry list.\r
+  //\r
+  Wrap = NetbufFromExt (&Frag, 1, 0, 0, Dhcp6DummyExtFree, NULL);\r
+\r
+  if (Wrap == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Multicast the Dhcp6 message, unless get the unicast server address by option.\r
+  //\r
+  ZeroMem (&EndPt, sizeof (UDP_END_POINT));\r
+\r
+  if (Instance->Unicast != NULL) {\r
+    CopyMem (\r
+      &EndPt.RemoteAddr,\r
+      Instance->Unicast,\r
+      sizeof (EFI_IPv6_ADDRESS)\r
+      );\r
+  } else {\r
+    CopyMem (\r
+      &EndPt.RemoteAddr,\r
+      &mAllDhcpRelayAndServersAddress,\r
+      sizeof (EFI_IPv6_ADDRESS)\r
+      );\r
+  }\r
+\r
+  EndPt.RemotePort = DHCP6_PORT_SERVER;\r
+  EndPt.LocalPort  = DHCP6_PORT_CLIENT;\r
+\r
+  //\r
+  // Update the elapsed time value.\r
+  //\r
+  if (Elapsed != NULL) {\r
+    SetElapsedTime (Elapsed, Instance);\r
+  }\r
+\r
+  //\r
+  // Send out the message by the configured Udp6Io.\r
+  //\r
+  Status = UdpIoSendDatagram (\r
+             Service->UdpIo,\r
+             Wrap,\r
+             &EndPt,\r
+             NULL,\r
+             Dhcp6OnTransmitted,\r
+             NULL\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetbufFree (Wrap);\r
+    return Status;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Create the solicit message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+\r
+  @retval EFI_SUCCESS           Created and sent the solicit message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval Others                Failed to send the solicit message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendSolicitMsg   (\r
+  IN DHCP6_INSTANCE            *Instance\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_DHCP6_PACKET             *Packet;\r
+  EFI_DHCP6_PACKET_OPTION      *UserOpt;\r
+  EFI_DHCP6_DUID               *ClientId;\r
+  DHCP6_SERVICE                *Service;\r
+  UINT8                        *Cursor;\r
+  UINT16                       *Elapsed;\r
+  UINT32                       UserLen;\r
+  UINTN                        Index;\r
+  UINT16                       Length;\r
+\r
+  Service  = Instance->Service;\r
+  ClientId = Service->ClientId;\r
+  UserLen  = 0;\r
+\r
+  ASSERT (Service->ClientId != NULL);\r
+  ASSERT (Instance->Config != NULL);\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+  //\r
+  // Calculate the added length of customized option list.\r
+  //\r
+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+    UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);\r
+  }\r
+\r
+  //\r
+  // Create the Dhcp6 packet and initialize commone fields.\r
+  //\r
+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);\r
+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgSolicit;\r
+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+  //\r
+  // Assembly Dhcp6 options for solicit message.\r
+  //\r
+  Cursor = Packet->Dhcp6.Option;\r
+\r
+  Length = HTONS (ClientId->Length);\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             HTONS (Dhcp6OptClientId),\r
+             Length,\r
+             ClientId->Duid\r
+             );\r
+\r
+  Cursor = Dhcp6AppendETOption (\r
+             Cursor,\r
+             Instance,\r
+             &Elapsed\r
+             );\r
+\r
+  Cursor = Dhcp6AppendIaOption (\r
+             Cursor,\r
+             Instance->IaCb.Ia,\r
+             Instance->IaCb.T1,\r
+             Instance->IaCb.T2\r
+             );\r
+\r
+  //\r
+  // Append user-defined when configurate Dhcp6 service.\r
+  //\r
+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+\r
+    UserOpt = Instance->Config->OptionList[Index];\r
+    Cursor  = Dhcp6AppendOption(\r
+                Cursor,\r
+                UserOpt->OpCode,\r
+                UserOpt->OpLen,\r
+                UserOpt->Data\r
+                );\r
+  }\r
+\r
+  //\r
+  // Determine the size/length of packet.\r
+  //\r
+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+  ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+  //\r
+  // Callback to user with the packet to be sent and check the user's feedback.\r
+  //\r
+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendSolicit, &Packet);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Send solicit packet with the state transition from Dhcp6init to\r
+  // Dhcp6selecting.\r
+  //\r
+  Instance->IaCb.Ia->State = Dhcp6Selecting;\r
+\r
+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Enqueue the sent packet for the retransmission in case reply timeout.\r
+  //\r
+  return Dhcp6EnqueueRetry (\r
+           Instance,\r
+           Packet,\r
+           Elapsed,\r
+           Instance->Config->SolicitRetransmission\r
+           );\r
+}\r
+\r
+/**\r
+  Configure some parameter to initiate SolicitMsg.\r
+\r
+  @param[in]  Instance          The pointer to the Dhcp6 instance.\r
+\r
+  @retval EFI_SUCCESS           Created and sent the solicit message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval Others                Failed to send the solicit message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6InitSolicitMsg   (\r
+  IN DHCP6_INSTANCE            *Instance\r
+  )\r
+{\r
+  Instance->IaCb.T1 = 0;\r
+  Instance->IaCb.T2 = 0;\r
+  Instance->IaCb.Ia->IaAddressCount = 0;\r
+\r
+  return Dhcp6SendSolicitMsg (Instance);\r
+}\r
+\r
+\r
+/**\r
+  Create the request message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+\r
+  @retval EFI_SUCCESS           Created and sent the request message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to send the request message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendRequestMsg (\r
+  IN DHCP6_INSTANCE            *Instance\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_DHCP6_PACKET             *Packet;\r
+  EFI_DHCP6_PACKET_OPTION      *UserOpt;\r
+  EFI_DHCP6_DUID               *ClientId;\r
+  EFI_DHCP6_DUID               *ServerId;\r
+  DHCP6_SERVICE                *Service;\r
+  UINT8                        *Option;\r
+  UINT8                        *Cursor;\r
+  UINT16                       *Elapsed;\r
+  UINT32                       UserLen;\r
+  UINTN                        Index;\r
+  UINT16                       Length;\r
+\r
+  ASSERT(Instance->AdSelect != NULL);\r
+  ASSERT(Instance->Config != NULL);\r
+  ASSERT(Instance->IaCb.Ia != NULL);\r
+  ASSERT(Instance->Service != NULL);\r
+\r
+  Service  = Instance->Service;\r
+  ClientId = Service->ClientId;\r
+\r
+  ASSERT(ClientId != NULL);\r
+\r
+  //\r
+  // Get the server Id from the selected advertisement message.\r
+  //\r
+  Option = Dhcp6SeekOption (\r
+             Instance->AdSelect->Dhcp6.Option,\r
+             Instance->AdSelect->Length - 4,\r
+             Dhcp6OptServerId\r
+             );\r
+  if (Option == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  ServerId = (EFI_DHCP6_DUID *) (Option + 2);\r
+\r
+  //\r
+  // Calculate the added length of customized option list.\r
+  //\r
+  UserLen = 0;\r
+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+    UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);\r
+  }\r
+\r
+  //\r
+  // Create the Dhcp6 packet and initialize commone fields.\r
+  //\r
+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);\r
+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgRequest;\r
+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+  //\r
+  // Assembly Dhcp6 options for request message.\r
+  //\r
+  Cursor = Packet->Dhcp6.Option;\r
+\r
+  Length = HTONS (ClientId->Length);\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             HTONS (Dhcp6OptClientId),\r
+             Length,\r
+             ClientId->Duid\r
+             );\r
+\r
+  Cursor = Dhcp6AppendETOption (\r
+             Cursor,\r
+             Instance,\r
+             &Elapsed\r
+             );\r
+\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             HTONS (Dhcp6OptServerId),\r
+             ServerId->Length,\r
+             ServerId->Duid\r
+             );\r
+\r
+  Cursor = Dhcp6AppendIaOption (\r
+             Cursor,\r
+             Instance->IaCb.Ia,\r
+             Instance->IaCb.T1,\r
+             Instance->IaCb.T2\r
+             );\r
+\r
+  //\r
+  // Append user-defined when configurate Dhcp6 service.\r
+  //\r
+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+\r
+    UserOpt = Instance->Config->OptionList[Index];\r
+    Cursor  = Dhcp6AppendOption(\r
+                Cursor,\r
+                UserOpt->OpCode,\r
+                UserOpt->OpLen,\r
+                UserOpt->Data\r
+                );\r
+  }\r
+\r
+  //\r
+  // Determine the size/length of packet.\r
+  //\r
+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+  ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+  //\r
+  // Callback to user with the packet to be sent and check the user's feedback.\r
+  //\r
+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendRequest, &Packet);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Send request packet with the state transition from Dhcp6selecting to\r
+  // Dhcp6requesting.\r
+  //\r
+  Instance->IaCb.Ia->State = Dhcp6Requesting;\r
+\r
+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Enqueue the sent packet for the retransmission in case reply timeout.\r
+  //\r
+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+/**\r
+  Create the decline message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  DecIa           The pointer to the decline Ia.\r
+\r
+  @retval EFI_SUCCESS           Created and sent the decline message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to send the decline message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendDeclineMsg (\r
+  IN DHCP6_INSTANCE            *Instance,\r
+  IN EFI_DHCP6_IA              *DecIa\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_DHCP6_PACKET             *Packet;\r
+  EFI_DHCP6_PACKET             *LastReply;\r
+  EFI_DHCP6_DUID               *ClientId;\r
+  EFI_DHCP6_DUID               *ServerId;\r
+  DHCP6_SERVICE                *Service;\r
+  UINT8                        *Option;\r
+  UINT8                        *Cursor;\r
+  UINT16                       *Elapsed;\r
+  UINT16                       Length;\r
+\r
+  ASSERT (Instance->Config != NULL);\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+  ASSERT (Instance->Service != NULL);\r
+\r
+  Service   = Instance->Service;\r
+  ClientId  = Service->ClientId;\r
+  LastReply = Instance->IaCb.Ia->ReplyPacket;\r
+\r
+  ASSERT (ClientId != NULL);\r
+  ASSERT (LastReply != NULL);\r
+\r
+  //\r
+  // Get the server Id from the last reply message.\r
+  //\r
+  Option = Dhcp6SeekOption (\r
+             LastReply->Dhcp6.Option,\r
+             LastReply->Length - 4,\r
+             Dhcp6OptServerId\r
+             );\r
+  if (Option == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // EFI_DHCP6_DUID contains a length field of 2 bytes.\r
+  //\r
+  ServerId = (EFI_DHCP6_DUID *) (Option + 2);\r
+\r
+  //\r
+  // Create the Dhcp6 packet and initialize commone fields.\r
+  //\r
+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE;\r
+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);\r
+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgDecline;\r
+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+  //\r
+  // Assembly Dhcp6 options for rebind/renew message.\r
+  //\r
+  Cursor = Packet->Dhcp6.Option;\r
+\r
+  Length = HTONS (ClientId->Length);\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             HTONS (Dhcp6OptClientId),\r
+             Length,\r
+             ClientId->Duid\r
+             );\r
+\r
+  Cursor = Dhcp6AppendETOption (\r
+             Cursor,\r
+             Instance,\r
+             &Elapsed\r
+             );\r
+\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             HTONS (Dhcp6OptServerId),\r
+             ServerId->Length,\r
+             ServerId->Duid\r
+             );\r
+\r
+  Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0);\r
+\r
+  //\r
+  // Determine the size/length of packet.\r
+  //\r
+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+  ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+  //\r
+  // Callback to user with the packet to be sent and check the user's feedback.\r
+  //\r
+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendDecline, &Packet);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Send decline packet with the state transition from Dhcp6bound to\r
+  // Dhcp6declining.\r
+  //\r
+  Instance->IaCb.Ia->State = Dhcp6Declining;\r
+\r
+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Enqueue the sent packet for the retransmission in case reply timeout.\r
+  //\r
+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+/**\r
+  Create the release message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  RelIa           The pointer to the release Ia.\r
+\r
+  @retval EFI_SUCCESS           Created and sent the release message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to send the release message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendReleaseMsg (\r
+  IN DHCP6_INSTANCE            *Instance,\r
+  IN EFI_DHCP6_IA              *RelIa\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_DHCP6_PACKET             *Packet;\r
+  EFI_DHCP6_PACKET             *LastReply;\r
+  EFI_DHCP6_DUID               *ClientId;\r
+  EFI_DHCP6_DUID               *ServerId;\r
+  DHCP6_SERVICE                *Service;\r
+  UINT8                        *Option;\r
+  UINT8                        *Cursor;\r
+  UINT16                       *Elapsed;\r
+  UINT16                       Length;\r
+\r
+  ASSERT(Instance->Config);\r
+  ASSERT(Instance->IaCb.Ia);\r
+\r
+  Service   = Instance->Service;\r
+  ClientId  = Service->ClientId;\r
+  LastReply = Instance->IaCb.Ia->ReplyPacket;\r
+\r
+  ASSERT(ClientId);\r
+  ASSERT(LastReply);\r
+\r
+  //\r
+  // Get the server Id from the last reply message.\r
+  //\r
+  Option = Dhcp6SeekOption (\r
+             LastReply->Dhcp6.Option,\r
+             LastReply->Length - 4,\r
+             Dhcp6OptServerId\r
+             );\r
+  if (Option == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  ServerId = (EFI_DHCP6_DUID *) (Option + 2);\r
+\r
+  //\r
+  // Create the Dhcp6 packet and initialize commone fields.\r
+  //\r
+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE;\r
+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);\r
+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgRelease;\r
+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+  //\r
+  // Assembly Dhcp6 options for rebind/renew message\r
+  //\r
+  Cursor = Packet->Dhcp6.Option;\r
+\r
+  Length = HTONS (ClientId->Length);\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             HTONS (Dhcp6OptClientId),\r
+             Length,\r
+             ClientId->Duid\r
+             );\r
+\r
+  //\r
+  // ServerId is extracted from packet, it's network order.\r
+  //\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             HTONS (Dhcp6OptServerId),\r
+             ServerId->Length,\r
+             ServerId->Duid\r
+             );\r
+\r
+  Cursor = Dhcp6AppendETOption (\r
+             Cursor,\r
+             Instance,\r
+             &Elapsed\r
+             );\r
+\r
+  Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0);\r
+\r
+  //\r
+  // Determine the size/length of packet\r
+  //\r
+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+  ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+  //\r
+  // Callback to user with the packet to be sent and check the user's feedback.\r
+  //\r
+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendRelease, &Packet);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Send release packet with the state transition from Dhcp6bound to\r
+  // Dhcp6releasing.\r
+  //\r
+  Instance->IaCb.Ia->State = Dhcp6Releasing;\r
+\r
+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Enqueue the sent packet for the retransmission in case reply timeout.\r
+  //\r
+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+/**\r
+  Create the renew/rebind message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  RebindRequest   If TRUE, it is a Rebind type message.\r
+                              Otherwise, it is a Renew type message.\r
+\r
+  @retval EFI_SUCCESS           Created and sent the renew/rebind message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to send the renew/rebind message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendRenewRebindMsg (\r
+  IN DHCP6_INSTANCE         *Instance,\r
+  IN BOOLEAN                RebindRequest\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_DHCP6_PACKET          *Packet;\r
+  EFI_DHCP6_PACKET          *LastReply;\r
+  EFI_DHCP6_PACKET_OPTION   *UserOpt;\r
+  EFI_DHCP6_DUID            *ClientId;\r
+  EFI_DHCP6_DUID            *ServerId;\r
+  EFI_DHCP6_STATE           State;\r
+  EFI_DHCP6_EVENT           Event;\r
+  DHCP6_SERVICE             *Service;\r
+  UINT8                     *Option;\r
+  UINT8                     *Cursor;\r
+  UINT16                    *Elapsed;\r
+  UINT32                    UserLen;\r
+  UINTN                     Index;\r
+  UINT16                    Length;\r
+\r
+  ASSERT(Instance->Config);\r
+  ASSERT(Instance->IaCb.Ia);\r
+\r
+  Service   = Instance->Service;\r
+  ClientId  = Service->ClientId;\r
+\r
+  ASSERT(ClientId);\r
+\r
+  //\r
+  // Calculate the added length of customized option list.\r
+  //\r
+  UserLen = 0;\r
+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+    UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);\r
+  }\r
+\r
+  //\r
+  // Create the Dhcp6 packet and initialize commone fields.\r
+  //\r
+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);\r
+  Packet->Dhcp6.Header.MessageType   = RebindRequest ? Dhcp6MsgRebind : Dhcp6MsgRenew;\r
+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+  //\r
+  // Assembly Dhcp6 options for rebind/renew message.\r
+  //\r
+  Cursor = Packet->Dhcp6.Option;\r
+\r
+  Length = HTONS (ClientId->Length);\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             HTONS (Dhcp6OptClientId),\r
+             Length,\r
+             ClientId->Duid\r
+             );\r
+\r
+  Cursor = Dhcp6AppendETOption (\r
+             Cursor,\r
+             Instance,\r
+             &Elapsed\r
+             );\r
+\r
+  Cursor = Dhcp6AppendIaOption (\r
+             Cursor,\r
+             Instance->IaCb.Ia,\r
+             Instance->IaCb.T1,\r
+             Instance->IaCb.T2\r
+             );\r
+\r
+  if (!RebindRequest) {\r
+    //\r
+    // Get the server Id from the last reply message and\r
+    // insert it for rebind request.\r
+    //\r
+    LastReply = Instance->IaCb.Ia->ReplyPacket;\r
+    ASSERT (LastReply);\r
+\r
+    Option = Dhcp6SeekOption (\r
+               LastReply->Dhcp6.Option,\r
+               LastReply->Length - 4,\r
+               Dhcp6OptServerId\r
+               );\r
+    if (Option == NULL) {\r
+      FreePool (Packet);\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    ServerId = (EFI_DHCP6_DUID *) (Option + 2);\r
+\r
+    Cursor = Dhcp6AppendOption (\r
+               Cursor,\r
+               HTONS (Dhcp6OptServerId),\r
+               ServerId->Length,\r
+               ServerId->Duid\r
+               );\r
+  }\r
+\r
+  //\r
+  // Append user-defined when configurate Dhcp6 service.\r
+  //\r
+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+\r
+    UserOpt = Instance->Config->OptionList[Index];\r
+    Cursor  = Dhcp6AppendOption(\r
+                Cursor,\r
+                UserOpt->OpCode,\r
+                UserOpt->OpLen,\r
+                UserOpt->Data\r
+                );\r
+  }\r
+\r
+  //\r
+  // Determine the size/length of packet.\r
+  //\r
+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+  ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+  //\r
+  // Callback to user with the packet to be sent and check the user's feedback.\r
+  //\r
+  State  = (RebindRequest) ? Dhcp6Rebinding : Dhcp6Renewing;\r
+  Event  = (RebindRequest) ? Dhcp6EnterRebinding : Dhcp6EnterRenewing;\r
+\r
+  Status = Dhcp6CallbackUser (Instance, Event, &Packet);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Send renew/rebind packet with the state transition from Dhcp6bound to\r
+  // Dhcp6renew/rebind.\r
+  // And sync the lease time when send renew/rebind, in case that user send\r
+  // renew/rebind actively.\r
+  //\r
+  Instance->IaCb.Ia->State = State;\r
+  Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1;\r
+\r
+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Enqueue the sent packet for the retransmission in case reply timeout.\r
+  //\r
+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+/**\r
+  Create the information request message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  InfCb           The pointer to the information request control block.\r
+  @param[in]  SendClientId    If TRUE, the client identifier option will be included in\r
+                              information request message. Otherwise, the client identifier\r
+                              option will not be included.\r
+  @param[in]  OptionRequest   The pointer to the option request option.\r
+  @param[in]  OptionCount     The number options in the OptionList.\r
+  @param[in]  OptionList      The array pointers to the appended options.\r
+  @param[in]  Retransmission  The pointer to the retransmission control.\r
+\r
+  @retval EFI_SUCCESS           Created and sent the info-request message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval Others                Failed to send the info-request message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendInfoRequestMsg (\r
+  IN DHCP6_INSTANCE            *Instance,\r
+  IN DHCP6_INF_CB              *InfCb,\r
+  IN BOOLEAN                   SendClientId,\r
+  IN EFI_DHCP6_PACKET_OPTION   *OptionRequest,\r
+  IN UINT32                    OptionCount,\r
+  IN EFI_DHCP6_PACKET_OPTION   *OptionList[],\r
+  IN EFI_DHCP6_RETRANSMISSION  *Retransmission\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_DHCP6_PACKET             *Packet;\r
+  EFI_DHCP6_PACKET_OPTION      *UserOpt;\r
+  EFI_DHCP6_DUID               *ClientId;\r
+  DHCP6_SERVICE                *Service;\r
+  UINT8                        *Cursor;\r
+  UINT16                       *Elapsed;\r
+  UINT32                       UserLen;\r
+  UINTN                        Index;\r
+  UINT16                       Length;\r
+\r
+  ASSERT(OptionRequest);\r
+\r
+  Service  = Instance->Service;\r
+  ClientId = Service->ClientId;\r
+  UserLen  = NTOHS (OptionRequest->OpLen) + 4;\r
+\r
+  ASSERT(ClientId);\r
+\r
+  //\r
+  // Calculate the added length of customized option list.\r
+  //\r
+  for (Index = 0; Index < OptionCount; Index++) {\r
+    UserLen += (NTOHS (OptionList[Index]->OpLen) + 4);\r
+  }\r
+\r
+  //\r
+  // Create the Dhcp6 packet and initialize commone fields.\r
+  //\r
+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);\r
+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgInfoRequest;\r
+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+  InfCb->Xid                         = Packet->Dhcp6.Header.TransactionId;\r
+\r
+  //\r
+  // Assembly Dhcp6 options for info-request message.\r
+  //\r
+  Cursor = Packet->Dhcp6.Option;\r
+\r
+  if (SendClientId) {\r
+    Length = HTONS (ClientId->Length);\r
+    Cursor = Dhcp6AppendOption (\r
+               Cursor,\r
+               HTONS (Dhcp6OptClientId),\r
+               Length,\r
+               ClientId->Duid\r
+               );\r
+  }\r
+\r
+  Cursor = Dhcp6AppendETOption (\r
+             Cursor,\r
+             Instance,\r
+             &Elapsed\r
+             );\r
+\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             OptionRequest->OpCode,\r
+             OptionRequest->OpLen,\r
+             OptionRequest->Data\r
+             );\r
+\r
+  //\r
+  // Append user-defined when configurate Dhcp6 service.\r
+  //\r
+  for (Index = 0; Index < OptionCount; Index++) {\r
+\r
+    UserOpt = OptionList[Index];\r
+    Cursor  = Dhcp6AppendOption(\r
+                Cursor,\r
+                UserOpt->OpCode,\r
+                UserOpt->OpLen,\r
+                UserOpt->Data\r
+                );\r
+  }\r
+\r
+  //\r
+  // Determine the size/length of packet.\r
+  //\r
+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+  ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+  //\r
+  // Send info-request packet with no state.\r
+  //\r
+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Enqueue the sent packet for the retransmission in case reply timeout.\r
+  //\r
+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, Retransmission);\r
+}\r
+\r
+\r
+/**\r
+  Create the Confirm message and send it.\r
+\r
+  @param[in]  Instance          The pointer to the Dhcp6 instance.\r
+\r
+  @retval EFI_SUCCESS           Created and sent the confirm message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to send the confirm message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendConfirmMsg (\r
+  IN DHCP6_INSTANCE            *Instance\r
+  )\r
+{\r
+  UINT8                        *Cursor;\r
+  UINTN                        Index;\r
+  UINT16                       Length;\r
+  UINT32                       UserLen;\r
+  EFI_STATUS                   Status;\r
+  DHCP6_SERVICE                *Service;\r
+  EFI_DHCP6_DUID               *ClientId;\r
+  EFI_DHCP6_PACKET             *Packet;\r
+  EFI_DHCP6_PACKET_OPTION      *UserOpt;\r
+  UINT16                       *Elapsed;\r
+\r
+  ASSERT (Instance->Config != NULL);\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+  ASSERT (Instance->Service != NULL);\r
+\r
+  Service  = Instance->Service;\r
+  ClientId = Service->ClientId;\r
+  ASSERT (ClientId != NULL);\r
+\r
+  //\r
+  // Calculate the added length of customized option list.\r
+  //\r
+  UserLen = 0;\r
+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+    UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);\r
+  }\r
+\r
+  //\r
+  // Create the Dhcp6 packet and initialize common fields.\r
+  //\r
+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);\r
+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgConfirm;\r
+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+  //\r
+  // Assembly Dhcp6 options for solicit message.\r
+  //\r
+  Cursor = Packet->Dhcp6.Option;\r
+\r
+  Length = HTONS (ClientId->Length);\r
+  Cursor = Dhcp6AppendOption (\r
+             Cursor,\r
+             HTONS (Dhcp6OptClientId),\r
+             Length,\r
+             ClientId->Duid\r
+             );\r
+\r
+  Cursor = Dhcp6AppendETOption (\r
+             Cursor,\r
+             Instance,\r
+             &Elapsed\r
+             );\r
+\r
+  Cursor = Dhcp6AppendIaOption (\r
+             Cursor,\r
+             Instance->IaCb.Ia,\r
+             Instance->IaCb.T1,\r
+             Instance->IaCb.T2\r
+             );\r
+\r
+  //\r
+  // Append user-defined when configurate Dhcp6 service.\r
+  //\r
+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+    UserOpt = Instance->Config->OptionList[Index];\r
+    Cursor  = Dhcp6AppendOption (\r
+                Cursor,\r
+                UserOpt->OpCode,\r
+                UserOpt->OpLen,\r
+                UserOpt->Data\r
+                );\r
+  }\r
+\r
+  //\r
+  // Determine the size/length of packet.\r
+  //\r
+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+  ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+  //\r
+  // Callback to user with the packet to be sent and check the user's feedback.\r
+  //\r
+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendConfirm, &Packet);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Send confirm packet with the state transition from Dhcp6Bound to\r
+  // Dhcp6Confirming.\r
+  //\r
+  Instance->IaCb.Ia->State = Dhcp6Confirming;\r
+\r
+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Packet);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Enqueue the sent packet for the retransmission in case reply timeout.\r
+  //\r
+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+\r
+/**\r
+  Handle with the Dhcp6 reply message.\r
+\r
+  @param[in]  Instance        The pointer to Dhcp6 instance.\r
+  @param[in]  Packet          The pointer to the Dhcp6 reply message.\r
+\r
+  @retval EFI_SUCCESS           Processed the reply message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to process the reply message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6HandleReplyMsg (\r
+  IN DHCP6_INSTANCE           *Instance,\r
+  IN EFI_DHCP6_PACKET         *Packet\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINT8                       *Option;\r
+  UINT16                      StsCode;\r
+\r
+  ASSERT (Instance->Config != NULL);\r
+  ASSERT (Instance->IaCb.Ia != NULL);\r
+  ASSERT (Packet != NULL);\r
+\r
+  if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // If the client subsequently receives a valid reply message that includes a\r
+  // rapid commit option since send a solicit with rapid commit option before,\r
+  // preocess the reply message and discard any reply messages received in\r
+  // response to the request message.\r
+  // See details in the section-17.1.4 of rfc-3315.\r
+  //\r
+  Option = Dhcp6SeekOption (\r
+             Packet->Dhcp6.Option,\r
+             Packet->Length - 4,\r
+             Dhcp6OptRapidCommit\r
+             );\r
+\r
+  if ((Option != NULL && !Instance->Config->RapidCommit) || (Option == NULL && Instance->Config->RapidCommit)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // As to a valid reply packet in response to a request/renew/rebind packet,\r
+  // ignore the packet if not contains the Ia option\r
+  //\r
+  if (Instance->IaCb.Ia->State == Dhcp6Requesting ||\r
+      Instance->IaCb.Ia->State == Dhcp6Renewing ||\r
+      Instance->IaCb.Ia->State == Dhcp6Rebinding\r
+      ) {\r
+\r
+    Option = Dhcp6SeekIaOption (\r
+               Packet->Dhcp6.Option,\r
+               Packet->Length,\r
+               &Instance->Config->IaDescriptor\r
+               );\r
+    if (Option == NULL) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Callback to user with the received packet and check the user's feedback.\r
+  //\r
+  Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdReply, &Packet);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Dequeue the sent packet from retransmit list since reply received.\r
+  //\r
+  Status = Dhcp6DequeueRetry (\r
+             Instance,\r
+             Packet->Dhcp6.Header.TransactionId,\r
+             FALSE\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // When receive a valid reply packet in response to a decline/release packet,\r
+  // the client considers the decline/release event completed regardless of the\r
+  // status code.\r
+  //\r
+  if (Instance->IaCb.Ia->State == Dhcp6Declining || Instance->IaCb.Ia->State == Dhcp6Releasing) {\r
+\r
+    if (Instance->IaCb.Ia->IaAddressCount != 0) {\r
+      Instance->IaCb.Ia->State       = Dhcp6Bound;\r
+    } else {\r
+      ASSERT (Instance->IaCb.Ia->ReplyPacket);\r
+      FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+      Instance->IaCb.Ia->ReplyPacket = NULL;\r
+      Instance->IaCb.Ia->State       = Dhcp6Init;\r
+    }\r
+\r
+    //\r
+    // For sync, set the success flag out of polling in decline/release.\r
+    //\r
+    Instance->UdpSts = EFI_SUCCESS;\r
+\r
+    //\r
+    // For async, signal the Ia event to inform Ia infomation update.\r
+    //\r
+    if (Instance->Config->IaInfoEvent != NULL) {\r
+      gBS->SignalEvent (Instance->Config->IaInfoEvent);\r
+    }\r
+\r
+    //\r
+    // Reset start time for next exchange.\r
+    //\r
+    Instance->StartTime       = 0;\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Upon the receipt of a valid reply packet in response to a solicit, request,\r
+  // confirm, renew and rebind, the behavior depends on the status code option.\r
+  // See the details in the section-18.1.8 of rfc-3315.\r
+  //\r
+  Option = NULL;\r
+  Status = Dhcp6SeekStsOption (\r
+             Instance,\r
+             Packet,\r
+             &Option\r
+             );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Reset start time for next exchange.\r
+    //\r
+    Instance->StartTime       = 0;\r
+\r
+    //\r
+    // No status code or no error status code means succeed to reply.\r
+    //\r
+    Status = Dhcp6UpdateIaInfo (Instance, Packet);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Set bound state and store the reply packet.\r
+    //\r
+    if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
+      FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+    }\r
+\r
+    Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size);\r
+\r
+    if (Instance->IaCb.Ia->ReplyPacket == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size);\r
+\r
+    Instance->IaCb.Ia->State = Dhcp6Bound;\r
+\r
+    //\r
+    // For sync, set the success flag out of polling in start/renewrebind.\r
+    //\r
+    Instance->UdpSts         = EFI_SUCCESS;\r
+\r
+    //\r
+    // Maybe this is a new round DHCP process due to some reason, such as NotOnLink\r
+    // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that\r
+    // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP\r
+    // consumers can be notified to flush old address.\r
+    //\r
+    Dhcp6AppendCacheIa (Instance);\r
+\r
+    //\r
+    // For async, signal the Ia event to inform Ia infomation update.\r
+    //\r
+    if (Instance->Config->IaInfoEvent != NULL) {\r
+      gBS->SignalEvent (Instance->Config->IaInfoEvent);\r
+    }\r
+  } else if (Option != NULL) {\r
+    //\r
+    // Any error status code option is found.\r
+    //\r
+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));\r
+    switch (StsCode) {\r
+    case Dhcp6StsUnspecFail:\r
+      //\r
+      // It indicates the server is unable to process the message due to an\r
+      // unspecified failure condition, so just retry if possible.\r
+      //\r
+      break;\r
+\r
+    case Dhcp6StsUseMulticast:\r
+      //\r
+      // It indicates the server receives a message via unicast from a client\r
+      // to which the server has not sent a unicast option, so retry it by\r
+      // multi-cast address.\r
+      //\r
+      if (Instance->Unicast != NULL) {\r
+        FreePool (Instance->Unicast);\r
+        Instance->Unicast = NULL;\r
+      }\r
+      break;\r
+\r
+    case Dhcp6StsNotOnLink:\r
+      if (Instance->IaCb.Ia->State == Dhcp6Confirming) {\r
+        //\r
+        // Before initiate new round DHCP, cache the current IA.\r
+        //\r
+        Status = Dhcp6CacheIa (Instance);\r
+        if (EFI_ERROR (Status)) {\r
+          return  Status;\r
+        }\r
+\r
+        //\r
+        // Restart S.A.R.R process to acquire new address.\r
+        //\r
+        Status = Dhcp6InitSolicitMsg (Instance);\r
+        if (EFI_ERROR (Status)) {\r
+          return Status;\r
+        }\r
+      }\r
+      break;\r
+\r
+    default:\r
+      //\r
+      // The other status code, just restart solicitation.\r
+      //\r
+      break;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Select the appointed Dhcp6 advertisement message.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  AdSelect        The pointer to the selected Dhcp6 advertisement message.\r
+\r
+  @retval EFI_SUCCESS           Selected the right advertisement message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval Others                Failed to select the advertise message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SelectAdvertiseMsg (\r
+  IN DHCP6_INSTANCE           *Instance,\r
+  IN EFI_DHCP6_PACKET         *AdSelect\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINT8                       *Option;\r
+\r
+  ASSERT (AdSelect != NULL);\r
+\r
+  //\r
+  // Callback to user with the selected advertisement packet, and the user\r
+  // might overwrite it.\r
+  //\r
+  Status = Dhcp6CallbackUser (Instance, Dhcp6SelectAdvertise, &AdSelect);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Instance->AdSelect = AdSelect;\r
+\r
+  //\r
+  // Dequeue the sent packet for the retransmission since advertisement selected.\r
+  //\r
+  Status = Dhcp6DequeueRetry (\r
+             Instance,\r
+             AdSelect->Dhcp6.Header.TransactionId,\r
+             FALSE\r
+             );\r
+\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Check whether there is server unicast option in the selected advertise\r
+  // packet, and update it.\r
+  //\r
+  Option = Dhcp6SeekOption(\r
+             AdSelect->Dhcp6.Option,\r
+             AdSelect->Length - 4,\r
+             Dhcp6OptServerUnicast\r
+             );\r
+\r
+  if (Option != NULL) {\r
+\r
+    Instance->Unicast = AllocateZeroPool (sizeof(EFI_IPv6_ADDRESS));\r
+\r
+    if (Instance->Unicast == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    CopyMem (Instance->Unicast, Option + 4, sizeof(EFI_IPv6_ADDRESS));\r
+  }\r
+\r
+  //\r
+  // Update the information of the Ia by the selected advertisement message.\r
+  //\r
+  Status = Dhcp6UpdateIaInfo (Instance, AdSelect);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Send the request message to continue the S.A.R.R. process.\r
+  //\r
+  return Dhcp6SendRequestMsg (Instance);\r
+}\r
+\r
+\r
+/**\r
+  Handle with the Dhcp6 advertisement message.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  Packet          The pointer to the Dhcp6 advertisement message.\r
+\r
+  @retval EFI_SUCCESS           Processed the advertisement message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to process the advertise message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6HandleAdvertiseMsg (\r
+  IN DHCP6_INSTANCE           *Instance,\r
+  IN EFI_DHCP6_PACKET         *Packet\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINT8                       *Option;\r
+  UINT16                      StsCode;\r
+  BOOLEAN                     Timeout;\r
+\r
+  ASSERT(Instance->Config);\r
+  ASSERT(Instance->IaCb.Ia);\r
+\r
+  Timeout = FALSE;\r
+  StsCode = Dhcp6StsSuccess;\r
+\r
+  //\r
+  // If the client does receives a valid reply message that includes a rapid\r
+  // commit option since a solicit with rapid commit optioin sent before, select\r
+  // this reply message. Or else, process the advertise messages as normal.\r
+  // See details in the section-17.1.4 of rfc-3315.\r
+  //\r
+  Option = Dhcp6SeekOption(\r
+             Packet->Dhcp6.Option,\r
+             Packet->Length - 4,\r
+             Dhcp6OptRapidCommit\r
+             );\r
+\r
+  if (Option != NULL && Instance->Config->RapidCommit && Packet->Dhcp6.Header.MessageType == Dhcp6MsgReply) {\r
+\r
+    return Dhcp6HandleReplyMsg (Instance, Packet);\r
+  }\r
+\r
+  if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Client must ignore any advertise message that includes a status code option\r
+  // containing the value noaddrsavail, with the exception that the client may\r
+  // display the associated status message to the user.\r
+  // See the details in the section-17.1.3 of rfc-3315.\r
+  //\r
+  Option = Dhcp6SeekOption(\r
+             Packet->Dhcp6.Option,\r
+             Packet->Length - 4,\r
+             Dhcp6OptStatusCode\r
+             );\r
+\r
+  if (Option != NULL) {\r
+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));\r
+    if (StsCode != Dhcp6StsSuccess) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Callback to user with the received packet and check the user's feedback.\r
+  //\r
+  Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdAdvertise, &Packet);\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Success means user choose the current advertisement packet.\r
+    //\r
+    if (Instance->AdSelect != NULL) {\r
+      FreePool (Instance->AdSelect);\r
+    }\r
+\r
+    //\r
+    // Store the selected advertisement packet and set a flag.\r
+    //\r
+    Instance->AdSelect = AllocateZeroPool (Packet->Size);\r
+\r
+    if (Instance->AdSelect == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    CopyMem (Instance->AdSelect, Packet, Packet->Size);\r
+\r
+    Instance->AdPref = 0xff;\r
+\r
+  } else if (Status == EFI_NOT_READY) {\r
+    //\r
+    // Not_ready means user wants to continue to receive more advertise packets.\r
+    //\r
+    if (Instance->AdPref == 0xff && Instance->AdSelect == NULL) {\r
+      //\r
+      // It's a tricky point. The timer routine set adpref as 0xff if the first\r
+      // rt timeout and no advertisement received, which means any advertisement\r
+      // received will be selected after the first rt.\r
+      //\r
+      Timeout = TRUE;\r
+    }\r
+\r
+    //\r
+    // Check whether the current packet has a 255 preference option or not.\r
+    // Take non-preference option as 0 value.\r
+    //\r
+    Option = Dhcp6SeekOption(\r
+               Packet->Dhcp6.Option,\r
+               Packet->Length - 4,\r
+               Dhcp6OptPreference\r
+               );\r
+\r
+    if (Instance->AdSelect == NULL || (Option != NULL && *(Option + 4) > Instance->AdPref)) {\r
+      //\r
+      // No advertisements received before or preference is more than other\r
+      // advertisements received before. Then store the new packet and the\r
+      // preference value.\r
+      //\r
+      if (Instance->AdSelect != NULL) {\r
+        FreePool (Instance->AdSelect);\r
+      }\r
+\r
+      Instance->AdSelect = AllocateZeroPool (Packet->Size);\r
+\r
+      if (Instance->AdSelect == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      CopyMem (Instance->AdSelect, Packet, Packet->Size);\r
+\r
+      if (Option != NULL) {\r
+        Instance->AdPref = *(Option + 4);\r
+      }\r
+    } else {\r
+      //\r
+      // Non-preference and other advertisements received before or current\r
+      // preference is less than other advertisements received before.\r
+      // Leave the packet alone.\r
+    }\r
+\r
+  } else {\r
+    //\r
+    // Other error status means termination.\r
+    //\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Client must collect advertise messages as more as possible until the first\r
+  // RT has elapsed, or get a highest preference 255 advertise.\r
+  // See details in the section-17.1.2 of rfc-3315.\r
+  //\r
+  if (Instance->AdPref == 0xff || Timeout) {\r
+    Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The Dhcp6 stateful exchange process routine.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  Packet          The pointer to the received Dhcp6 message.\r
+\r
+**/\r
+VOID\r
+Dhcp6HandleStateful (\r
+  IN DHCP6_INSTANCE         *Instance,\r
+  IN EFI_DHCP6_PACKET       *Packet\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_DHCP6_DUID            *ClientId;\r
+  DHCP6_SERVICE             *Service;\r
+  UINT8                     *Option;\r
+\r
+  Service  = Instance->Service;\r
+  ClientId = Service->ClientId;\r
+  Status   = EFI_SUCCESS;\r
+\r
+  if (Instance->InDestory || Instance->Config == NULL) {\r
+    goto ON_CONTINUE;\r
+  }\r
+\r
+  ASSERT (ClientId);\r
+  ASSERT (Instance->Config);\r
+  ASSERT (Instance->IaCb.Ia);\r
+\r
+  //\r
+  // Discard the packet if not advertisement or reply packet.\r
+  //\r
+  if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise && Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {\r
+    goto ON_CONTINUE;\r
+  }\r
+\r
+  //\r
+  // Check whether include client Id or not.\r
+  //\r
+  Option = Dhcp6SeekOption(\r
+             Packet->Dhcp6.Option,\r
+             Packet->Length - 4,\r
+             Dhcp6OptClientId\r
+             );\r
+\r
+  if (Option == NULL || CompareMem (Option + 4, ClientId->Duid, ClientId->Length) != 0) {\r
+    goto ON_CONTINUE;\r
+  }\r
+\r
+  //\r
+  // Check whether include server Id or not.\r
+  //\r
+  Option = Dhcp6SeekOption(\r
+             Packet->Dhcp6.Option,\r
+             Packet->Length - 4,\r
+             Dhcp6OptServerId\r
+             );\r
+\r
+  if (Option == NULL) {\r
+    goto ON_CONTINUE;\r
+  }\r
+\r
+  switch (Instance->IaCb.Ia->State) {\r
+  case Dhcp6Selecting:\r
+    //\r
+    // Handle the advertisement message when in the Dhcp6Selecting state.\r
+    // Do not need check return status, if failed, just continue to the next.\r
+    //\r
+    Dhcp6HandleAdvertiseMsg (Instance, Packet);\r
+    break;\r
+\r
+  case Dhcp6Requesting:\r
+  case Dhcp6Confirming:\r
+  case Dhcp6Renewing:\r
+  case Dhcp6Rebinding:\r
+  case Dhcp6Releasing:\r
+  case Dhcp6Declining:\r
+    //\r
+    // Handle the reply message when in the Dhcp6Requesting,  Dhcp6Renewing\r
+    // Dhcp6Rebinding, Dhcp6Releasing and Dhcp6Declining state.\r
+    // If failed here, it should reset the current session.\r
+    //\r
+    Status = Dhcp6HandleReplyMsg (Instance, Packet);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+    break;\r
+  default:\r
+    //\r
+    // Other state has not supported yet.\r
+    //\r
+    break;\r
+  }\r
+\r
+ON_CONTINUE:\r
+  //\r
+  // Continue to receive the following Dhcp6 message.\r
+  //\r
+  Status = UdpIoRecvDatagram (\r
+             Service->UdpIo,\r
+             Dhcp6ReceivePacket,\r
+             Service,\r
+             0\r
+             );\r
+ON_EXIT:\r
+  if (EFI_ERROR (Status)) {\r
+    Dhcp6CleanupSession (Instance, Status);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  The Dhcp6 stateless exchange process routine.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  Packet          The pointer to the received Dhcp6 message.\r
+\r
+**/\r
+VOID\r
+Dhcp6HandleStateless (\r
+  IN DHCP6_INSTANCE         *Instance,\r
+  IN EFI_DHCP6_PACKET       *Packet\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  DHCP6_SERVICE             *Service;\r
+  DHCP6_INF_CB              *InfCb;\r
+  UINT8                     *Option;\r
+  BOOLEAN                   IsMatched;\r
+\r
+  Service   = Instance->Service;\r
+  Status    = EFI_SUCCESS;\r
+  IsMatched = FALSE;\r
+  InfCb     = NULL;\r
+\r
+  if (Instance->InDestory) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check whether it's a desired Info-request message by Xid.\r
+  //\r
+  while (!IsListEmpty (&Instance->InfList)) {\r
+    InfCb = NET_LIST_HEAD (&Instance->InfList, DHCP6_INF_CB, Link);\r
+    if (InfCb->Xid == Packet->Dhcp6.Header.TransactionId) {\r
+      IsMatched = TRUE;\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (!IsMatched) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check whether include server Id or not.\r
+  //\r
+  Option = Dhcp6SeekOption (\r
+             Packet->Dhcp6.Option,\r
+             Packet->Length - 4,\r
+             Dhcp6OptServerId\r
+             );\r
+\r
+  if (Option == NULL) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Callback to user with the received packet and check the user's feedback.\r
+  //\r
+  Status = InfCb->ReplyCallback (\r
+                    &Instance->Dhcp6,\r
+                    InfCb->CallbackContext,\r
+                    Packet\r
+                    );\r
+\r
+  if (Status == EFI_NOT_READY) {\r
+    //\r
+    // Success or aborted will both stop this info-request exchange process,\r
+    // but not ready means user wants to continue to receive reply.\r
+    //\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Dequeue the sent packet from the txlist if the xid matched, and ignore\r
+  // if no xid matched.\r
+  //\r
+  Dhcp6DequeueRetry (\r
+    Instance,\r
+    Packet->Dhcp6.Header.TransactionId,\r
+    FALSE\r
+    );\r
+\r
+  //\r
+  // For sync, set the status out of polling for info-request.\r
+  //\r
+  Instance->UdpSts = Status;\r
+\r
+ON_EXIT:\r
+\r
+  Status = UdpIoRecvDatagram (\r
+             Service->UdpIo,\r
+             Dhcp6ReceivePacket,\r
+             Service,\r
+             0\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATELESS);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  The receive callback function for Dhcp6 exchange process.\r
+\r
+  @param[in]  Udp6Wrap        The pointer to the received net buffer.\r
+  @param[in]  EndPoint        The pointer to the udp end point.\r
+  @param[in]  IoStatus        The return status from udp io.\r
+  @param[in]  Context         The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6ReceivePacket (\r
+  IN NET_BUF                *Udp6Wrap,\r
+  IN UDP_END_POINT          *EndPoint,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_DHCP6_HEADER          *Head;\r
+  EFI_DHCP6_PACKET          *Packet;\r
+  DHCP6_SERVICE             *Service;\r
+  DHCP6_INSTANCE            *Instance;\r
+  DHCP6_TX_CB               *TxCb;\r
+  UINT32                    Size;\r
+  BOOLEAN                   IsDispatched;\r
+  BOOLEAN                   IsStateless;\r
+  LIST_ENTRY                *Entry1;\r
+  LIST_ENTRY                *Next1;\r
+  LIST_ENTRY                *Entry2;\r
+  LIST_ENTRY                *Next2;\r
+\r
+  ASSERT (Udp6Wrap != NULL);\r
+  ASSERT (Context != NULL);\r
+\r
+  Service      = (DHCP6_SERVICE *) Context;\r
+  Instance     = NULL;\r
+  Packet       = NULL;\r
+  IsDispatched = FALSE;\r
+  IsStateless  = FALSE;\r
+\r
+  if (EFI_ERROR (IoStatus)) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Copy the net buffer received from upd6 to a Dhcp6 packet.\r
+  //\r
+  Size   = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize;\r
+  Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size);\r
+\r
+  if (Packet == NULL) {\r
+    goto ON_CONTINUE;\r
+  }\r
+\r
+  Packet->Size   = Size;\r
+  Head           = &Packet->Dhcp6.Header;\r
+  Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head);\r
+\r
+  if (Packet->Length == 0) {\r
+    goto ON_CONTINUE;\r
+  }\r
+\r
+  //\r
+  // Dispatch packet to right instance by transaction id.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {\r
+\r
+    Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);\r
+\r
+    NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) {\r
+\r
+      TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link);\r
+\r
+      if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) {\r
+        //\r
+        // Find the corresponding packet in tx list, and check it whether belongs\r
+        // to stateful exchange process.\r
+        //\r
+        if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {\r
+          IsStateless = TRUE;\r
+        }\r
+        IsDispatched  = TRUE;\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (IsDispatched) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Skip this packet if not dispatched to any instance.\r
+  //\r
+  if (!IsDispatched) {\r
+    goto ON_CONTINUE;\r
+  }\r
+\r
+  //\r
+  // Dispatch the received packet ot the right instance.\r
+  //\r
+  if (IsStateless) {\r
+    Dhcp6HandleStateless (Instance, Packet);\r
+  } else {\r
+    Dhcp6HandleStateful (Instance, Packet);\r
+  }\r
+\r
+ON_CONTINUE:\r
+\r
+  NetbufFree (Udp6Wrap);\r
+\r
+  if (Packet != NULL) {\r
+    FreePool (Packet);\r
+  }\r
+}\r
+\r
+/**\r
+  Detect Link movement for specified network device.\r
+\r
+  This routine will try to invoke Snp->GetStatus() to get the media status.\r
+  If media present status switches from unpresent to present, a link movement\r
+  is detected. Note that the underlying UNDI driver may not support reporting\r
+  media status from GET_STATUS command. If that, fail to detect link movement.\r
+\r
+  @param[in]  Instance       The pointer to DHCP6_INSTANCE.\r
+\r
+  @retval     TRUE           A link movement is detected.\r
+  @retval     FALSE          A link movement is not detected.\r
+\r
+**/\r
+BOOLEAN\r
+Dhcp6LinkMovDetect (\r
+  IN  DHCP6_INSTANCE            *Instance\r
+  )\r
+{\r
+  UINT32                       InterruptStatus;\r
+  BOOLEAN                      MediaPresent;\r
+  EFI_STATUS                   Status;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL  *Snp;\r
+\r
+  ASSERT (Instance != NULL);\r
+  Snp = Instance->Service->Snp;\r
+  MediaPresent = Instance->MediaPresent;\r
+\r
+  //\r
+  // Check whether SNP support media detection\r
+  //\r
+  if (!Snp->Mode->MediaPresentSupported) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data\r
+  //\r
+  Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  Instance->MediaPresent = Snp->Mode->MediaPresent;\r
+  //\r
+  // Media transimit Unpresent to Present means new link movement is detected.\r
+  //\r
+  if (!MediaPresent && Instance->MediaPresent) {\r
+    return TRUE;\r
+  }\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  The timer routine of the Dhcp6 instance for each second.\r
+\r
+  @param[in]  Event           The timer event.\r
+  @param[in]  Context         The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6OnTimerTick (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *NextEntry;\r
+  DHCP6_INSTANCE            *Instance;\r
+  DHCP6_TX_CB               *TxCb;\r
+  DHCP6_IA_CB               *IaCb;\r
+  UINT32                    LossTime;\r
+\r
+  ASSERT (Context != NULL);\r
+\r
+  Instance = (DHCP6_INSTANCE *) Context;\r
+\r
+  //\r
+  // 1. Loop the tx list, count live time of every tx packet to check whether\r
+  //    need re-transmit or not.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {\r
+\r
+    TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);\r
+\r
+    TxCb->TickTime++;\r
+\r
+    if (TxCb->TickTime > TxCb->RetryExp) {\r
+      //\r
+      // Handle the first rt in the transmission of solicit specially.\r
+      //\r
+      if (TxCb->RetryCnt == 0 && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {\r
+        if (Instance->AdSelect == NULL) {\r
+          //\r
+          // Set adpref as 0xff here to indicate select any advertisement\r
+          // afterwards.\r
+          //\r
+          Instance->AdPref = 0xff;\r
+        } else {\r
+          //\r
+          // Select the advertisement received before.\r
+          //\r
+          Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);\r
+          return;\r
+        }\r
+      }\r
+      //\r
+      // Increase the retry count for the packet and add up the total loss time.\r
+      //\r
+      TxCb->RetryCnt++;\r
+      TxCb->RetryLos += TxCb->RetryExp;\r
+\r
+      //\r
+      // Check whether overflow the max retry count limit for this packet\r
+      //\r
+      if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) {\r
+        goto ON_CLOSE;\r
+      }\r
+\r
+      //\r
+      // Check whether overflow the max retry duration for this packet\r
+      //\r
+      if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) {\r
+        goto ON_CLOSE;\r
+      }\r
+\r
+      //\r
+      // Re-calculate retry expire timeout for the next time.\r
+      //\r
+      // Firstly, Check the new calculated time whether overflow the max retry\r
+      // expire time.\r
+      //\r
+      TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+                         TxCb->RetryExp,\r
+                         FALSE,\r
+                         TRUE\r
+                         );\r
+\r
+      if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) {\r
+        TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+                           TxCb->RetryCtl.Mrt,\r
+                           TRUE,\r
+                           TRUE\r
+                           );\r
+      }\r
+\r
+      //\r
+      // Secondly, Check the new calculated time whether overflow the max retry\r
+      // duration time.\r
+      //\r
+      LossTime = TxCb->RetryLos + TxCb->RetryExp;\r
+      if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) {\r
+        TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos;\r
+      }\r
+\r
+      //\r
+      // Reset the tick time for the next retransmission\r
+      //\r
+      TxCb->TickTime = 0;\r
+\r
+      //\r
+      // Retransmit the last sent packet again.\r
+      //\r
+      Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed);\r
+    }\r
+  }\r
+\r
+  //\r
+  // 2. Check the configured Ia, count lease time of every valid Ia to check\r
+  // whether need to renew or rebind this Ia.\r
+  //\r
+  IaCb = &Instance->IaCb;\r
+\r
+  if (Instance->Config == NULL || IaCb->Ia == NULL) {\r
+    return;\r
+  }\r
+\r
+  if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) {\r
+\r
+    IaCb->LeaseTime++;\r
+\r
+    if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) {\r
+      //\r
+      // Exceed t2, send rebind packet to extend the Ia lease.\r
+      //\r
+      Dhcp6SendRenewRebindMsg (Instance, TRUE);\r
+\r
+    } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) {\r
+\r
+      //\r
+      // Exceed t1, send renew packet to extend the Ia lease.\r
+      //\r
+      Dhcp6SendRenewRebindMsg (Instance, FALSE);\r
+    }\r
+  }\r
+\r
+  //\r
+  // 3. In any situation when a client may have moved to a new link, the\r
+  //    client MUST initiate a Confirm/Reply message exchange.\r
+  //\r
+  if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) {\r
+    Dhcp6SendConfirmMsg (Instance);\r
+  }\r
+\r
+  return;\r
+\r
+ ON_CLOSE:\r
+\r
+  if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest ||\r
+      TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew       ||\r
+      TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm\r
+      ) {\r
+    //\r
+    // The failure of renew/Confirm will still switch to the bound state.\r
+    //\r
+    if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) ||\r
+        (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) {\r
+      ASSERT (Instance->IaCb.Ia);\r
+      Instance->IaCb.Ia->State = Dhcp6Bound;\r
+    }\r
+    //\r
+    // The failure of info-request will return no response.\r
+    //\r
+    if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {\r
+      Instance->UdpSts = EFI_NO_RESPONSE;\r
+    }\r
+    Dhcp6DequeueRetry (\r
+      Instance,\r
+      TxCb->Xid,\r
+      TRUE\r
+      );\r
+  } else {\r
+    //\r
+    // The failure of the others will terminate current state machine if timeout.\r
+    //\r
+    Dhcp6CleanupSession (Instance, EFI_NO_RESPONSE);\r
+  }\r
+}\r
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h
new file mode 100644 (file)
index 0000000..31459c9
--- /dev/null
@@ -0,0 +1,193 @@
+/** @file\r
+  Dhcp6 internal functions declaration.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_DHCP6_IO_H__\r
+#define __EFI_DHCP6_IO_H__\r
+\r
+\r
+/**\r
+  Clean up the specific nodes in the retry list.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  Scope           The scope of cleanup nodes.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupRetry (\r
+  IN DHCP6_INSTANCE         *Instance,\r
+  IN UINT32                 Scope\r
+  );\r
+\r
+/**\r
+  Clean up the session of the instance stateful exchange.\r
+\r
+  @param[in, out]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]       Status          The return status from udp.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupSession (\r
+  IN OUT DHCP6_INSTANCE          *Instance,\r
+  IN     EFI_STATUS              Status\r
+  );\r
+\r
+/**\r
+  Create the solicit message and send it.\r
+\r
+  @param[in]  Instance        The pointer to Dhcp6 instance.\r
+\r
+  @retval EFI_SUCCESS           Create and send the solicit message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval Others                Failed to send the solicit message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendSolicitMsg (\r
+  IN DHCP6_INSTANCE         *Instance\r
+  );\r
+\r
+/**\r
+  Create the request message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+\r
+  @retval EFI_SUCCESS           Create and send the request message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to send the request message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendRequestMsg (\r
+  IN DHCP6_INSTANCE         *Instance\r
+  );\r
+\r
+/**\r
+  Create the renew/rebind message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  RebindRequest   If TRUE, it is a Rebind type message.\r
+                              Otherwise, it is a Renew type message.\r
+\r
+  @retval EFI_SUCCESS           Create and send the renew/rebind message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to send the renew/rebind message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendRenewRebindMsg (\r
+  IN DHCP6_INSTANCE         *Instance,\r
+  IN BOOLEAN                RebindRequest\r
+  );\r
+\r
+/**\r
+  Create the decline message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  DecIa           The pointer to the decline Ia.\r
+\r
+  @retval EFI_SUCCESS           Create and send the decline message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to send the decline message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendDeclineMsg (\r
+  IN DHCP6_INSTANCE            *Instance,\r
+  IN EFI_DHCP6_IA              *DecIa\r
+  );\r
+\r
+/**\r
+  Create the release message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  RelIa           The pointer to the release Ia.\r
+\r
+  @retval EFI_SUCCESS           Create and send the release message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected error.\r
+  @retval Others                Failed to send the release message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendReleaseMsg (\r
+  IN DHCP6_INSTANCE            *Instance,\r
+  IN EFI_DHCP6_IA              *RelIa\r
+  );\r
+\r
+/**\r
+  Create the information request message and send it.\r
+\r
+  @param[in]  Instance        The pointer to the Dhcp6 instance.\r
+  @param[in]  InfCb           The pointer to the information request control block.\r
+  @param[in]  SendClientId    If TRUE, the client identifier option will be included in\r
+                              information request message. Otherwise, the client identifier\r
+                              option will not be included.\r
+  @param[in]  OptionRequest   The pointer to the option request option.\r
+  @param[in]  OptionCount     The number options in the OptionList.\r
+  @param[in]  OptionList      The array pointers to the appended options.\r
+  @param[in]  Retransmission  The pointer to the retransmission control.\r
+\r
+  @retval EFI_SUCCESS           Create and send the info-request message successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval Others                Failed to send the info-request message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendInfoRequestMsg (\r
+  IN DHCP6_INSTANCE            *Instance,\r
+  IN DHCP6_INF_CB              *InfCb,\r
+  IN BOOLEAN                   SendClientId,\r
+  IN EFI_DHCP6_PACKET_OPTION   *OptionRequest,\r
+  IN UINT32                    OptionCount,\r
+  IN EFI_DHCP6_PACKET_OPTION   *OptionList[],\r
+  IN EFI_DHCP6_RETRANSMISSION  *Retransmission\r
+  );\r
+\r
+/**\r
+  The receive callback function for the Dhcp6 exchange process.\r
+\r
+  @param[in]  Udp6Wrap        The pointer to the received net buffer.\r
+  @param[in]  EndPoint        The pointer to the udp end point.\r
+  @param[in]  IoStatus        The return status from udp io.\r
+  @param[in]  Context         The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6ReceivePacket (\r
+  IN NET_BUF                *Udp6Wrap,\r
+  IN UDP_END_POINT          *EndPoint,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+/**\r
+  The timer routine of the Dhcp6 instance for each second.\r
+\r
+  @param[in]  Event           The timer event.\r
+  @param[in]  Context         The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6OnTimerTick (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c
new file mode 100644 (file)
index 0000000..be7a985
--- /dev/null
@@ -0,0 +1,1146 @@
+/** @file\r
+  Dhcp6 support functions implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+\r
+/**\r
+  Generate client Duid in the format of Duid-llt.\r
+\r
+  @param[in]  Mode          The pointer to the mode of SNP.\r
+\r
+  @retval     NULL          If it failed to generate a client Id.\r
+  @retval     others        The pointer to the new client id.\r
+\r
+**/\r
+EFI_DHCP6_DUID *\r
+Dhcp6GenerateClientId (\r
+  IN EFI_SIMPLE_NETWORK_MODE   *Mode\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_DHCP6_DUID            *Duid;\r
+  EFI_TIME                  Time;\r
+  UINT32                    Stamp;\r
+\r
+  //\r
+  // Attempt to get client Id from variable to keep it constant.\r
+  // See details in section-9 of rfc-3315.\r
+  //\r
+  Duid = GetVariable (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid);\r
+  if (Duid != NULL) {\r
+    return Duid;\r
+  }\r
+\r
+  //\r
+  // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month.\r
+  //\r
+  gRT->GetTime (&Time, NULL);\r
+  Stamp = (UINT32)\r
+    (\r
+      (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) *\r
+      60 +\r
+      Time.Second\r
+    );\r
+\r
+  //\r
+  //  The format of client identifier option:\r
+  //\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |        OPTION_CLIENTID        |          option-len           |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    .                                                               .\r
+  //    .                              DUID                             .\r
+  //    .                        (variable length)                      .\r
+  //    .                                                               .\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+  //\r
+  //  The format of DUID-LLT:\r
+  //\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |          Duid type (1)        |    hardware type (16 bits)    |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                        time (32 bits)                         |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    .                                                               .\r
+  //    .             link-layer address (variable length)              .\r
+  //    .                                                               .\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  //\r
+  // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes\r
+  //\r
+  Duid = AllocateZeroPool (10 + Mode->HwAddressSize);\r
+  if (Duid == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // sizeof (Duid-type + hardware-type + time) = 8 bytes\r
+  //\r
+  Duid->Length = (UINT16) (Mode->HwAddressSize + 8);\r
+\r
+  //\r
+  // Set the Duid-type, hardware-type, time and copy the hardware address.\r
+  //\r
+  WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeLlt));\r
+  WriteUnaligned16 ((UINT16 *) (Duid->Duid + 2), HTONS (NET_IFTYPE_ETHERNET));\r
+  WriteUnaligned32 ((UINT32 *) (Duid->Duid + 4), HTONL (Stamp));\r
+\r
+  CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize);\r
+\r
+  Status = gRT->SetVariable (\r
+                  L"ClientId",\r
+                  &gEfiDhcp6ServiceBindingProtocolGuid,\r
+                  (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),\r
+                  Duid->Length + 2,\r
+                  (VOID *) Duid\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  return Duid;\r
+}\r
+\r
+\r
+/**\r
+  Copy the Dhcp6 configure data.\r
+\r
+  @param[in]  DstCfg        The pointer to the destination configure data.\r
+  @param[in]  SorCfg        The pointer to the source configure data.\r
+\r
+  @retval EFI_SUCCESS           Copy the content from SorCfg from DstCfg successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CopyConfigData (\r
+  IN EFI_DHCP6_CONFIG_DATA      *DstCfg,\r
+  IN EFI_DHCP6_CONFIG_DATA      *SorCfg\r
+  )\r
+{\r
+  UINTN                     Index;\r
+  UINTN                     OptionListSize;\r
+  UINTN                     OptionSize;\r
+\r
+  CopyMem (DstCfg, SorCfg, sizeof (EFI_DHCP6_CONFIG_DATA));\r
+\r
+  //\r
+  // Allocate another buffer for solicitretransmission, and copy it.\r
+  //\r
+  if (SorCfg->SolicitRetransmission != NULL) {\r
+\r
+    DstCfg->SolicitRetransmission = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));\r
+\r
+    if (DstCfg->SolicitRetransmission == NULL) {\r
+      //\r
+      // Error will be handled out of this function.\r
+      //\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    CopyMem (\r
+      DstCfg->SolicitRetransmission,\r
+      SorCfg->SolicitRetransmission,\r
+      sizeof (EFI_DHCP6_RETRANSMISSION)\r
+      );\r
+  }\r
+\r
+  if (SorCfg->OptionList != NULL && SorCfg->OptionCount != 0) {\r
+\r
+    OptionListSize     = SorCfg->OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *);\r
+    DstCfg->OptionList = AllocateZeroPool (OptionListSize);\r
+\r
+    if (DstCfg->OptionList == NULL) {\r
+      //\r
+      // Error will be handled out of this function.\r
+      //\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    for (Index = 0; Index < SorCfg->OptionCount; Index++) {\r
+\r
+      OptionSize                = NTOHS (SorCfg->OptionList[Index]->OpLen) + 4;\r
+      DstCfg->OptionList[Index] = AllocateZeroPool (OptionSize);\r
+\r
+      if (DstCfg->OptionList[Index] == NULL) {\r
+        //\r
+        // Error will be handled out of this function.\r
+        //\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      CopyMem (\r
+        DstCfg->OptionList[Index],\r
+        SorCfg->OptionList[Index],\r
+        OptionSize\r
+        );\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Clean up the configure data.\r
+\r
+  @param[in, out]  CfgData       The pointer to the configure data.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupConfigData (\r
+  IN OUT EFI_DHCP6_CONFIG_DATA       *CfgData\r
+  )\r
+{\r
+  UINTN                          Index;\r
+\r
+  ASSERT (CfgData != NULL);\r
+  //\r
+  // Clean up all fields in config data including the reference buffers, but do\r
+  // not free the config data buffer itself.\r
+  //\r
+  if (CfgData->OptionList != NULL) {\r
+    for (Index = 0; Index < CfgData->OptionCount; Index++) {\r
+      if (CfgData->OptionList[Index] != NULL) {\r
+        FreePool (CfgData->OptionList[Index]);\r
+      }\r
+    }\r
+    FreePool (CfgData->OptionList);\r
+  }\r
+\r
+  if (CfgData->SolicitRetransmission != NULL) {\r
+    FreePool (CfgData->SolicitRetransmission);\r
+  }\r
+\r
+  ZeroMem (CfgData, sizeof (EFI_DHCP6_CONFIG_DATA));\r
+}\r
+\r
+\r
+/**\r
+  Clean up the mode data.\r
+\r
+  @param[in, out]  ModeData      The pointer to the mode data.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupModeData (\r
+  IN OUT EFI_DHCP6_MODE_DATA        *ModeData\r
+  )\r
+{\r
+  ASSERT (ModeData != NULL);\r
+  //\r
+  // Clean up all fields in mode data including the reference buffers, but do\r
+  // not free the mode data buffer itself.\r
+  //\r
+  if (ModeData->ClientId != NULL) {\r
+    FreePool (ModeData->ClientId);\r
+  }\r
+\r
+  if (ModeData->Ia != NULL) {\r
+\r
+    if (ModeData->Ia->ReplyPacket != NULL) {\r
+      FreePool (ModeData->Ia->ReplyPacket);\r
+    }\r
+    FreePool (ModeData->Ia);\r
+  }\r
+\r
+  ZeroMem (ModeData, sizeof (EFI_DHCP6_MODE_DATA));\r
+}\r
+\r
+\r
+/**\r
+  Calculate the expire time by the algorithm defined in rfc.\r
+\r
+  @param[in]  Base          The base value of the time.\r
+  @param[in]  IsFirstRt     If TRUE, it is the first time to calculate expire time.\r
+  @param[in]  NeedSigned    If TRUE, the the signed factor is needed.\r
+\r
+  @return     Expire        The calculated result for the new expire time.\r
+\r
+**/\r
+UINT32\r
+Dhcp6CalculateExpireTime (\r
+  IN UINT32                 Base,\r
+  IN BOOLEAN                IsFirstRt,\r
+  IN BOOLEAN                NeedSigned\r
+  )\r
+{\r
+  EFI_TIME                  Time;\r
+  BOOLEAN                   Signed;\r
+  UINT32                    Seed;\r
+  UINT32                    Expire;\r
+\r
+  //\r
+  // Take the 10bits of microsecond in system time as a uniform distribution.\r
+  // Take the 10th bit as a flag to determine it's signed or not.\r
+  //\r
+  gRT->GetTime (&Time, NULL);\r
+  Seed   = ((Time.Nanosecond >> 10) & DHCP6_10_BIT_MASK);\r
+  Signed = (BOOLEAN) ((((Time.Nanosecond >> 9) & 0x01) != 0) ? TRUE : FALSE);\r
+  Signed = (BOOLEAN) (NeedSigned ? Signed : FALSE);\r
+\r
+  //\r
+  // Calculate expire by the following algo:\r
+  //   1. base + base * (-0.1 ~ 0) for the first solicit\r
+  //   2. base + base * (-0.1 ~ 0.1) for the first other messages\r
+  //   3. 2 * base + base * (-0.1 ~ 0.1) for the subsequent all messages\r
+  //   4. base + base * (-0.1 ~ 0) for the more than mrt timeout\r
+  //\r
+  // The (Seed / 0x3ff / 10) is used to a random range (0, 0.1).\r
+  //\r
+  if (IsFirstRt && Signed) {\r
+\r
+    Expire = Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);\r
+\r
+  } else if (IsFirstRt && !Signed) {\r
+\r
+    Expire = Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);\r
+\r
+  } else if (!IsFirstRt && Signed) {\r
+\r
+    Expire = 2 * Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);\r
+\r
+  } else {\r
+\r
+    Expire = 2 * Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);\r
+  }\r
+\r
+  Expire = (Expire != 0) ? Expire : 1;\r
+\r
+  return Expire;\r
+}\r
+\r
+\r
+/**\r
+  Calculate the lease time by the algorithm defined in rfc.\r
+\r
+  @param[in]  IaCb          The pointer to the Ia control block.\r
+\r
+**/\r
+VOID\r
+Dhcp6CalculateLeaseTime (\r
+  IN DHCP6_IA_CB              *IaCb\r
+  )\r
+{\r
+  EFI_DHCP6_IA_ADDRESS        *IaAddr;\r
+  UINT32                      MinLt;\r
+  UINT32                      MaxLt;\r
+  UINTN                       Index;\r
+\r
+  ASSERT (IaCb->Ia->IaAddressCount > 0);\r
+\r
+  MinLt    = (UINT32) (-1);\r
+  MaxLt    = 0;\r
+\r
+  //\r
+  // Calculate minlt as min of all valid life time, and maxlt as max of all\r
+  // valid life time.\r
+  //\r
+  for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) {\r
+    IaAddr = IaCb->Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+    MinLt  = MIN (MinLt, IaAddr->ValidLifetime);\r
+    MaxLt  = MAX (MinLt, IaAddr->ValidLifetime);\r
+  }\r
+\r
+  //\r
+  // Take 50% minlt as t1, and 80% maxlt as t2 if Dhcp6 server doesn't offer\r
+  // such information.\r
+  //\r
+  IaCb->T1            = (IaCb->T1 != 0) ? IaCb->T1 : (UINT32)(MinLt * 5 / 10);\r
+  IaCb->T2            = (IaCb->T2 != 0) ? IaCb->T2 : (UINT32)(MinLt * 8 / 10);\r
+  IaCb->AllExpireTime = MaxLt;\r
+  IaCb->LeaseTime     = 0;\r
+}\r
+\r
+\r
+/**\r
+  Check whether the addresses are all included by the configured Ia.\r
+\r
+  @param[in]  Ia            The pointer to the Ia.\r
+  @param[in]  AddressCount  The number of addresses.\r
+  @param[in]  Addresses     The pointer to the addresses buffer.\r
+\r
+  @retval EFI_SUCCESS         The addresses are all included by the configured IA.\r
+  @retval EFI_NOT_FOUND       The addresses are not included by the configured IA.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CheckAddress (\r
+  IN EFI_DHCP6_IA             *Ia,\r
+  IN UINT32                   AddressCount,\r
+  IN EFI_IPv6_ADDRESS         *Addresses\r
+  )\r
+{\r
+  UINTN                       Index1;\r
+  UINTN                       Index2;\r
+  BOOLEAN                     Found;\r
+\r
+  //\r
+  // Check whether the addresses are all included by the configured IA. And it\r
+  // will return success if address count is zero, which means all addresses.\r
+  //\r
+  for (Index1 = 0; Index1 < AddressCount; Index1++) {\r
+\r
+    Found = FALSE;\r
+\r
+    for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {\r
+\r
+      if (CompareMem (\r
+            &Addresses[Index1],\r
+            &Ia->IaAddress[Index2],\r
+            sizeof (EFI_IPv6_ADDRESS)\r
+            ) == 0) {\r
+\r
+        Found = TRUE;\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (!Found) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Deprive the addresses from current Ia, and generate another eliminated Ia.\r
+\r
+  @param[in]  Ia            The pointer to the Ia.\r
+  @param[in]  AddressCount  The number of addresses.\r
+  @param[in]  Addresses     The pointer to the addresses buffer.\r
+\r
+  @retval     NULL          If it failed to generate the deprived Ia.\r
+  @retval     others        The pointer to the deprived Ia.\r
+\r
+**/\r
+EFI_DHCP6_IA *\r
+Dhcp6DepriveAddress (\r
+  IN EFI_DHCP6_IA             *Ia,\r
+  IN UINT32                   AddressCount,\r
+  IN EFI_IPv6_ADDRESS         *Addresses\r
+  )\r
+{\r
+  EFI_DHCP6_IA                *IaCopy;\r
+  UINTN                       IaCopySize;\r
+  UINTN                       Index1;\r
+  UINTN                       Index2;\r
+  BOOLEAN                     Found;\r
+\r
+  if (AddressCount == 0) {\r
+    //\r
+    // It means release all Ia addresses if address count is zero.\r
+    //\r
+    AddressCount = Ia->IaAddressCount;\r
+  }\r
+\r
+  ASSERT (AddressCount != 0);\r
+\r
+  IaCopySize = sizeof (EFI_DHCP6_IA) + (AddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+  IaCopy     = AllocateZeroPool (IaCopySize);\r
+\r
+  if (IaCopy == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  if (AddressCount == Ia->IaAddressCount) {\r
+    //\r
+    // If release all Ia addresses, just copy the configured Ia and then set\r
+    // its address count as zero.\r
+    // We may decline/release part of addresses at the begining. So it's a\r
+    // forwarding step to update address infor for decline/release, while the\r
+    // other infor such as Ia state will be updated when receiving reply.\r
+    //\r
+    CopyMem (IaCopy, Ia, IaCopySize);\r
+    Ia->IaAddressCount = 0;\r
+    return IaCopy;\r
+  }\r
+\r
+  CopyMem (IaCopy, Ia, sizeof (EFI_DHCP6_IA));\r
+\r
+  //\r
+  // Move the addresses from the Ia of instance to the deprived Ia.\r
+  //\r
+  for (Index1 = 0; Index1 < AddressCount; Index1++) {\r
+\r
+    Found = FALSE;\r
+\r
+    for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {\r
+\r
+      if (CompareMem (\r
+            &Addresses[Index1],\r
+            &Ia->IaAddress[Index2],\r
+            sizeof (EFI_IPv6_ADDRESS)\r
+            ) == 0) {\r
+        //\r
+        // Copy the deprived address to the copy of Ia\r
+        //\r
+        CopyMem (\r
+          &IaCopy->IaAddress[Index1],\r
+          &Ia->IaAddress[Index2],\r
+          sizeof (EFI_DHCP6_IA_ADDRESS)\r
+          );\r
+        //\r
+        // Delete the deprived address from the instance Ia\r
+        //\r
+        if (Index2 + 1 < Ia->IaAddressCount) {\r
+          CopyMem (\r
+            &Ia->IaAddress[Index2],\r
+            &Ia->IaAddress[Index2 + 1],\r
+            (Ia->IaAddressCount - Index2 - 1) * sizeof (EFI_DHCP6_IA_ADDRESS)\r
+            );\r
+        }\r
+        Found = TRUE;\r
+        break;\r
+      }\r
+    }\r
+    ASSERT (Found == TRUE);\r
+  }\r
+\r
+  Ia->IaAddressCount    -= AddressCount;\r
+  IaCopy->IaAddressCount = AddressCount;\r
+\r
+  return IaCopy;\r
+}\r
+\r
+\r
+/**\r
+  The dummy ext buffer free callback routine.\r
+\r
+  @param[in]  Arg           The pointer to the parameter.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6DummyExtFree (\r
+  IN VOID                      *Arg\r
+  )\r
+{\r
+}\r
+\r
+\r
+/**\r
+  The callback routine once message transmitted.\r
+\r
+  @param[in]  Udp6Wrap      The pointer to the received net buffer.\r
+  @param[in]  EndPoint      The pointer to the udp end point.\r
+  @param[in]  IoStatus      The return status from udp io.\r
+  @param[in]  Context       The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6OnTransmitted (\r
+  IN NET_BUF                   *Wrap,\r
+  IN UDP_END_POINT             *EndPoint,\r
+  IN EFI_STATUS                IoStatus,\r
+  IN VOID                      *Context\r
+  )\r
+{\r
+  NetbufFree (Wrap);\r
+}\r
+\r
+\r
+/**\r
+  Append the option to Buf, and move Buf to the end.\r
+\r
+  @param[in, out] Buf           The pointer to the buffer.\r
+  @param[in]      OptType       The option type.\r
+  @param[in]      OptLen        The length of option contents.\r
+  @param[in]      Data          The pointer to the option content.\r
+\r
+  @return         Buf           The position to append the next option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendOption (\r
+  IN OUT UINT8               *Buf,\r
+  IN     UINT16              OptType,\r
+  IN     UINT16              OptLen,\r
+  IN     UINT8               *Data\r
+  )\r
+{\r
+  //\r
+  //  The format of Dhcp6 option:\r
+  //\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |          option-code          |   option-len (option data)    |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                          option-data                          |\r
+  //    |                      (option-len octets)                      |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  ASSERT (OptLen != 0);\r
+\r
+  WriteUnaligned16 ((UINT16 *) Buf, OptType);\r
+  Buf            += 2;\r
+  WriteUnaligned16 ((UINT16 *) Buf, OptLen);\r
+  Buf            += 2;\r
+  CopyMem (Buf, Data, NTOHS (OptLen));\r
+  Buf            += NTOHS (OptLen);\r
+\r
+  return Buf;\r
+}\r
+\r
+\r
+/**\r
+  Append the appointed Ia option to Buf, and move Buf to the end.\r
+\r
+  @param[in, out] Buf           The pointer to the position to append.\r
+  @param[in]      Ia            The pointer to the Ia.\r
+  @param[in]      T1            The time of T1.\r
+  @param[in]      T2            The time of T2.\r
+\r
+  @return         Buf           The position to append the next Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendIaOption (\r
+  IN OUT UINT8                  *Buf,\r
+  IN     EFI_DHCP6_IA           *Ia,\r
+  IN     UINT32                 T1,\r
+  IN     UINT32                 T2\r
+  )\r
+{\r
+  UINT8                     *AddrOpt;\r
+  UINT16                    *Len;\r
+  UINTN                     Index;\r
+  UINT16                    Length;\r
+\r
+  //\r
+  //  The format of IA_NA and IA_TA option:\r
+  //\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |          OPTION_IA_NA         |          option-len           |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                        IAID (4 octets)                        |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                        T1 (only for IA_NA)                    |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                        T2 (only for IA_NA)                    |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                                                               |\r
+  //    .                  IA_NA-options/IA_TA-options                  .\r
+  //    .                                                               .\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  //\r
+  // Fill the value of Ia option type\r
+  //\r
+  WriteUnaligned16 ((UINT16 *) Buf, HTONS (Ia->Descriptor.Type));\r
+  Buf                     += 2;\r
+\r
+  //\r
+  // Fill the len of Ia option later, keep the pointer first\r
+  //\r
+  Len                      = (UINT16 *) Buf;\r
+  Buf                     += 2;\r
+\r
+  //\r
+  // Fill the value of iaid\r
+  //\r
+  WriteUnaligned32 ((UINT32 *) Buf, HTONL (Ia->Descriptor.IaId));\r
+  Buf                     += 4;\r
+\r
+  //\r
+  // Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified.\r
+  //\r
+  if (Ia->Descriptor.Type == Dhcp6OptIana) {\r
+    WriteUnaligned32 ((UINT32 *) Buf, ((T1 != 0) ? T1 : 0xffffffff));\r
+    Buf                   += 4;\r
+    WriteUnaligned32 ((UINT32 *) Buf, ((T2 != 0) ? T2 : 0xffffffff));\r
+    Buf                   += 4;\r
+  }\r
+\r
+  //\r
+  // Fill all the addresses belong to the Ia\r
+  //\r
+  for (Index = 0; Index < Ia->IaAddressCount; Index++) {\r
+\r
+     AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+     Length  = HTONS ((UINT16) sizeof (EFI_DHCP6_IA_ADDRESS));\r
+     Buf     = Dhcp6AppendOption (\r
+                 Buf,\r
+                 HTONS (Dhcp6OptIaAddr),\r
+                 Length,\r
+                 AddrOpt\r
+                 );\r
+  }\r
+\r
+  //\r
+  // Fill the value of Ia option length\r
+  //\r
+  *Len = HTONS ((UINT16) (Buf - (UINT8 *) Len - 2));\r
+\r
+  return Buf;\r
+}\r
+\r
+/**\r
+  Append the appointed Elapsed time option to Buf, and move Buf to the end.\r
+\r
+  @param[in, out] Buf           The pointer to the position to append.\r
+  @param[in]      Instance      The pointer to the Dhcp6 instance.\r
+  @param[out]     Elapsed       The pointer to the elapsed time value in\r
+                                  the generated packet.\r
+\r
+  @return         Buf           The position to append the next Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendETOption (\r
+  IN OUT UINT8                  *Buf,\r
+  IN     DHCP6_INSTANCE         *Instance,\r
+  OUT    UINT16                 **Elapsed\r
+  )\r
+{\r
+  //\r
+  //  The format of elapsed time option:\r
+  //\r
+  //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //  |      OPTION_ELAPSED_TIME      |           option-len          |\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //  |          elapsed-time         |\r
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  //\r
+  // Fill the value of elapsed-time option type.\r
+  //\r
+  WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptElapsedTime));\r
+  Buf                     += 2;\r
+\r
+  //\r
+  // Fill the len of elapsed-time option, which is fixed.\r
+  //\r
+  WriteUnaligned16 ((UINT16 *) Buf, HTONS(2));\r
+  Buf                     += 2;\r
+\r
+  //\r
+  // Fill in elapsed time value with 0 value for now.  The actual value is\r
+  // filled in later just before the packet is transmitted.\r
+  //\r
+  WriteUnaligned16 ((UINT16 *) Buf, HTONS(0));\r
+  *Elapsed                  = (UINT16 *) Buf;\r
+  Buf                     += 2;\r
+\r
+  return Buf;\r
+}\r
+\r
+/**\r
+  Set the elapsed time based on the given instance and the pointer to the\r
+  elapsed time option.\r
+\r
+  @param[in]      Elapsed       The pointer to the position to append.\r
+  @param[in]      Instance      The pointer to the Dhcp6 instance.\r
+\r
+**/\r
+VOID\r
+SetElapsedTime (\r
+  IN     UINT16                 *Elapsed,\r
+  IN     DHCP6_INSTANCE         *Instance\r
+  )\r
+{\r
+  EFI_TIME          Time;\r
+  UINT64            CurrentStamp;\r
+  UINT64            ElapsedTimeValue;\r
+\r
+  //\r
+  // Generate a time stamp of the centiseconds from 2000/1/1, assume 30day/month.\r
+  //\r
+  gRT->GetTime (&Time, NULL);\r
+  CurrentStamp = (UINT64)\r
+    (\r
+      ((((((Time.Year - 2000) * 360 +\r
+       (Time.Month - 1)) * 30 +\r
+       (Time.Day - 1)) * 24 + Time.Hour) * 60 +\r
+       Time.Minute) * 60 + Time.Second) * 100\r
+       + DivU64x32(Time.Nanosecond, 10000000)\r
+    );\r
+\r
+  //\r
+  // Sentinel value of 0 means that this is the first DHCP packet that we are\r
+  // sending and that we need to initialize the value.  First DHCP Solicit\r
+  // gets 0 elapsed-time.  Otherwise, calculate based on StartTime.\r
+  //\r
+  if (Instance->StartTime == 0) {\r
+    ElapsedTimeValue = 0;\r
+    Instance->StartTime = CurrentStamp;\r
+  } else {\r
+    ElapsedTimeValue = CurrentStamp - Instance->StartTime;\r
+\r
+    //\r
+    // If elapsed time cannot fit in two bytes, set it to 0xffff.\r
+    //\r
+    if (ElapsedTimeValue > 0xffff) {\r
+      ElapsedTimeValue = 0xffff;\r
+    }\r
+  }\r
+  WriteUnaligned16 (Elapsed, HTONS((UINT16) ElapsedTimeValue));\r
+}\r
+\r
+\r
+/**\r
+  Seek the address of the first byte of the option header.\r
+\r
+  @param[in]  Buf           The pointer to the buffer.\r
+  @param[in]  SeekLen       The length to seek.\r
+  @param[in]  OptType       The option type.\r
+\r
+  @retval     NULL          If it failed to seek the option.\r
+  @retval     others        The position to the option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6SeekOption (\r
+  IN UINT8           *Buf,\r
+  IN UINT32          SeekLen,\r
+  IN UINT16          OptType\r
+  )\r
+{\r
+  UINT8              *Cursor;\r
+  UINT8              *Option;\r
+  UINT16             DataLen;\r
+  UINT16             OpCode;\r
+\r
+  Option = NULL;\r
+  Cursor = Buf;\r
+\r
+  //\r
+  // The format of Dhcp6 option refers to Dhcp6AppendOption().\r
+  //\r
+  while (Cursor < Buf + SeekLen) {\r
+    OpCode = ReadUnaligned16 ((UINT16 *) Cursor);\r
+    if (OpCode == HTONS (OptType)) {\r
+      Option = Cursor;\r
+      break;\r
+    }\r
+    DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));\r
+    Cursor += (DataLen + 4);\r
+  }\r
+\r
+  return Option;\r
+}\r
+\r
+\r
+/**\r
+  Seek the address of the first byte of the Ia option header.\r
+\r
+  @param[in]  Buf           The pointer to the buffer.\r
+  @param[in]  SeekLen       The length to seek.\r
+  @param[in]  IaDesc        The pointer to the Ia descriptor.\r
+\r
+  @retval     NULL          If it failed to seek the Ia option.\r
+  @retval     others        The position to the Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6SeekIaOption (\r
+  IN UINT8                    *Buf,\r
+  IN UINT32                   SeekLen,\r
+  IN EFI_DHCP6_IA_DESCRIPTOR  *IaDesc\r
+  )\r
+{\r
+  UINT8              *Cursor;\r
+  UINT8              *Option;\r
+  UINT16             DataLen;\r
+  UINT16             OpCode;\r
+  UINT32             IaId;\r
+\r
+  //\r
+  // The format of IA_NA and IA_TA option refers to Dhcp6AppendIaOption().\r
+  //\r
+  Option = NULL;\r
+  Cursor = Buf;\r
+\r
+  while (Cursor < Buf + SeekLen) {\r
+    OpCode = ReadUnaligned16 ((UINT16 *) Cursor);\r
+    IaId   = ReadUnaligned32 ((UINT32 *) (Cursor + 4));\r
+    if (OpCode == HTONS (IaDesc->Type) && IaId == HTONL (IaDesc->IaId)) {\r
+      Option = Cursor;\r
+      break;\r
+    }\r
+    DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));\r
+    Cursor += (DataLen + 4);\r
+  }\r
+\r
+  return Option;\r
+}\r
+\r
+\r
+/**\r
+  Parse the address option and update the address infomation.\r
+\r
+  @param[in]      IaInnerOpt    The pointer to the buffer.\r
+  @param[in]      IaInnerLen    The length to parse.\r
+  @param[out]     AddrNum       The number of addresses.\r
+  @param[in, out] AddrBuf       The pointer to the address buffer.\r
+\r
+**/\r
+VOID\r
+Dhcp6ParseAddrOption (\r
+  IN     UINT8                   *IaInnerOpt,\r
+  IN     UINT16                  IaInnerLen,\r
+     OUT UINT32                  *AddrNum,\r
+  IN OUT EFI_DHCP6_IA_ADDRESS    *AddrBuf\r
+  )\r
+{\r
+  UINT8                       *Cursor;\r
+  UINT16                      DataLen;\r
+  UINT16                      OpCode;\r
+  UINT32                      ValidLt;\r
+\r
+  //\r
+  //  The format of the IA Address option:\r
+  //\r
+  //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |          OPTION_IAADDR        |          option-len           |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                                                               |\r
+  //    |                         IPv6 address                          |\r
+  //    |                                                               |\r
+  //    |                                                               |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                      preferred-lifetime                       |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    |                        valid-lifetime                         |\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //    .                                                               .\r
+  //    .                        IAaddr-options                         .\r
+  //    .                                                               .\r
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  //\r
+  //  Two usage model:\r
+  //\r
+  //    1. Pass addrbuf == null, to get the addrnum over the Ia inner options.\r
+  //    2. Pass addrbuf != null, to resolve the addresses over the Ia inner\r
+  //       options to the addrbuf.\r
+  //\r
+\r
+  Cursor   = IaInnerOpt;\r
+  *AddrNum = 0;\r
+\r
+  while (Cursor < IaInnerOpt + IaInnerLen) {\r
+    //\r
+    // Count the Ia address option with non-0 valid time.\r
+    //\r
+    OpCode  = ReadUnaligned16 ((UINT16 *) Cursor);\r
+    ValidLt = ReadUnaligned32 ((UINT32 *) (Cursor + 24));\r
+    if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt != 0) {\r
+\r
+      if (AddrBuf != NULL) {\r
+        CopyMem (AddrBuf, Cursor + 4, sizeof (EFI_DHCP6_IA_ADDRESS));\r
+        AddrBuf->PreferredLifetime = NTOHL (AddrBuf->PreferredLifetime);\r
+        AddrBuf->ValidLifetime     = NTOHL (AddrBuf->ValidLifetime);\r
+        AddrBuf = (EFI_DHCP6_IA_ADDRESS *) ((UINT8 *) AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS));\r
+      }\r
+\r
+      (*AddrNum)++;\r
+    }\r
+    DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));\r
+    Cursor += (DataLen + 4);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Create a control blcok for the Ia according to the corresponding options.\r
+\r
+  @param[in]  Instance              The pointer to DHCP6 Instance.\r
+  @param[in]  IaInnerOpt            The pointer to the inner options in the Ia option.\r
+  @param[in]  IaInnerLen            The length of all the inner options in the Ia option.\r
+  @param[in]  T1                    T1 time in the Ia option.\r
+  @param[in]  T2                    T2 time in the Ia option.\r
+\r
+  @retval     EFI_NOT_FOUND         No valid IA option is found.\r
+  @retval     EFI_SUCCESS           Create an IA control block successfully.\r
+  @retval     EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6GenerateIaCb (\r
+  IN  DHCP6_INSTANCE           *Instance,\r
+  IN  UINT8                    *IaInnerOpt,\r
+  IN  UINT16                   IaInnerLen,\r
+  IN  UINT32                   T1,\r
+  IN  UINT32                   T2\r
+  )\r
+{\r
+  UINT32                       AddrNum;\r
+  UINT32                       IaSize;\r
+  EFI_DHCP6_IA                 *Ia;\r
+\r
+  if (Instance->IaCb.Ia == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Calculate the number of addresses for this Ia, excluding the addresses with\r
+  // the value 0 of valid lifetime.\r
+  //\r
+  Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, NULL);\r
+\r
+  if (AddrNum == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Allocate for new IA.\r
+  //\r
+  IaSize = sizeof (EFI_DHCP6_IA) + (AddrNum - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+  Ia = AllocateZeroPool (IaSize);\r
+\r
+  if (Ia == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Fill up this new IA fields.\r
+  //\r
+  Ia->State          = Instance->IaCb.Ia->State;\r
+  Ia->IaAddressCount = AddrNum;\r
+  CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR));\r
+  Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress);\r
+\r
+  //\r
+  // Free original IA resource.\r
+  //\r
+  if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
+    FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+  }\r
+  FreePool (Instance->IaCb.Ia);\r
+\r
+\r
+  ZeroMem (&Instance->IaCb, sizeof (DHCP6_IA_CB));\r
+\r
+  //\r
+  // Update IaCb to use new IA.\r
+  //\r
+  Instance->IaCb.Ia   = Ia;\r
+\r
+  //\r
+\r
+ // Fill in IaCb fields. Such as T1, T2, AllExpireTime and LeaseTime.\r
+  //\r
+  Instance->IaCb.T1 = T1;\r
+  Instance->IaCb.T2 = T2;\r
+  Dhcp6CalculateLeaseTime (&Instance->IaCb);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Cache the current IA configuration information.\r
+\r
+  @param[in] Instance           The pointer to DHCP6 Instance.\r
+\r
+  @retval EFI_SUCCESS           Cache the current IA successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CacheIa (\r
+  IN DHCP6_INSTANCE           *Instance\r
+  )\r
+{\r
+  UINTN                        IaSize;\r
+  EFI_DHCP6_IA                 *Ia;\r
+\r
+  Ia = Instance->IaCb.Ia;\r
+\r
+  if ((Instance->CacheIa == NULL) && (Ia != NULL)) {\r
+    //\r
+    // Cache the current IA.\r
+    //\r
+    IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+\r
+    Instance->CacheIa = AllocateZeroPool (IaSize);\r
+    if (Instance->CacheIa == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    CopyMem (Instance->CacheIa, Ia, IaSize);\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.\r
+\r
+  @param[in]  Instance            The pointer to DHCP6 instance.\r
+\r
+**/\r
+VOID\r
+Dhcp6AppendCacheIa (\r
+  IN DHCP6_INSTANCE           *Instance\r
+  )\r
+{\r
+  UINT8                        *Ptr;\r
+  UINTN                        Index;\r
+  UINTN                        IaSize;\r
+  UINTN                        NewIaSize;\r
+  EFI_DHCP6_IA                 *Ia;\r
+  EFI_DHCP6_IA                 *NewIa;\r
+  EFI_DHCP6_IA                 *CacheIa;\r
+\r
+  Ia      = Instance->IaCb.Ia;\r
+  CacheIa = Instance->CacheIa;\r
+\r
+  if ((CacheIa != NULL) && (CacheIa->IaAddressCount != 0)) {\r
+    //\r
+    // There are old addresses existing. Merge with current addresses.\r
+    //\r
+    NewIaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount + CacheIa->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+    NewIa     = AllocateZeroPool (NewIaSize);\r
+    if (NewIa == NULL) {\r
+      return;\r
+    }\r
+\r
+    IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+    CopyMem (NewIa, Ia, IaSize);\r
+\r
+    //\r
+    // Clear old address.ValidLifetime\r
+    //\r
+    for (Index = 0; Index < CacheIa->IaAddressCount; Index++) {\r
+      CacheIa->IaAddress[Index].ValidLifetime  = 0;\r
+    }\r
+\r
+    NewIa->IaAddressCount += CacheIa->IaAddressCount;\r
+    Ptr   = (UINT8*)&NewIa->IaAddress[Ia->IaAddressCount];\r
+    CopyMem (Ptr, CacheIa->IaAddress, CacheIa->IaAddressCount * sizeof (EFI_DHCP6_IA_ADDRESS));\r
+\r
+    //\r
+    // Migrate to the NewIa and free previous.\r
+    //\r
+    FreePool (Instance->CacheIa);\r
+    FreePool (Instance->IaCb.Ia);\r
+    Instance->CacheIa  = NULL;\r
+    Instance->IaCb.Ia  = NewIa;\r
+  }\r
+}\r
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h
new file mode 100644 (file)
index 0000000..62985a3
--- /dev/null
@@ -0,0 +1,340 @@
+/** @file\r
+  Dhcp6 support functions declaration.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_DHCP6_UTILITY_H__\r
+#define __EFI_DHCP6_UTILITY_H__\r
+\r
+\r
+#define  DHCP6_10_BIT_MASK     0x3ff\r
+\r
+/**\r
+  Generate client Duid in the format of Duid-llt.\r
+\r
+  @param[in]  Mode          The pointer to the mode of SNP.\r
+\r
+  @retval     NULL          if failed to generate client Id.\r
+  @retval     Others        The pointer to the new client id.\r
+\r
+**/\r
+EFI_DHCP6_DUID *\r
+Dhcp6GenerateClientId (\r
+  IN EFI_SIMPLE_NETWORK_MODE    *Mode\r
+  );\r
+\r
+/**\r
+  Copy the Dhcp6 configure data.\r
+\r
+  @param[in]  DstCfg        The pointer to the destination configure data.\r
+  @param[in]  SorCfg        The pointer to the source configure data.\r
+\r
+  @retval EFI_SUCCESS           Copy the content from SorCfg from DstCfg successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CopyConfigData (\r
+  IN EFI_DHCP6_CONFIG_DATA     *DstCfg,\r
+  IN EFI_DHCP6_CONFIG_DATA     *SorCfg\r
+  );\r
+\r
+/**\r
+  Clean up the configure data.\r
+\r
+  @param[in, out]  CfgData       The pointer to the configure data.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupConfigData (\r
+  IN OUT EFI_DHCP6_CONFIG_DATA       *CfgData\r
+  );\r
+\r
+/**\r
+  Clean up the mode data.\r
+\r
+  @param[in, out]  ModeData      The pointer to the mode data.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupModeData (\r
+  IN OUT EFI_DHCP6_MODE_DATA        *ModeData\r
+  );\r
+\r
+/**\r
+  Calculate the expire time by the algorithm defined in rfc.\r
+\r
+  @param[in]  Base          The base value of the time.\r
+  @param[in]  IsFirstRt     If TRUE, it is the first time to calculate expire time.\r
+  @param[in]  NeedSigned    If TRUE, the the signed factor is needed.\r
+\r
+  @return     Expire        The calculated result for the new expire time.\r
+\r
+**/\r
+UINT32\r
+Dhcp6CalculateExpireTime (\r
+  IN UINT32                    Base,\r
+  IN BOOLEAN                   IsFirstRt,\r
+  IN BOOLEAN                   NeedSigned\r
+  );\r
+\r
+/**\r
+  Calculate the lease time by the algorithm defined in rfc.\r
+\r
+  @param[in]  IaCb          The pointer to the Ia control block.\r
+\r
+**/\r
+VOID\r
+Dhcp6CalculateLeaseTime (\r
+  IN DHCP6_IA_CB               *IaCb\r
+  );\r
+\r
+/**\r
+  Check whether the addresses are all included by the configured Ia.\r
+\r
+  @param[in]  Ia            The pointer to the Ia.\r
+  @param[in]  AddressCount  The number of addresses.\r
+  @param[in]  Addresses     The pointer to the addresses buffer.\r
+\r
+  @retval EFI_SUCCESS         The addresses are all included by the configured IA.\r
+  @retval EFI_NOT_FOUND       The addresses are not included by the configured IA.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CheckAddress (\r
+  IN EFI_DHCP6_IA              *Ia,\r
+  IN UINT32                    AddressCount,\r
+  IN EFI_IPv6_ADDRESS          *Addresses\r
+  );\r
+\r
+/**\r
+  Deprive the addresses from current Ia, and generate another eliminated Ia.\r
+\r
+  @param[in]  Ia            The pointer to the Ia.\r
+  @param[in]  AddressCount  The number of addresses.\r
+  @param[in]  Addresses     The pointer to the addresses buffer.\r
+\r
+  @retval     NULL          If failed to generate the deprived Ia.\r
+  @retval     others        The pointer to the deprived Ia.\r
+\r
+**/\r
+EFI_DHCP6_IA *\r
+Dhcp6DepriveAddress (\r
+  IN EFI_DHCP6_IA              *Ia,\r
+  IN UINT32                    AddressCount,\r
+  IN EFI_IPv6_ADDRESS          *Addresses\r
+  );\r
+\r
+/**\r
+  The dummy ext buffer free callback routine.\r
+\r
+  @param[in]  Arg           The pointer to the parameter.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6DummyExtFree (\r
+  IN VOID                      *Arg\r
+  );\r
+\r
+/**\r
+  The callback routine once message transmitted.\r
+\r
+  @param[in]  Udp6Wrap      The pointer to the received net buffer.\r
+  @param[in]  EndPoint      The pointer to the udp end point.\r
+  @param[in]  IoStatus      The return status from udp io.\r
+  @param[in]  Context       The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6OnTransmitted (\r
+  IN NET_BUF                   *Wrap,\r
+  IN UDP_END_POINT             *EndPoint,\r
+  IN EFI_STATUS                IoStatus,\r
+  IN VOID                      *Context\r
+  );\r
+\r
+/**\r
+  Append the appointed option to the buf, and move the buf to the end.\r
+\r
+  @param[in, out] Buf           The pointer to buffer.\r
+  @param[in]      OptType       The option type.\r
+  @param[in]      OptLen        The lenght of option content.s\r
+  @param[in]      Data          The pointer to the option content.\r
+\r
+  @return         Buf           The position to append the next option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendOption (\r
+  IN OUT UINT8                 *Buf,\r
+  IN     UINT16                OptType,\r
+  IN     UINT16                OptLen,\r
+  IN     UINT8                 *Data\r
+  );\r
+\r
+/**\r
+  Append the Ia option to Buf, and move Buf to the end.\r
+\r
+  @param[in, out] Buf           The pointer to the position to append.\r
+  @param[in]      Ia            The pointer to the Ia.\r
+  @param[in]      T1            The time of T1.\r
+  @param[in]      T2            The time of T2.\r
+\r
+  @return         Buf           The position to append the next Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendIaOption (\r
+  IN OUT UINT8                  *Buf,\r
+  IN     EFI_DHCP6_IA           *Ia,\r
+  IN     UINT32                 T1,\r
+  IN     UINT32                 T2\r
+  );\r
+\r
+/**\r
+  Append the appointed Elapsed time option to Buf, and move Buf to the end.\r
+\r
+  @param[in, out] Buf           The pointer to the position to append.\r
+  @param[in]      Instance      The pointer to the Dhcp6 instance.\r
+  @param[out]     Elapsed       The pointer to the elapsed time value in\r
+                                  the generated packet.\r
+\r
+  @return         Buf           The position to append the next Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendETOption (\r
+  IN OUT UINT8                  *Buf,\r
+  IN     DHCP6_INSTANCE         *Instance,\r
+  OUT    UINT16                 **Elapsed\r
+  );\r
+\r
+/**\r
+  Set the elapsed time based on the given instance and the pointer to the\r
+  elapsed time option.\r
+\r
+  @param[in]      Elapsed       The pointer to the position to append.\r
+  @param[in]      Instance      The pointer to the Dhcp6 instance.\r
+**/\r
+VOID\r
+SetElapsedTime (\r
+  IN     UINT16                 *Elapsed,\r
+  IN     DHCP6_INSTANCE         *Instance\r
+  );\r
+\r
+/**\r
+  Seek the address of the first byte of the option header.\r
+\r
+  @param[in]  Buf           The pointer to buffer.\r
+  @param[in]  SeekLen       The length to seek.\r
+  @param[in]  OptType       The option type.\r
+\r
+  @retval     NULL          If failed to seek the option.\r
+  @retval     others        The position to the option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6SeekOption (\r
+  IN UINT8                     *Buf,\r
+  IN UINT32                    SeekLen,\r
+  IN UINT16                    OptType\r
+  );\r
+\r
+/**\r
+  Seek the address of the first byte of the Ia option header.\r
+\r
+  @param[in]  Buf           The pointer to the buffer.\r
+  @param[in]  SeekLen       The length to seek.\r
+  @param[in]  IaDesc        The pointer to the Ia descriptor.\r
+\r
+  @retval     NULL          If failed to seek the Ia option.\r
+  @retval     others        The position to the Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6SeekIaOption (\r
+  IN UINT8                     *Buf,\r
+  IN UINT32                    SeekLen,\r
+  IN EFI_DHCP6_IA_DESCRIPTOR   *IaDesc\r
+  );\r
+\r
+/**\r
+  Parse the address option and update the address info.\r
+\r
+  @param[in]      IaInnerOpt    The pointer to the buffer.\r
+  @param[in]      IaInnerLen    The length to parse.\r
+  @param[out]     AddrNum       The number of addresses.\r
+  @param[in, out] AddrBuf       The pointer to the address buffer.\r
+\r
+**/\r
+VOID\r
+Dhcp6ParseAddrOption (\r
+  IN     UINT8                   *IaInnerOpt,\r
+  IN     UINT16                  IaInnerLen,\r
+     OUT UINT32                  *AddrNum,\r
+  IN OUT EFI_DHCP6_IA_ADDRESS    *AddrBuf\r
+  );\r
+\r
+/**\r
+  Create a control blcok for the Ia according to the corresponding options.\r
+\r
+  @param[in]  Instance              The pointer to DHCP6 Instance.\r
+  @param[in]  IaInnerOpt            The pointer to the inner options in the Ia option.\r
+  @param[in]  IaInnerLen            The length of all the inner options in the Ia option.\r
+  @param[in]  T1                    T1 time in the Ia option.\r
+  @param[in]  T2                    T2 time in the Ia option.\r
+\r
+  @retval     EFI_NOT_FOUND         No valid IA option is found.\r
+  @retval     EFI_SUCCESS           Create an IA control block successfully.\r
+  @retval     EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6GenerateIaCb (\r
+  IN  DHCP6_INSTANCE           *Instance,\r
+  IN  UINT8                    *IaInnerOpt,\r
+  IN  UINT16                   IaInnerLen,\r
+  IN  UINT32                   T1,\r
+  IN  UINT32                   T2\r
+  );\r
+\r
+\r
+/**\r
+  Cache the current IA configuration information.\r
+\r
+  @param[in] Instance           The pointer to DHCP6 Instance.\r
+\r
+  @retval EFI_SUCCESS           Cache the current IA successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CacheIa (\r
+  IN DHCP6_INSTANCE           *Instance\r
+  );\r
+\r
+\r
+/**\r
+  Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.\r
+\r
+  @param[in]  Instance            The pointer to DHCP6 instance.\r
+\r
+**/\r
+VOID\r
+Dhcp6AppendCacheIa (\r
+  IN DHCP6_INSTANCE           *Instance\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/ComponentName.c b/NetworkPkg/Ip6Dxe/ComponentName.c
new file mode 100644 (file)
index 0000000..c8382f7
--- /dev/null
@@ -0,0 +1,313 @@
+/** @file\r
+  Implementation of EFI_COMPONENT_NAME_PROTOCOL and\r
+  EFI_COMPONENT_NAME2_PROTOCOL protocol.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  );\r
+\r
+//\r
+// EFI Component Name Protocol.\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL   gIp6ComponentName = {\r
+  Ip6ComponentNameGetDriverName,\r
+  Ip6ComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol.\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL  gIp6ComponentName2 = {\r
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ip6ComponentNameGetDriverName,\r
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ip6ComponentNameGetControllerName,\r
+  "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE      mIp6DriverNameTable[] = {\r
+  {\r
+    "eng;en",\r
+    L"IP6 Network Service Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+{\r
+  return LookupUnicodeString2 (\r
+          Language,\r
+          This->SupportedLanguages,\r
+          mIp6DriverNameTable,\r
+          DriverName,\r
+          (BOOLEAN) (This == &gIp6ComponentName)\r
+          );\r
+\r
+}\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Common.c b/NetworkPkg/Ip6Dxe/Ip6Common.c
new file mode 100644 (file)
index 0000000..2ae14a9
--- /dev/null
@@ -0,0 +1,796 @@
+/** @file\r
+  The implementation of common functions shared by IP6 driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+  Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number\r
+  of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL,\r
+  only the address count is returned.\r
+\r
+  @param[in]  IpSb              The IP6 service binding instance.\r
+  @param[out] AddressCount      The number of returned addresses.\r
+  @param[out] AddressList       The pointer to the array of EFI_IP6_ADDRESS_INFO.\r
+                                This is an optional parameter.\r
+\r
+\r
+  @retval EFI_SUCCESS           The address array successfully built.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the address info.\r
+  @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiAddressList (\r
+  IN IP6_SERVICE            *IpSb,\r
+  OUT UINT32                *AddressCount,\r
+  OUT EFI_IP6_ADDRESS_INFO  **AddressList OPTIONAL\r
+  )\r
+{\r
+  UINT32                Count;\r
+  LIST_ENTRY            *Entry;\r
+  EFI_IP6_ADDRESS_INFO  *EfiAddrInfo;\r
+  IP6_ADDRESS_INFO      *AddrInfo;\r
+\r
+  if (AddressCount == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (IpSb->LinkLocalOk) {\r
+    Count = 1 + IpSb->DefaultInterface->AddressCount;\r
+  } else {\r
+    Count = 0;\r
+  }\r
+\r
+  *AddressCount = Count;\r
+\r
+  if ((AddressList == NULL) || (Count == 0)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (*AddressList == NULL) {\r
+    *AddressList = AllocatePool (sizeof (EFI_IP6_ADDRESS_INFO) * Count);\r
+    if (*AddressList == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+  EfiAddrInfo = *AddressList;\r
+\r
+  IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &IpSb->LinkLocalAddr);\r
+  EfiAddrInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;\r
+\r
+  EfiAddrInfo++;\r
+  Count = 1;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->DefaultInterface->AddressList) {\r
+    AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+    IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &AddrInfo->Address);\r
+    EfiAddrInfo->PrefixLength = AddrInfo->PrefixLength;\r
+\r
+    EfiAddrInfo++;\r
+    Count++;\r
+  }\r
+\r
+  ASSERT (Count == *AddressCount);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Generate the multicast addresses identify the group of all IPv6 nodes or IPv6\r
+  routers defined in RFC4291.\r
+\r
+  All Nodes Addresses: FF01::1, FF02::1.\r
+  All Router Addresses: FF01::2, FF02::2, FF05::2.\r
+\r
+  @param[in]  Router            If TRUE, generate all routers addresses,\r
+                                else generate all node addresses.\r
+  @param[in]  Scope             interface-local(1), link-local(2), or site-local(5)\r
+  @param[out] Ip6Addr           The generated multicast address.\r
+\r
+  @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+  @retval EFI_SUCCESS           The address is generated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetToAllNodeMulticast (\r
+  IN  BOOLEAN          Router,\r
+  IN  UINT8            Scope,\r
+  OUT EFI_IPv6_ADDRESS *Ip6Addr\r
+  )\r
+{\r
+  if (Ip6Addr == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!Router && Scope == IP6_SITE_LOCAL_SCOPE) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ZeroMem (Ip6Addr, sizeof (EFI_IPv6_ADDRESS));\r
+  Ip6Addr->Addr[0] = 0xFF;\r
+  Ip6Addr->Addr[1] = Scope;\r
+\r
+  if (!Router) {\r
+    Ip6Addr->Addr[15] = 0x1;\r
+  } else {\r
+    Ip6Addr->Addr[15] = 0x2;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function converts MAC address to 64 bits interface ID according to RFC4291\r
+  and returns the interface ID. Currently only 48-bit MAC address is supported by\r
+  this function.\r
+\r
+  @param[in, out]  IpSb      The IP6 service binding instance.\r
+\r
+  @retval          NULL      The operation fails.\r
+  @return                    Pointer to the generated interface ID.\r
+\r
+**/\r
+UINT8 *\r
+Ip6CreateInterfaceID (\r
+  IN OUT IP6_SERVICE         *IpSb\r
+  )\r
+{\r
+  UINT8                      InterfaceId[8];\r
+  UINT8                      Byte;\r
+  EFI_MAC_ADDRESS            *MacAddr;\r
+  UINT32                     AddrLen;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  AddrLen = IpSb->SnpMode.HwAddressSize;\r
+\r
+  //\r
+  // Currently only IEEE 802 48-bit MACs are supported to create link local address.\r
+  //\r
+  if (AddrLen != IP6_MAC_LEN || IpSb->InterfaceIdLen != IP6_IF_ID_LEN) {\r
+    return NULL;\r
+  }\r
+\r
+  MacAddr = &IpSb->SnpMode.CurrentAddress;\r
+\r
+  //\r
+  // Convert MAC address to 64 bits interface ID according to Appendix A of RFC4291:\r
+  // 1. Insert 0xFFFE to the middle\r
+  // 2. Invert the universal/local bit - bit 6 in network order\r
+  //\r
+  CopyMem (InterfaceId, MacAddr, 3);\r
+  InterfaceId[3] = 0xFF;\r
+  InterfaceId[4] = 0xFE;\r
+  CopyMem (&InterfaceId[5], &MacAddr->Addr[3], 3);\r
+\r
+  Byte = (UINT8) (InterfaceId[0] & IP6_U_BIT);\r
+  if (Byte == IP6_U_BIT) {\r
+    InterfaceId[0] &= ~IP6_U_BIT;\r
+  } else {\r
+    InterfaceId[0] |= IP6_U_BIT;\r
+  }\r
+\r
+  //\r
+  // Return the interface ID.\r
+  //\r
+  return AllocateCopyPool (IpSb->InterfaceIdLen, InterfaceId);\r
+}\r
+\r
+/**\r
+  This function creates link-local address from interface identifier. The\r
+  interface identifier is normally created from MAC address. It might be manually\r
+  configured by administrator if the link-local address created from MAC address\r
+  is a duplicate address.\r
+\r
+  @param[in, out]  IpSb      The IP6 service binding instance.\r
+\r
+  @retval          NULL      If the operation fails.\r
+  @return                    The generated Link Local address, in network order.\r
+\r
+**/\r
+EFI_IPv6_ADDRESS *\r
+Ip6CreateLinkLocalAddr (\r
+  IN OUT IP6_SERVICE           *IpSb\r
+  )\r
+{\r
+  EFI_IPv6_ADDRESS             *Ip6Addr;\r
+  EFI_IP6_CONFIG_PROTOCOL      *Ip6Config;\r
+  UINTN                        DataSize;\r
+  EFI_IP6_CONFIG_INTERFACE_ID  InterfaceId;\r
+  EFI_STATUS                   Status;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  if (IpSb->InterfaceId != NULL) {\r
+    FreePool (IpSb->InterfaceId);\r
+  }\r
+\r
+  //\r
+  // Get the interface id if it is manully configured.\r
+  //\r
+  Ip6Config = &IpSb->Ip6ConfigInstance.Ip6Config;\r
+  DataSize  = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);\r
+  ZeroMem (&InterfaceId, DataSize);\r
+\r
+  Status = Ip6Config->GetData (\r
+                        Ip6Config,\r
+                        Ip6ConfigDataTypeAltInterfaceId,\r
+                        &DataSize,\r
+                        &InterfaceId\r
+                        );\r
+  if (Status == EFI_NOT_FOUND) {\r
+    //\r
+    // Since the interface id is not configured, generate the interface id from\r
+    // MAC address.\r
+    //\r
+    IpSb->InterfaceId = Ip6CreateInterfaceID (IpSb);\r
+    if (IpSb->InterfaceId == NULL) {\r
+      return NULL;\r
+    }\r
+\r
+    CopyMem (&InterfaceId, IpSb->InterfaceId, IpSb->InterfaceIdLen);\r
+    //\r
+    // Record the interface id.\r
+    //\r
+    Status = Ip6Config->SetData (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypeAltInterfaceId,\r
+                          DataSize,\r
+                          &InterfaceId\r
+                          );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (IpSb->InterfaceId);\r
+      IpSb->InterfaceId = NULL;\r
+      return NULL;\r
+    }\r
+  } else if (!EFI_ERROR (Status)) {\r
+    IpSb->InterfaceId = AllocateCopyPool (DataSize, &InterfaceId);\r
+    if (IpSb->InterfaceId == NULL) {\r
+      return NULL;\r
+    }\r
+  } else {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Append FE80::/64 to the left of IPv6 address then return.\r
+  //\r
+  Ip6Addr = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS));\r
+  if (Ip6Addr == NULL) {\r
+    FreePool (IpSb->InterfaceId);\r
+    IpSb->InterfaceId = NULL;\r
+    return NULL;\r
+  }\r
+\r
+  CopyMem (&Ip6Addr->Addr[8], IpSb->InterfaceId, IpSb->InterfaceIdLen);\r
+  Ip6Addr->Addr[1] = 0x80;\r
+  Ip6Addr->Addr[0] = 0xFE;\r
+\r
+  return Ip6Addr;\r
+}\r
+\r
+/**\r
+  Compute the solicited-node multicast address for an unicast or anycast address,\r
+  by taking the low-order 24 bits of this address, and appending those bits to\r
+  the prefix FF02:0:0:0:0:1:FF00::/104.\r
+\r
+  @param[in]  Ip6Addr               The unicast or anycast address, in network order.\r
+  @param[out] MulticastAddr         The generated solicited-node multicast address,\r
+                                    in network order.\r
+\r
+**/\r
+VOID\r
+Ip6CreateSNMulticastAddr (\r
+  IN EFI_IPv6_ADDRESS  *Ip6Addr,\r
+  OUT EFI_IPv6_ADDRESS *MulticastAddr\r
+  )\r
+{\r
+  ASSERT (Ip6Addr != NULL && MulticastAddr != NULL);\r
+\r
+  ZeroMem (MulticastAddr, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  MulticastAddr->Addr[0]  = 0xFF;\r
+  MulticastAddr->Addr[1]  = 0x02;\r
+  MulticastAddr->Addr[11] = 0x1;\r
+  MulticastAddr->Addr[12] = 0xFF;\r
+\r
+  CopyMem (&MulticastAddr->Addr[13], &Ip6Addr->Addr[13], 3);\r
+}\r
+\r
+/**\r
+  Insert a node IP6_ADDRESS_INFO to an IP6 interface.\r
+\r
+  @param[in, out]  IpIf             Points to an IP6 interface.\r
+  @param[in]       AddrInfo         Points to IP6_ADDRESS_INFO\r
+\r
+**/\r
+VOID\r
+Ip6AddAddr (\r
+  IN OUT IP6_INTERFACE *IpIf,\r
+  IN IP6_ADDRESS_INFO  *AddrInfo\r
+  )\r
+{\r
+  InsertHeadList (&IpIf->AddressList, &AddrInfo->Link);\r
+  IpIf->AddressCount++;\r
+}\r
+\r
+/**\r
+  Destroy the IP instance if its StationAddress is removed. It is the help function\r
+  for Ip6RemoveAddr().\r
+\r
+  @param[in, out]  IpSb             Points to an IP6 service binding instance.\r
+  @param[in]       Address          The to be removed address\r
+\r
+**/\r
+VOID\r
+Ip6DestroyInstanceByAddress (\r
+  IN OUT IP6_SERVICE   *IpSb,\r
+  IN EFI_IPv6_ADDRESS  *Address\r
+  )\r
+{\r
+  BOOLEAN                       OneDestroyed;\r
+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
+  LIST_ENTRY                    *Entry;\r
+  IP6_PROTOCOL                  *Instance;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  ServiceBinding = &IpSb->ServiceBinding;\r
+\r
+  //\r
+  // Upper layer IP protocol consumers may have tight relationship between several\r
+  // IP protocol instances, in other words, calling ServiceBinding->DestroyChild to\r
+  // destroy one IP child may cause other related IP children destroyed too. This\r
+  // will probably leave hole in the children list when we iterate it. So everytime\r
+  // we just destroy one child then back to the start point to iterate the list.\r
+  //\r
+  do {\r
+    OneDestroyed = FALSE;\r
+\r
+    NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+      Instance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);\r
+\r
+      if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, Address)) {\r
+        ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);\r
+        OneDestroyed = TRUE;\r
+        break;\r
+      }\r
+    }\r
+  } while (OneDestroyed);\r
+}\r
+\r
+/**\r
+  Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO.\r
+\r
+  This function removes the matching IPv6 addresses from the address list and\r
+  adjusts the address count of the address list. If IpSb is not NULL, this function\r
+  calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the\r
+  its solicited-node multicast MAC address from the filter list and sends out\r
+  a Multicast Listener Done. If Prefix is NULL, all address in the address list\r
+  will be removed. If Prefix is not NULL, the address that matching the Prefix\r
+  with PrefixLength in the address list will be removed.\r
+\r
+  @param[in]       IpSb             NULL or points to IP6 service binding instance.\r
+  @param[in, out]  AddressList      Address list array.\r
+  @param[in, out]  AddressCount     The count of addresses in address list array.\r
+  @param[in]       Prefix           NULL or an IPv6 address prefix.\r
+  @param[in]       PrefixLength     The length of Prefix.\r
+\r
+  @retval    EFI_SUCCESS            The operation completed successfully.\r
+  @retval    EFI_NOT_FOUND          The address matching the Prefix with PrefixLength\r
+                                    cannot be found in the address list.\r
+  @retval    EFI_INVALID_PARAMETER  Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6RemoveAddr (\r
+  IN IP6_SERVICE       *IpSb          OPTIONAL,\r
+  IN OUT LIST_ENTRY    *AddressList,\r
+  IN OUT UINT32        *AddressCount,\r
+  IN EFI_IPv6_ADDRESS  *Prefix        OPTIONAL,\r
+  IN UINT8             PrefixLength\r
+  )\r
+{\r
+  EFI_STATUS           Status;\r
+  LIST_ENTRY           *Entry;\r
+  LIST_ENTRY           *Next;\r
+  IP6_ADDRESS_INFO     *AddrInfo;\r
+  EFI_IPv6_ADDRESS     SnMCastAddr;\r
+\r
+  if (IsListEmpty (AddressList) || *AddressCount < 1 || PrefixLength > IP6_PREFIX_NUM) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = EFI_NOT_FOUND;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, AddressList) {\r
+    AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+    if (Prefix == NULL ||\r
+        (PrefixLength == 128 && EFI_IP6_EQUAL (Prefix, &AddrInfo->Address)) ||\r
+        (PrefixLength == AddrInfo->PrefixLength && NetIp6IsNetEqual (Prefix, &AddrInfo->Address, PrefixLength))\r
+        ) {\r
+      if (IpSb != NULL) {\r
+        NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+        Ip6CreateSNMulticastAddr (&AddrInfo->Address, &SnMCastAddr);\r
+        Ip6LeaveGroup (IpSb, &SnMCastAddr);\r
+\r
+        //\r
+        // Destroy any instance who is using the dying address as the source address.\r
+        //\r
+        Ip6DestroyInstanceByAddress (IpSb, &AddrInfo->Address);\r
+      }\r
+\r
+      RemoveEntryList (Entry);\r
+      FreePool (AddrInfo);\r
+      (*AddressCount)--;\r
+\r
+      Status = EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Check whether the incoming Ipv6 address is a solicited-node multicast address.\r
+\r
+  @param[in]  Ip6               Ip6 address, in network order.\r
+\r
+  @retval TRUE                  Yes, solicited-node multicast address\r
+  @retval FALSE                 No\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsSNMulticastAddr (\r
+  IN EFI_IPv6_ADDRESS *Ip6\r
+  )\r
+{\r
+  EFI_IPv6_ADDRESS    Sn;\r
+  BOOLEAN             Flag;\r
+\r
+  Ip6CreateSNMulticastAddr (Ip6, &Sn);\r
+  Flag = FALSE;\r
+\r
+  if (CompareMem (Sn.Addr, Ip6->Addr, 13) == 0) {\r
+    Flag = TRUE;\r
+  }\r
+\r
+  return Flag;\r
+}\r
+\r
+/**\r
+  Check whether the incoming IPv6 address is one of the maintained addresses in\r
+  the IP6 service binding instance.\r
+\r
+  @param[in]  IpSb              Points to a IP6 service binding instance.\r
+  @param[in]  Address           The IP6 address to be checked.\r
+  @param[out] Interface         If not NULL, output the IP6 interface which\r
+                                maintains the Address.\r
+  @param[out] AddressInfo       If not NULL, output the IP6 address information\r
+                                of the Address.\r
+\r
+  @retval TRUE                  Yes, it is one of the maintained address.\r
+  @retval FALSE                 No, it is not one of the maintained address.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsOneOfSetAddress (\r
+  IN  IP6_SERVICE           *IpSb,\r
+  IN  EFI_IPv6_ADDRESS      *Address,\r
+  OUT IP6_INTERFACE         **Interface   OPTIONAL,\r
+  OUT IP6_ADDRESS_INFO      **AddressInfo OPTIONAL\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Entry2;\r
+  IP6_INTERFACE             *IpIf;\r
+  IP6_ADDRESS_INFO          *TmpAddressInfo;\r
+\r
+  //\r
+  // Check link-local address first\r
+  //\r
+  if (IpSb->LinkLocalOk && EFI_IP6_EQUAL (&IpSb->LinkLocalAddr, Address)) {\r
+    if (Interface != NULL) {\r
+      *Interface = IpSb->DefaultInterface;\r
+    }\r
+\r
+    if (AddressInfo != NULL) {\r
+      *AddressInfo = NULL;\r
+    }\r
+\r
+    return TRUE;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);\r
+\r
+    NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {\r
+      TmpAddressInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+      if (EFI_IP6_EQUAL (&TmpAddressInfo->Address, Address)) {\r
+        if (Interface != NULL) {\r
+          *Interface = IpIf;\r
+        }\r
+\r
+        if (AddressInfo != NULL) {\r
+          *AddressInfo = TmpAddressInfo;\r
+        }\r
+\r
+        return TRUE;\r
+      }\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Check whether the incoming MAC address is valid.\r
+\r
+  @param[in]  IpSb              Points to a IP6 service binding instance.\r
+  @param[in]  LinkAddress       The MAC address.\r
+\r
+  @retval TRUE                  Yes, it is valid.\r
+  @retval FALSE                 No, it is not valid.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsValidLinkAddress (\r
+  IN  IP6_SERVICE      *IpSb,\r
+  IN  EFI_MAC_ADDRESS  *LinkAddress\r
+  )\r
+{\r
+  UINT32               Index;\r
+\r
+  //\r
+  // TODO: might be updated later to be more acceptable.\r
+  //\r
+  for (Index = IpSb->SnpMode.HwAddressSize; Index < sizeof (EFI_MAC_ADDRESS); Index++) {\r
+    if (LinkAddress->Addr[Index] != 0) {\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Copy the PrefixLength bits from Src to Dest.\r
+\r
+  @param[out] Dest              A pointer to the buffer to copy to.\r
+  @param[in]  Src               A pointer to the buffer to copy from.\r
+  @param[in]  PrefixLength      The number of bits to copy.\r
+\r
+**/\r
+VOID\r
+Ip6CopyAddressByPrefix (\r
+  OUT EFI_IPv6_ADDRESS *Dest,\r
+  IN  EFI_IPv6_ADDRESS *Src,\r
+  IN  UINT8            PrefixLength\r
+  )\r
+{\r
+  UINT8 Byte;\r
+  UINT8 Bit;\r
+  UINT8 Mask;\r
+\r
+  ASSERT (Dest != NULL && Src != NULL);\r
+  ASSERT (PrefixLength < IP6_PREFIX_NUM);\r
+\r
+  Byte = (UINT8) (PrefixLength / 8);\r
+  Bit  = (UINT8) (PrefixLength % 8);\r
+\r
+  ZeroMem (Dest, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  CopyMem (Dest, Src, Byte);\r
+\r
+  if (Bit > 0) {\r
+    Mask = (UINT8) (0xFF << (8 - Bit));\r
+    ASSERT (Byte < 16);\r
+    Dest->Addr[Byte] = (UINT8) (Src->Addr[Byte] & Mask);\r
+  }\r
+}\r
+\r
+/**\r
+  Get the MAC address for a multicast IP address. Call\r
+  Mnp's McastIpToMac to find the MAC address instead of\r
+  hard-coding the NIC to be Ethernet.\r
+\r
+  @param[in]  Mnp                   The Mnp instance to get the MAC address.\r
+  @param[in]  Multicast             The multicast IP address to translate.\r
+  @param[out] Mac                   The buffer to hold the translated address.\r
+\r
+  @retval EFI_SUCCESS               The multicast IP successfully\r
+                                    translated to a multicast MAC address.\r
+  @retval Other                     The address is not converted because an error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6GetMulticastMac (\r
+  IN  EFI_MANAGED_NETWORK_PROTOCOL *Mnp,\r
+  IN  EFI_IPv6_ADDRESS             *Multicast,\r
+  OUT EFI_MAC_ADDRESS              *Mac\r
+  )\r
+{\r
+  EFI_IP_ADDRESS        EfiIp;\r
+\r
+  IP6_COPY_ADDRESS (&EfiIp.v6, Multicast);\r
+\r
+  return Mnp->McastIpToMac (Mnp, TRUE, &EfiIp, Mac);\r
+}\r
+\r
+/**\r
+  Set the Ip6 variable data.\r
+\r
+  @param[in]  IpSb              Points to an IP6 service binding instance.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.\r
+  @retval other                 Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetVariableData (\r
+  IN IP6_SERVICE  *IpSb\r
+  )\r
+{\r
+  UINT32                 NumConfiguredInstance;\r
+  LIST_ENTRY             *Entry;\r
+  UINTN                  VariableDataSize;\r
+  EFI_IP6_VARIABLE_DATA  *Ip6VariableData;\r
+  EFI_IP6_ADDRESS_PAIR   *Ip6AddressPair;\r
+  IP6_PROTOCOL           *IpInstance;\r
+  CHAR16                 *NewMacString;\r
+  EFI_STATUS             Status;\r
+\r
+  NumConfiguredInstance = 0;\r
+\r
+  //\r
+  // Go through the children list to count the configured children.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+    IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);\r
+\r
+    if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+      NumConfiguredInstance++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Calculate the size of the Ip6VariableData. As there may be no IP child,\r
+  // we should add extra buffer for the address paris only if the number of configured\r
+  // children is more than 1.\r
+  //\r
+  VariableDataSize = sizeof (EFI_IP6_VARIABLE_DATA);\r
+\r
+  if (NumConfiguredInstance > 1) {\r
+    VariableDataSize += sizeof (EFI_IP6_ADDRESS_PAIR) * (NumConfiguredInstance - 1);\r
+  }\r
+\r
+  Ip6VariableData = AllocatePool (VariableDataSize);\r
+  if (Ip6VariableData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ip6VariableData->DriverHandle = IpSb->Image;\r
+  Ip6VariableData->AddressCount = NumConfiguredInstance;\r
+\r
+  Ip6AddressPair                = &Ip6VariableData->AddressPairs[0];\r
+\r
+  //\r
+  // Go through the children list to fill the configured children's address pairs.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+    IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);\r
+\r
+    if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+      Ip6AddressPair->InstanceHandle = IpInstance->Handle;\r
+      Ip6AddressPair->PrefixLength   = IpInstance->PrefixLength;\r
+      IP6_COPY_ADDRESS (&Ip6AddressPair->Ip6Address, &IpInstance->ConfigData.StationAddress);\r
+\r
+      Ip6AddressPair++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Get the mac string.\r
+  //\r
+  Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &NewMacString);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  if (IpSb->MacString != NULL) {\r
+    //\r
+    // The variable is set already, we're going to update it.\r
+    //\r
+    if (StrCmp (IpSb->MacString, NewMacString) != 0) {\r
+      //\r
+      // The mac address is changed, delete the previous variable first.\r
+      //\r
+      gRT->SetVariable (\r
+             IpSb->MacString,\r
+             &gEfiIp6ServiceBindingProtocolGuid,\r
+             EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+             0,\r
+             NULL\r
+             );\r
+    }\r
+\r
+    FreePool (IpSb->MacString);\r
+  }\r
+\r
+  IpSb->MacString = NewMacString;\r
+\r
+  Status = gRT->SetVariable (\r
+                  IpSb->MacString,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                  VariableDataSize,\r
+                  (VOID *) Ip6VariableData\r
+                  );\r
+\r
+Exit:\r
+  FreePool (Ip6VariableData);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Clear the variable and free the resource.\r
+\r
+  @param[in]  IpSb                  Ip6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6ClearVariableData (\r
+  IN IP6_SERVICE  *IpSb\r
+  )\r
+{\r
+  ASSERT (IpSb->MacString != NULL);\r
+\r
+  gRT->SetVariable (\r
+         IpSb->MacString,\r
+         &gEfiIp6ServiceBindingProtocolGuid,\r
+         EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+         0,\r
+         NULL\r
+         );\r
+\r
+  FreePool (IpSb->MacString);\r
+  IpSb->MacString = NULL;\r
+}\r
+\r
+/**\r
+  Convert the multibyte field in IP header's byter order.\r
+  In spite of its name, it can also be used to convert from\r
+  host to network byte order.\r
+\r
+  @param[in, out]  Head                  The IP head to convert.\r
+\r
+  @return Point to the converted IP head.\r
+\r
+**/\r
+EFI_IP6_HEADER *\r
+Ip6NtohHead (\r
+  IN OUT EFI_IP6_HEADER *Head\r
+  )\r
+{\r
+  Head->FlowLabelL    = NTOHS (Head->FlowLabelL);\r
+  Head->PayloadLength = NTOHS (Head->PayloadLength);\r
+\r
+  return Head;\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Common.h b/NetworkPkg/Ip6Dxe/Ip6Common.h
new file mode 100644 (file)
index 0000000..c3755f4
--- /dev/null
@@ -0,0 +1,338 @@
+/** @file\r
+  Common definition and functions for IP6 driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_COMMON_H__\r
+#define __EFI_IP6_COMMON_H__\r
+\r
+#define IP6_LINK_EQUAL(Mac1, Mac2) (CompareMem ((Mac1), (Mac2), sizeof (EFI_MAC_ADDRESS)) == 0)\r
+\r
+//\r
+// Convert the Microsecond to second. IP transmit/receive time is\r
+// in the unit of microsecond. IP ticks once per second.\r
+//\r
+#define IP6_US_TO_SEC(Us)              (((Us) + 999999) / 1000000)\r
+\r
+#define IP6_ETHER_PROTO                0x86DD\r
+\r
+#define IP6_MAC_LEN                    6\r
+#define IP6_IF_ID_LEN                  8\r
+\r
+#define IP6_INTERFACE_LOCAL_SCOPE      1\r
+#define IP6_LINK_LOCAL_SCOPE           2\r
+#define IP6_SITE_LOCAL_SCOPE           5\r
+\r
+#define IP6_INFINIT_LIFETIME           0xFFFFFFFF\r
+\r
+#define IP6_HOP_LIMIT                  255\r
+//\r
+// Make it to 64 since all 54 bits are zero.\r
+//\r
+#define IP6_LINK_LOCAL_PREFIX_LENGTH   64\r
+\r
+#define IP6_TIMER_INTERVAL_IN_MS       100\r
+#define IP6_ONE_SECOND_IN_MS           1000\r
+\r
+//\r
+// The packet is received as link level broadcast/multicast/promiscuous.\r
+//\r
+#define IP6_LINK_BROADCAST             0x00000001\r
+#define IP6_LINK_MULTICAST             0x00000002\r
+#define IP6_LINK_PROMISC               0x00000004\r
+\r
+#define IP6_U_BIT                      0x02\r
+\r
+typedef enum {\r
+  Ip6Promiscuous                     = 1,\r
+  Ip6Unicast,\r
+  Ip6Multicast,\r
+  Ip6AnyCast\r
+} IP6_ADDRESS_TYPE;\r
+\r
+typedef struct _IP6_INTERFACE    IP6_INTERFACE;\r
+typedef struct _IP6_PROTOCOL     IP6_PROTOCOL;\r
+typedef struct _IP6_SERVICE      IP6_SERVICE;\r
+typedef struct _IP6_ADDRESS_INFO IP6_ADDRESS_INFO;\r
+\r
+/**\r
+  Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number\r
+  of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL,\r
+  only the address count is returned.\r
+\r
+  @param[in]  IpSb              The IP6 service binding instance.\r
+  @param[out] AddressCount      The number of returned addresses.\r
+  @param[out] AddressList       The pointer to the array of EFI_IP6_ADDRESS_INFO.\r
+                                This is an optional parameter.\r
+\r
+\r
+  @retval EFI_SUCCESS           The address array is successfully build\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the address info.\r
+  @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiAddressList (\r
+  IN IP6_SERVICE            *IpSb,\r
+  OUT UINT32                *AddressCount,\r
+  OUT EFI_IP6_ADDRESS_INFO  **AddressList OPTIONAL\r
+  );\r
+\r
+/**\r
+  Generate the multicast addresses identify the group of all IPv6 nodes or IPv6\r
+  routers defined in RFC4291.\r
+\r
+  All Nodes Addresses: FF01::1, FF02::1.\r
+  All Router Addresses: FF01::2, FF02::2, FF05::2.\r
+\r
+  @param[in]  Router            If TRUE, generate all routers addresses,\r
+                                else generate all node addresses.\r
+  @param[in]  Scope             interface-local(1), link-local(2), or site-local(5)\r
+  @param[out] Ip6Addr           The generated multicast address.\r
+\r
+  @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+  @retval EFI_SUCCESS           The address is generated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetToAllNodeMulticast (\r
+  IN  BOOLEAN          Router,\r
+  IN  UINT8            Scope,\r
+  OUT EFI_IPv6_ADDRESS *Ip6Addr\r
+  );\r
+\r
+/**\r
+  This function converts MAC address to 64 bits interface ID according to RFC4291\r
+  and returns the interface ID. Currently only 48-bit MAC address is supported by\r
+  this function.\r
+\r
+  @param[in, out]  IpSb      The IP6 service binding instance.\r
+\r
+  @retval          NULL      The operation fails.\r
+  @return                    Pointer to the generated interface ID.\r
+\r
+**/\r
+UINT8 *\r
+Ip6CreateInterfaceID (\r
+  IN OUT IP6_SERVICE         *IpSb\r
+  );\r
+\r
+/**\r
+  This function creates link-local address from interface identifier. The\r
+  interface identifier is normally created from MAC address. It might be manually\r
+  configured by administrator if the link-local address created from MAC address\r
+  is a duplicate address.\r
+\r
+  @param[in, out]  IpSb      The IP6 service binding instance.\r
+\r
+  @retval          NULL      If the operation fails.\r
+  @return                    The generated Link Local address, in network order.\r
+\r
+**/\r
+EFI_IPv6_ADDRESS *\r
+Ip6CreateLinkLocalAddr (\r
+  IN OUT IP6_SERVICE         *IpSb\r
+  );\r
+\r
+/**\r
+  Compute the solicited-node multicast address for an unicast or anycast address,\r
+  by taking the low-order 24 bits of this address, and appending those bits to\r
+  the prefix FF02:0:0:0:0:1:FF00::/104.\r
+\r
+  @param  Ip6Addr               The unicast or anycast address, in network order.\r
+  @param  MulticastAddr         The generated solicited-node multicast address,\r
+                                in network order.\r
+\r
+**/\r
+VOID\r
+Ip6CreateSNMulticastAddr (\r
+  IN EFI_IPv6_ADDRESS  *Ip6Addr,\r
+  OUT EFI_IPv6_ADDRESS *MulticastAddr\r
+  );\r
+\r
+/**\r
+  Check whether the incoming Ipv6 address is a solicited-node multicast address.\r
+\r
+  @param[in]  Ip6               Ip6 address, in network order.\r
+\r
+  @retval TRUE                  Yes, solicited-node multicast address\r
+  @retval FALSE                 No\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsSNMulticastAddr (\r
+  IN EFI_IPv6_ADDRESS *Ip6\r
+  );\r
+\r
+/**\r
+  Check whether the incoming IPv6 address is one of the maintained address in\r
+  the IP6 service binding instance.\r
+\r
+  @param[in]  IpSb              Points to a IP6 service binding instance\r
+  @param[in]  Address           The IP6 address to be checked.\r
+  @param[out] Interface         If not NULL, output the IP6 interface which\r
+                                maintains the Address.\r
+  @param[out] AddressInfo       If not NULL, output the IP6 address information\r
+                                of the Address.\r
+\r
+  @retval TRUE                  Yes, it is one of the maintained addresses.\r
+  @retval FALSE                 No, it is not one of the maintained addresses.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsOneOfSetAddress (\r
+  IN  IP6_SERVICE           *IpSb,\r
+  IN  EFI_IPv6_ADDRESS      *Address,\r
+  OUT IP6_INTERFACE         **Interface   OPTIONAL,\r
+  OUT IP6_ADDRESS_INFO      **AddressInfo OPTIONAL\r
+  );\r
+\r
+/**\r
+  Check whether the incoming MAC address is valid.\r
+\r
+  @param[in]  IpSb              Points to a IP6 service binding instance.\r
+  @param[in]  LinkAddress       The MAC address.\r
+\r
+  @retval TRUE                  Yes, it is valid.\r
+  @retval FALSE                 No, it is not valid.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsValidLinkAddress (\r
+  IN  IP6_SERVICE      *IpSb,\r
+  IN  EFI_MAC_ADDRESS  *LinkAddress\r
+  );\r
+\r
+\r
+/**\r
+  Copy the PrefixLength bits from Src to Dest.\r
+\r
+  @param[out] Dest              A pointer to the buffer to copy to.\r
+  @param[in]  Src               A pointer to the buffer to copy from.\r
+  @param[in]  PrefixLength      The number of bits to copy.\r
+\r
+**/\r
+VOID\r
+Ip6CopyAddressByPrefix (\r
+  OUT EFI_IPv6_ADDRESS *Dest,\r
+  IN  EFI_IPv6_ADDRESS *Src,\r
+  IN  UINT8            PrefixLength\r
+  );\r
+\r
+/**\r
+  Insert a node IP6_ADDRESS_INFO to an IP6 interface.\r
+\r
+  @param[in, out]  IpIf             Points to an IP6 interface.\r
+  @param[in]       AddrInfo         Points to an IP6_ADDRESS_INFO.\r
+\r
+**/\r
+VOID\r
+Ip6AddAddr (\r
+  IN OUT IP6_INTERFACE *IpIf,\r
+  IN IP6_ADDRESS_INFO  *AddrInfo\r
+  );\r
+\r
+/**\r
+  Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO.\r
+\r
+  This function removes the matching IPv6 addresses from the address list and\r
+  adjusts the address count of the address list. If IpSb is not NULL, this function\r
+  calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the\r
+  its solicited-node multicast MAC address from the filter list and sends out\r
+  a Multicast Listener Done. If Prefix is NULL, all address in the address list\r
+  will be removed. If Prefix is not NULL, the address that matching the Prefix\r
+  with PrefixLength in the address list will be removed.\r
+\r
+  @param[in]       IpSb             NULL or points to IP6 service binding instance.\r
+  @param[in, out]  AddressList      address list array\r
+  @param[in, out]  AddressCount     the count of addresses in address list array\r
+  @param[in]       Prefix           NULL or an IPv6 address prefix\r
+  @param[in]       PrefixLength     the length of Prefix\r
+\r
+  @retval    EFI_SUCCESS            The operation completed successfully.\r
+  @retval    EFI_NOT_FOUND          The address matching the Prefix with PrefixLength\r
+                                    cannot be found in address list.\r
+  @retval    EFI_INVALID_PARAMETER  Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6RemoveAddr (\r
+  IN IP6_SERVICE       *IpSb          OPTIONAL,\r
+  IN OUT LIST_ENTRY    *AddressList,\r
+  IN OUT UINT32        *AddressCount,\r
+  IN EFI_IPv6_ADDRESS  *Prefix        OPTIONAL,\r
+  IN UINT8             PrefixLength\r
+  );\r
+\r
+/**\r
+  Set the Ip6 variable data.\r
+\r
+  @param[in]  IpSb              Points to an IP6 service binding instance\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.\r
+  @retval other                 Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetVariableData (\r
+  IN IP6_SERVICE  *IpSb\r
+  );\r
+\r
+/**\r
+  Clear the variable and free the resource.\r
+\r
+  @param[in]  IpSb                  Ip6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6ClearVariableData (\r
+  IN IP6_SERVICE  *IpSb\r
+  );\r
+\r
+/**\r
+  Get the MAC address for a multicast IP address. Call\r
+  Mnp's McastIpToMac to find the MAC address instead of\r
+  hard-coding the NIC to be Ethernet.\r
+\r
+  @param[in]  Mnp                   The Mnp instance to get the MAC address.\r
+  @param[in]  Multicast             The multicast IP address to translate.\r
+  @param[out] Mac                   The buffer to hold the translated address.\r
+\r
+  @retval EFI_SUCCESS               The multicast IP is successfully\r
+                                    translated to a multicast MAC address.\r
+  @retval Other                     The address is not converted because an error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6GetMulticastMac (\r
+  IN  EFI_MANAGED_NETWORK_PROTOCOL *Mnp,\r
+  IN  EFI_IPv6_ADDRESS             *Multicast,\r
+  OUT EFI_MAC_ADDRESS              *Mac\r
+  );\r
+\r
+/**\r
+  Convert the multibyte field in IP header's byter order.\r
+  In spite of its name, it can also be used to convert from\r
+  host to network byte order.\r
+\r
+  @param[in, out]  Head                  The IP head to convert.\r
+\r
+  @return Point to the converted IP head.\r
+\r
+**/\r
+EFI_IP6_HEADER *\r
+Ip6NtohHead (\r
+  IN OUT EFI_IP6_HEADER *Head\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Config.vfr b/NetworkPkg/Ip6Dxe/Ip6Config.vfr
new file mode 100644 (file)
index 0000000..902cef6
--- /dev/null
@@ -0,0 +1,170 @@
+/** @file\r
+  VFR file used by the IP6 configuration component.\r
+\r
+  Copyright (c) 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6NvData.h"\r
+\r
+#define EFI_NETWORK_DEVICE_CLASS  0x04\r
+\r
+formset\r
+  guid     = IP6_CONFIG_NVDATA_GUID,\r
+  title    = STRING_TOKEN(STR_IP6_CONFIG_FORM_TITLE),\r
+  help     = STRING_TOKEN(STR_IP6_CONFIG_FORM_HELP),\r
+  class    = EFI_NETWORK_DEVICE_CLASS,\r
+  subclass = 0x03,\r
+\r
+  varstore IP6_CONFIG_IFR_NVDATA,\r
+    name = IP6_CONFIG_IFR_NVDATA,\r
+    guid = IP6_CONFIG_NVDATA_GUID;\r
+\r
+  form formid = FORMID_MAIN_FORM,\r
+    title  = STRING_TOKEN(STR_IP6_DEVICE_FORM_TITLE);\r
+\r
+    text\r
+      help   = STRING_TOKEN(STR_IP6_INTERFACE_NAME_HELP),\r
+      text   = STRING_TOKEN(STR_IP6_INTERFACE_NAME),\r
+        text   = STRING_TOKEN(STR_IP6_INTERFACE_NAME_CONTENT);\r
+\r
+    text\r
+      help   = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_HELP),\r
+      text   = STRING_TOKEN(STR_IP6_INTERFACE_TYPE),\r
+        text   = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_CONTENT);\r
+\r
+    text\r
+      help   = STRING_TOKEN(STR_IP6_MAC_ADDRESS_HELP),\r
+      text   = STRING_TOKEN(STR_IP6_MAC_ADDRESS),\r
+        text   = STRING_TOKEN(STR_IP6_MAC_ADDRESS_CONTENT);\r
+\r
+    text\r
+      help   = STRING_TOKEN(STR_IP6_HOST_ADDRESS_HELP),\r
+      text   = STRING_TOKEN(STR_IP6_HOST_ADDRESS),\r
+        text   = STRING_TOKEN(STR_NULL);\r
+\r
+    label HOST_ADDRESS_LABEL;\r
+    label LABEL_END;\r
+\r
+    text\r
+      help   = STRING_TOKEN(STR_IP6_ROUTE_TABLE_HELP),\r
+      text   = STRING_TOKEN(STR_IP6_ROUTE_TABLE),\r
+        text   = STRING_TOKEN(STR_NULL);\r
+\r
+    label ROUTE_TABLE_LABEL;\r
+    label LABEL_END;\r
+\r
+    text\r
+      help   = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS_HELP),\r
+      text   = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS),\r
+        text   = STRING_TOKEN(STR_NULL);\r
+\r
+    label GATEWAY_ADDRESS_LABEL;\r
+    label LABEL_END;\r
+\r
+    text\r
+      help   = STRING_TOKEN(STR_IP6_DNS_ADDRESS_HELP),\r
+      text   = STRING_TOKEN(STR_IP6_DNS_ADDRESS),\r
+        text   = STRING_TOKEN(STR_NULL);\r
+\r
+    label DNS_ADDRESS_LABEL;\r
+    label LABEL_END;\r
+\r
+    string  varid   = IP6_CONFIG_IFR_NVDATA.InterfaceId,\r
+            prompt  = STRING_TOKEN(STR_IP6_INTERFACE_ID),\r
+            help    = STRING_TOKEN(STR_IP6_INTERFACE_ID_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_INTERFACE_ID,\r
+            minsize = INTERFACE_ID_STR_MIN_SIZE,\r
+            maxsize = INTERFACE_ID_STR_MAX_SIZE,\r
+    endstring;\r
+\r
+    numeric varid   = IP6_CONFIG_IFR_NVDATA.DadTransmitCount,\r
+            prompt  = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT),\r
+            help    = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT_HELP),\r
+            flags   = 0,\r
+            minimum = 0,\r
+            maximum = DAD_MAX_TRANSMIT_COUNT,\r
+            step    = 0,\r
+    endnumeric;\r
+\r
+    oneof varid  = IP6_CONFIG_IFR_NVDATA.Policy,\r
+          prompt = STRING_TOKEN(STR_POLICY_TYPE_PROMPT),\r
+          help   = STRING_TOKEN(STR_POLICY_TYPE_HELP),\r
+          option text = STRING_TOKEN(STR_POLICY_TYPE_AUTO),   value = IP6_POLICY_AUTO,   flags = DEFAULT;\r
+          option text = STRING_TOKEN(STR_POLICY_TYPE_MANUAL), value = IP6_POLICY_MANUAL, flags = 0;\r
+    endoneof;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    suppressif ideqval IP6_CONFIG_IFR_NVDATA.Policy == IP6_POLICY_AUTO;\r
+    goto FORMID_MANUAL_CONFIG_FORM,\r
+         prompt = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM),\r
+         help   = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM_HELP),\r
+         flags  = 0;\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+    endif;\r
+\r
+    text\r
+      help   = STRING_TOKEN (STR_SAVE_CHANGES_HELP),\r
+      text   = STRING_TOKEN (STR_SAVE_CHANGES),\r
+        text   = STRING_TOKEN (STR_NULL),\r
+      flags  = INTERACTIVE,\r
+      key    = KEY_SAVE_CHANGES;\r
+\r
+  endform;\r
+\r
+  form formid = FORMID_MANUAL_CONFIG_FORM,\r
+    title  = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM);\r
+\r
+    string  varid   = IP6_CONFIG_IFR_NVDATA.ManualAddress,\r
+            prompt  = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS),\r
+            help    = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_MANUAL_ADDRESS,\r
+            minsize = ADDRESS_STR_MIN_SIZE,\r
+            maxsize = ADDRESS_STR_MAX_SIZE,\r
+    endstring;\r
+\r
+    string  varid   = IP6_CONFIG_IFR_NVDATA.GatewayAddress,\r
+            prompt  = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDRESS),\r
+            help    = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDR_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_GATEWAY_ADDRESS,\r
+            minsize = ADDRESS_STR_MIN_SIZE,\r
+            maxsize = ADDRESS_STR_MAX_SIZE,\r
+    endstring;\r
+\r
+    string  varid   = IP6_CONFIG_IFR_NVDATA.DnsAddress,\r
+            prompt  = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS),\r
+            help    = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_DNS_ADDRESS,\r
+            minsize = ADDRESS_STR_MIN_SIZE,\r
+            maxsize = ADDRESS_STR_MAX_SIZE,\r
+    endstring;\r
+\r
+    goto FORMID_MAIN_FORM,\r
+    prompt = STRING_TOKEN (STR_SAVE_AND_EXIT),\r
+    help   = STRING_TOKEN (STR_SAVE_AND_EXIT),\r
+    flags  = INTERACTIVE,\r
+    key    = KEY_SAVE_CONFIG_CHANGES;\r
+\r
+    goto FORMID_MAIN_FORM,\r
+    prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),\r
+    help   = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),\r
+    flags  = INTERACTIVE,\r
+    key    = KEY_IGNORE_CONFIG_CHANGES;\r
+\r
+  endform;\r
+\r
+endformset;\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c
new file mode 100644 (file)
index 0000000..3cfd1f2
--- /dev/null
@@ -0,0 +1,2369 @@
+/** @file\r
+  The implementation of EFI IPv6 Configuration Protocol.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+LIST_ENTRY  mIp6ConfigInstanceList = {&mIp6ConfigInstanceList, &mIp6ConfigInstanceList};\r
+\r
+/**\r
+  The event process routine when the DHCPv6 service binding protocol is installed\r
+  in the system.\r
+\r
+  @param[in]     Event         Not used.\r
+  @param[in]     Context       Pointer to the IP6 config instance data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6ConfigOnDhcp6SbInstalled (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  );\r
+\r
+/**\r
+  Update the current policy to NewPolicy. During the transition\r
+  period, the default router list, on-link prefix list, autonomous prefix list\r
+  and address list in all interfaces will be released.\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance.\r
+  @param[in]  NewPolicy          The new policy to be updated to.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigOnPolicyChanged (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_CONFIG_POLICY  NewPolicy\r
+  )\r
+{\r
+  LIST_ENTRY      *Entry;\r
+  LIST_ENTRY      *Entry2;\r
+  LIST_ENTRY      *Next;\r
+  IP6_INTERFACE   *IpIf;\r
+  IP6_DAD_ENTRY   *DadEntry;\r
+\r
+  //\r
+  // Currently there are only two policies: Manual and Automatic. Regardless of\r
+  // what transition is going on, i.e., Manual -> Automatic and Automatic ->\r
+  // Manual, we have to free default router list, on-link prefix list, autonomous\r
+  // prefix list, address list in all the interfaces and destroy any IPv6 child\r
+  // instance whose local IP is neither 0 nor the link-local address.\r
+  //\r
+  Ip6CleanDefaultRouterList (IpSb);\r
+  Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix);\r
+  Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix);\r
+\r
+  //\r
+  // It's tricky... If the LinkLocal address is O.K., add back the link-local\r
+  // prefix to the on-link prefix table.\r
+  //\r
+  if (IpSb->LinkLocalOk) {\r
+    Ip6CreatePrefixListEntry (\r
+      IpSb,\r
+      TRUE,\r
+      (UINT32) IP6_INFINIT_LIFETIME,\r
+      (UINT32) IP6_INFINIT_LIFETIME,\r
+      IP6_LINK_LOCAL_PREFIX_LENGTH,\r
+      &IpSb->LinkLocalAddr\r
+      );\r
+  }\r
+\r
+  //\r
+  // All IPv6 children that use global unicast address as it's source address\r
+  // should be destryoed now. The survivers are those use the link-local address\r
+  // or the unspecified address as the source address.\r
+  // TODO: Conduct a check here.\r
+  Ip6RemoveAddr (\r
+    IpSb,\r
+    &IpSb->DefaultInterface->AddressList,\r
+    &IpSb->DefaultInterface->AddressCount,\r
+    NULL,\r
+    0\r
+    );\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    //\r
+    // remove all pending DAD entries for the global addresses.\r
+    //\r
+    IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);\r
+\r
+    NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {\r
+      DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);\r
+\r
+      if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) {\r
+        //\r
+        // Fail this DAD entry if the address is not link-local.\r
+        //\r
+        Ip6OnDADFinished (FALSE, IpIf, DadEntry);\r
+      }\r
+    }\r
+  }\r
+\r
+  if (NewPolicy == Ip6ConfigPolicyAutomatic) {\r
+    //\r
+    // Set paramters to trigger router solicitation sending in timer handler.\r
+    //\r
+    IpSb->RouterAdvertiseReceived = FALSE;\r
+    IpSb->SolicitTimer            = IP6_MAX_RTR_SOLICITATIONS;\r
+    //\r
+    // delay 1 second\r
+    //\r
+    IpSb->Ticks                   = (UINT32) IP6_GET_TICKS (IP6_ONE_SECOND_IN_MS);\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration.\r
+\r
+  @param[in]     Instance      Pointer to the IP6 config instance data.\r
+  @param[in]     OtherInfoOnly If FALSE, get stateful address and other information\r
+                               via DHCPv6. Otherwise, only get the other information.\r
+\r
+  @retval    EFI_SUCCESS       The operation finished successfully.\r
+  @retval    EFI_UNSUPPORTED   The DHCP6 driver is not available.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigStartStatefulAutoConfig (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN BOOLEAN              OtherInfoOnly\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  IP6_SERVICE               *IpSb;\r
+  EFI_DHCP6_CONFIG_DATA     Dhcp6CfgData;\r
+  EFI_DHCP6_PROTOCOL        *Dhcp6;\r
+  EFI_DHCP6_PACKET_OPTION   *OptList[1];\r
+  UINT16                    OptBuf[4];\r
+  EFI_DHCP6_PACKET_OPTION   *Oro;\r
+  EFI_DHCP6_RETRANSMISSION  InfoReqReXmit;\r
+\r
+  //\r
+  // A host must not invoke stateful address configuration if it is already\r
+  // participating in the statuful protocol as a result of an earlier advertisement.\r
+  //\r
+  if (Instance->Dhcp6Handle != NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+\r
+  Instance->OtherInfoOnly = OtherInfoOnly;\r
+\r
+  Status = NetLibCreateServiceChild (\r
+             IpSb->Controller,\r
+             IpSb->Image,\r
+             &gEfiDhcp6ServiceBindingProtocolGuid,\r
+             &Instance->Dhcp6Handle\r
+             );\r
+\r
+  if (Status == EFI_UNSUPPORTED) {\r
+    //\r
+    // No DHCPv6 Service Binding protocol, register a notify.\r
+    //\r
+    if (Instance->Dhcp6SbNotifyEvent == NULL) {\r
+      Instance->Dhcp6SbNotifyEvent = EfiCreateProtocolNotifyEvent (\r
+                                       &gEfiDhcp6ServiceBindingProtocolGuid,\r
+                                       TPL_CALLBACK,\r
+                                       Ip6ConfigOnDhcp6SbInstalled,\r
+                                       (VOID *) Instance,\r
+                                       &Instance->Registration\r
+                                       );\r
+    }\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Instance->Dhcp6SbNotifyEvent != NULL) {\r
+    gBS->CloseEvent (Instance->Dhcp6SbNotifyEvent);\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Instance->Dhcp6Handle,\r
+                  &gEfiDhcp6ProtocolGuid,\r
+                  (VOID **) &Instance->Dhcp6,\r
+                  IpSb->Image,\r
+                  IpSb->Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Dhcp6 = Instance->Dhcp6;\r
+  Dhcp6->Configure (Dhcp6, NULL);\r
+\r
+  //\r
+  // Set the exta options to send. Here we only want the option request option\r
+  // with DNS SERVERS.\r
+  //\r
+  Oro                         = (EFI_DHCP6_PACKET_OPTION *) OptBuf;\r
+  Oro->OpCode                 = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO);\r
+  Oro->OpLen                  = HTONS (2);\r
+  *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);\r
+  OptList[0]                  = Oro;\r
+\r
+  Status                      = EFI_SUCCESS;\r
+\r
+  if (!OtherInfoOnly) {\r
+    //\r
+    // Get stateful address and other information via DHCPv6.\r
+    //\r
+    Dhcp6CfgData.Dhcp6Callback         = NULL;\r
+    Dhcp6CfgData.CallbackContext       = NULL;\r
+    Dhcp6CfgData.OptionCount           = 1;\r
+    Dhcp6CfgData.OptionList            = &OptList[0];\r
+    Dhcp6CfgData.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;\r
+    Dhcp6CfgData.IaDescriptor.IaId     = Instance->IaId;\r
+    Dhcp6CfgData.IaInfoEvent           = Instance->Dhcp6Event;\r
+    Dhcp6CfgData.ReconfigureAccept     = FALSE;\r
+    Dhcp6CfgData.RapidCommit           = FALSE;\r
+    Dhcp6CfgData.SolicitRetransmission = NULL;\r
+\r
+    Status = Dhcp6->Configure (Dhcp6, &Dhcp6CfgData);\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+\r
+      if (IpSb->LinkLocalOk) {\r
+        Status = Dhcp6->Start (Dhcp6);\r
+      } else {\r
+        IpSb->Dhcp6NeedStart = TRUE;\r
+      }\r
+\r
+    }\r
+  } else {\r
+    //\r
+    // Only get other information via DHCPv6, this doesn't require a config\r
+    // action.\r
+    //\r
+    InfoReqReXmit.Irt = 4;\r
+    InfoReqReXmit.Mrc = 64;\r
+    InfoReqReXmit.Mrt = 60;\r
+    InfoReqReXmit.Mrd = 0;\r
+\r
+    if (IpSb->LinkLocalOk) {\r
+      Status = Dhcp6->InfoRequest (\r
+                        Dhcp6,\r
+                        TRUE,\r
+                        Oro,\r
+                        0,\r
+                        NULL,\r
+                        &InfoReqReXmit,\r
+                        Instance->Dhcp6Event,\r
+                        Ip6ConfigOnDhcp6Reply,\r
+                        Instance\r
+                        );\r
+    } else {\r
+      IpSb->Dhcp6NeedInfoRequest = TRUE;\r
+    }\r
+\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Signal the registered event. It is the callback routine for NetMapIterate.\r
+\r
+  @param[in]  Map    Points to the list of registered event.\r
+  @param[in]  Item   The registered event.\r
+  @param[in]  Arg    Not used.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ConfigSignalEvent (\r
+  IN NET_MAP                *Map,\r
+  IN NET_MAP_ITEM           *Item,\r
+  IN VOID                   *Arg\r
+  )\r
+{\r
+  gBS->SignalEvent ((EFI_EVENT) Item->Key);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Read the configuration data from variable storage according to the VarName and\r
+  gEfiIp6ConfigProtocolGuid. It checks the integrity of variable data. If the\r
+  data is corrupted, it clears the variable data to ZERO. Othewise, it outputs the\r
+  configuration data to IP6_CONFIG_INSTANCE.\r
+\r
+  @param[in]      VarName  The pointer to the variable name\r
+  @param[in, out] Instance The pointer to the IP6 config instance data.\r
+\r
+  @retval EFI_NOT_FOUND         The variable can not be found or already corrupted.\r
+  @retval EFI_OUT_OF_RESOURCES  Fail to allocate resource to complete the operation.\r
+  @retval EFI_SUCCESS           The configuration data was retrieved successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigReadConfigData (\r
+  IN     CHAR16               *VarName,\r
+  IN OUT IP6_CONFIG_INSTANCE  *Instance\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  UINTN                   VarSize;\r
+  IP6_CONFIG_VARIABLE     *Variable;\r
+  IP6_CONFIG_DATA_ITEM    *DataItem;\r
+  UINTN                   Index;\r
+  IP6_CONFIG_DATA_RECORD  DataRecord;\r
+  CHAR8                   *Data;\r
+\r
+  //\r
+  // Try to read the configuration variable.\r
+  //\r
+  VarSize = 0;\r
+  Status  = gRT->GetVariable (\r
+                   VarName,\r
+                   &gEfiIp6ConfigProtocolGuid,\r
+                   NULL,\r
+                   &VarSize,\r
+                   NULL\r
+                   );\r
+\r
+  if (Status == EFI_BUFFER_TOO_SMALL) {\r
+    //\r
+    // Allocate buffer and read the config variable.\r
+    //\r
+    Variable = AllocatePool (VarSize);\r
+    if (Variable == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    Status = gRT->GetVariable (\r
+                    VarName,\r
+                    &gEfiIp6ConfigProtocolGuid,\r
+                    NULL,\r
+                    &VarSize,\r
+                    Variable\r
+                    );\r
+    if (EFI_ERROR (Status) || (UINT16) (~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize)) != 0) {\r
+      //\r
+      // GetVariable still error or the variable is corrupted.\r
+      // Fall back to the default value.\r
+      //\r
+      FreePool (Variable);\r
+\r
+      //\r
+      // Remove the problematic variable and return EFI_NOT_FOUND, a new\r
+      // variable will be set again.\r
+      //\r
+      gRT->SetVariable (\r
+             VarName,\r
+             &gEfiIp6ConfigProtocolGuid,\r
+             IP6_CONFIG_VARIABLE_ATTRIBUTE,\r
+             0,\r
+             NULL\r
+             );\r
+\r
+      return EFI_NOT_FOUND;\r
+    }\r
+\r
+    //\r
+    // Get the IAID we use.\r
+    //\r
+    Instance->IaId = Variable->IaId;\r
+\r
+    for (Index = 0; Index < Variable->DataRecordCount; Index++) {\r
+\r
+      CopyMem (&DataRecord, &Variable->DataRecord[Index], sizeof (DataRecord));\r
+\r
+      DataItem = &Instance->DataItem[DataRecord.DataType];\r
+      if (DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED) &&\r
+          (DataItem->DataSize != DataRecord.DataSize)\r
+          ) {\r
+        //\r
+        // Perhaps a corrupted data record...\r
+        //\r
+        continue;\r
+      }\r
+\r
+      if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {\r
+        //\r
+        // This data item has variable length data.\r
+        //\r
+        DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize);\r
+        if (DataItem->Data.Ptr == NULL) {\r
+          //\r
+          // no memory resource\r
+          //\r
+          continue;\r
+        }\r
+      }\r
+\r
+      Data = (CHAR8 *) Variable + DataRecord.Offset;\r
+      CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize);\r
+\r
+      DataItem->DataSize = DataRecord.DataSize;\r
+      DataItem->Status   = EFI_SUCCESS;\r
+    }\r
+\r
+    FreePool (Variable);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Write the configuration data from IP6_CONFIG_INSTANCE to variable storage.\r
+\r
+  @param[in]      VarName  The pointer to the variable name.\r
+  @param[in]      Instance The pointer to the IP6 configuration instance data.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Fail to allocate resource to complete the operation.\r
+  @retval EFI_SUCCESS           The configuration data is written successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigWriteConfigData (\r
+  IN CHAR16               *VarName,\r
+  IN IP6_CONFIG_INSTANCE  *Instance\r
+  )\r
+{\r
+  UINTN                   Index;\r
+  UINTN                   VarSize;\r
+  IP6_CONFIG_DATA_ITEM    *DataItem;\r
+  IP6_CONFIG_VARIABLE     *Variable;\r
+  IP6_CONFIG_DATA_RECORD  *DataRecord;\r
+  CHAR8                   *Heap;\r
+  EFI_STATUS              Status;\r
+\r
+  VarSize = sizeof (IP6_CONFIG_VARIABLE) - sizeof (IP6_CONFIG_DATA_RECORD);\r
+\r
+  for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {\r
+\r
+    DataItem = &Instance->DataItem[Index];\r
+    if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {\r
+\r
+      VarSize += sizeof (IP6_CONFIG_DATA_RECORD) + DataItem->DataSize;\r
+    }\r
+  }\r
+\r
+  Variable = AllocatePool (VarSize);\r
+  if (Variable == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Variable->IaId            = Instance->IaId;\r
+  Heap                      = (CHAR8 *) Variable + VarSize;\r
+  Variable->DataRecordCount = 0;\r
+\r
+  for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {\r
+\r
+    DataItem = &Instance->DataItem[Index];\r
+    if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {\r
+\r
+      Heap -= DataItem->DataSize;\r
+      CopyMem (Heap, DataItem->Data.Ptr, DataItem->DataSize);\r
+\r
+      DataRecord           = &Variable->DataRecord[Variable->DataRecordCount];\r
+      DataRecord->DataType = (EFI_IP6_CONFIG_DATA_TYPE) Index;\r
+      DataRecord->DataSize = DataItem->DataSize;\r
+      DataRecord->Offset   = (UINT16) (Heap - (CHAR8 *) Variable);\r
+\r
+      Variable->DataRecordCount++;\r
+    }\r
+  }\r
+\r
+  Variable->Checksum = 0;\r
+  Variable->Checksum = (UINT16) ~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize);\r
+\r
+  Status = gRT->SetVariable (\r
+                  VarName,\r
+                  &gEfiIp6ConfigProtocolGuid,\r
+                  IP6_CONFIG_VARIABLE_ATTRIBUTE,\r
+                  VarSize,\r
+                  Variable\r
+                  );\r
+\r
+  FreePool (Variable);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The work function for EfiIp6ConfigGetData() to get the interface information\r
+  of the communication device this IP6Config instance manages.\r
+\r
+  @param[in]      Instance Pointer to the IP6 config instance data.\r
+  @param[in, out] DataSize On input, in bytes, the size of Data. On output, in\r
+                           bytes, the size of buffer required to store the specified\r
+                           configuration data.\r
+  @param[in]      Data     The data buffer in which the configuration data is returned.\r
+                           Ignored if DataSize is ZERO.\r
+\r
+  @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified\r
+                               configuration data, and the required size is\r
+                               returned in DataSize.\r
+  @retval EFI_SUCCESS          The specified configuration data was obtained.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigGetIfInfo (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN OUT UINTN            *DataSize,\r
+  IN VOID                 *Data      OPTIONAL\r
+  )\r
+{\r
+  IP6_SERVICE                    *IpSb;\r
+  UINTN                          Length;\r
+  IP6_CONFIG_DATA_ITEM           *Item;\r
+  EFI_IP6_CONFIG_INTERFACE_INFO  *IfInfo;\r
+  UINT32                         AddressCount;\r
+  UINT32                         RouteCount;\r
+\r
+  IpSb   = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+  Length = sizeof (EFI_IP6_CONFIG_INTERFACE_INFO);\r
+\r
+  //\r
+  // Calculate the required length, add the buffer size for AddressInfo and\r
+  // RouteTable\r
+  //\r
+  Ip6BuildEfiAddressList (IpSb, &AddressCount, NULL);\r
+  Ip6BuildEfiRouteTable (IpSb->RouteTable, &RouteCount, NULL);\r
+\r
+  Length += AddressCount * sizeof (EFI_IP6_ADDRESS_INFO) + RouteCount * sizeof (EFI_IP6_ROUTE_TABLE);\r
+\r
+  if (*DataSize < Length) {\r
+    *DataSize = Length;\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  //\r
+  // Copy the fixed size part of the interface info.\r
+  //\r
+  Item = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo];\r
+  IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data;\r
+  CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP6_CONFIG_INTERFACE_INFO));\r
+\r
+  //\r
+  // AddressInfo\r
+  //\r
+  IfInfo->AddressInfo = (EFI_IP6_ADDRESS_INFO *) (IfInfo + 1);\r
+  Ip6BuildEfiAddressList (IpSb, &IfInfo->AddressInfoCount, &IfInfo->AddressInfo);\r
+\r
+  //\r
+  // RouteTable\r
+  //\r
+  IfInfo->RouteTable = (EFI_IP6_ROUTE_TABLE *) (IfInfo->AddressInfo + IfInfo->AddressInfoCount);\r
+  Ip6BuildEfiRouteTable (IpSb->RouteTable, &IfInfo->RouteCount, &IfInfo->RouteTable);\r
+\r
+  if (IfInfo->AddressInfoCount == 0) {\r
+    IfInfo->AddressInfo = NULL;\r
+  }\r
+\r
+  if (IfInfo->RouteCount == 0) {\r
+    IfInfo->RouteTable = NULL;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  The work function for EfiIp6ConfigSetData() to set the alternative inteface ID\r
+  for the communication device managed by this IP6Config instance, if the link local\r
+  IPv6 addresses generated from the interface ID based on the default source the\r
+  EFI IPv6 Protocol uses is a duplicate address.\r
+\r
+  @param[in]     Instance Pointer to the IP6 configuration instance data.\r
+  @param[in]     DataSize Size of the buffer pointed to by Data in bytes.\r
+  @param[in]     Data     The data buffer to set.\r
+\r
+  @retval EFI_BAD_BUFFER_SIZE  The DataSize does not match the size of the type,\r
+                               8 bytes.\r
+  @retval EFI_SUCCESS          The specified configuration data for the EFI IPv6\r
+                               network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetAltIfId (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN UINTN                DataSize,\r
+  IN VOID                 *Data\r
+  )\r
+{\r
+  EFI_IP6_CONFIG_INTERFACE_ID  *OldIfId;\r
+  EFI_IP6_CONFIG_INTERFACE_ID  *NewIfId;\r
+  IP6_CONFIG_DATA_ITEM         *DataItem;\r
+\r
+  if (DataSize != sizeof (EFI_IP6_CONFIG_INTERFACE_ID)) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId];\r
+  OldIfId  = DataItem->Data.AltIfId;\r
+  NewIfId  = (EFI_IP6_CONFIG_INTERFACE_ID *) Data;\r
+\r
+  CopyMem (OldIfId, NewIfId, DataSize);\r
+  DataItem->Status = EFI_SUCCESS;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  The work function for EfiIp6ConfigSetData() to set the general configuration\r
+  policy for the EFI IPv6 network stack that is running on the communication device\r
+  managed by this IP6Config instance. The policy will affect other configuration settings.\r
+\r
+  @param[in]     Instance Pointer to the IP6 config instance data.\r
+  @param[in]     DataSize Size of the buffer pointed to by Data in bytes.\r
+  @param[in]     Data     The data buffer to set.\r
+\r
+  @retval EFI_INVALID_PARAMETER The to be set policy is invalid.\r
+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type.\r
+  @retval EFI_ABORTED           The new policy equals the current policy.\r
+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6\r
+                                network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetPolicy (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN UINTN                DataSize,\r
+  IN VOID                 *Data\r
+  )\r
+{\r
+  EFI_IP6_CONFIG_POLICY  NewPolicy;\r
+  IP6_CONFIG_DATA_ITEM   *DataItem;\r
+  IP6_SERVICE            *IpSb;\r
+\r
+  if (DataSize != sizeof (EFI_IP6_CONFIG_POLICY)) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  NewPolicy = *((EFI_IP6_CONFIG_POLICY *) Data);\r
+\r
+  if (NewPolicy > Ip6ConfigPolicyAutomatic) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (NewPolicy == Instance->Policy) {\r
+\r
+    return EFI_ABORTED;\r
+  } else {\r
+\r
+    if (NewPolicy == Ip6ConfigPolicyAutomatic) {\r
+      //\r
+      // Clean the ManualAddress, Gateway and DnsServers, shrink the variable\r
+      // data size, and fire up all the related events.\r
+      //\r
+      DataItem           = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];\r
+      if (DataItem->Data.Ptr != NULL) {\r
+        FreePool (DataItem->Data.Ptr);\r
+      }\r
+      DataItem->Data.Ptr = NULL;\r
+      DataItem->DataSize = 0;\r
+      DataItem->Status   = EFI_NOT_FOUND;\r
+      NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);\r
+\r
+      DataItem           = &Instance->DataItem[Ip6ConfigDataTypeGateway];\r
+      if (DataItem->Data.Ptr != NULL) {\r
+        FreePool (DataItem->Data.Ptr);\r
+      }\r
+      DataItem->Data.Ptr = NULL;\r
+      DataItem->DataSize = 0;\r
+      DataItem->Status   = EFI_NOT_FOUND;\r
+      NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);\r
+\r
+      DataItem           = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];\r
+      DataItem->Data.Ptr = NULL;\r
+      DataItem->DataSize = 0;\r
+      DataItem->Status   = EFI_NOT_FOUND;\r
+      NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);\r
+    } else {\r
+      //\r
+      // The policy is changed from automatic to manual. Stop the DHCPv6 process\r
+      // and destroy the DHCPv6 child.\r
+      //\r
+      if (Instance->Dhcp6Handle != NULL) {\r
+        Ip6ConfigDestroyDhcp6 (Instance);\r
+      }\r
+    }\r
+\r
+    IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+    Ip6ConfigOnPolicyChanged (IpSb, NewPolicy);\r
+\r
+    Instance->Policy = NewPolicy;\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+/**\r
+  The work function for EfiIp6ConfigSetData() to set the number of consecutive\r
+  Neighbor Solicitation messages sent while performing Duplicate Address Detection\r
+  on a tentative address. A value of ZERO indicates that Duplicate Address Detection\r
+  will not be performed on a tentative address.\r
+\r
+  @param[in]     The Instance Pointer to the IP6 config instance data.\r
+  @param[in]     DataSize Size of the buffer pointed to by Data in bytes.\r
+  @param[in]     Data     The data buffer to set.\r
+\r
+  @retval EFI_BAD_BUFFER_SIZE  The DataSize does not match the size of the type.\r
+  @retval EFI_ABORTED          The new transmit count equals the current configuration.\r
+  @retval EFI_SUCCESS          The specified configuration data for the EFI IPv6\r
+                               network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetDadXmits (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN UINTN                DataSize,\r
+  IN VOID                 *Data\r
+  )\r
+{\r
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  *OldDadXmits;\r
+\r
+  if (DataSize != sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS)) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  OldDadXmits = Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits].Data.DadXmits;\r
+\r
+  if ((*(UINT32 *) Data) == OldDadXmits->DupAddrDetectTransmits) {\r
+\r
+    return EFI_ABORTED;\r
+  } else {\r
+\r
+    OldDadXmits->DupAddrDetectTransmits = *((UINT32 *) Data);\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+/**\r
+  The callback function for Ip6SetAddr. The prototype is defined\r
+  as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed\r
+  for the manual address set by Ip6ConfigSetMaunualAddress.\r
+\r
+  @param[in]     IsDadPassed   If TRUE, Duplicate Address Detection passed.\r
+  @param[in]     TargetAddress The tentative IPv6 address to be checked.\r
+  @param[in]     Context       Pointer to the IP6 configuration instance data.\r
+\r
+**/\r
+VOID\r
+Ip6ManualAddrDadCallback (\r
+  IN BOOLEAN           IsDadPassed,\r
+  IN EFI_IPv6_ADDRESS  *TargetAddress,\r
+  IN VOID              *Context\r
+  )\r
+{\r
+  IP6_CONFIG_INSTANCE            *Instance;\r
+  UINTN                          Index;\r
+  IP6_CONFIG_DATA_ITEM           *Item;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS  *ManualAddr;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS  *PassedAddr;\r
+  UINTN                          DadPassCount;\r
+  UINTN                          DadFailCount;\r
+  IP6_SERVICE                    *IpSb;\r
+\r
+  Instance   = (IP6_CONFIG_INSTANCE *) Context;\r
+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+  Item       = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];\r
+  ManualAddr = NULL;\r
+\r
+  for (Index = 0; Index < Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); Index++) {\r
+    //\r
+    // Find the original tag used to place into the NET_MAP.\r
+    //\r
+    ManualAddr = Item->Data.ManualAddress + Index;\r
+    if (EFI_IP6_EQUAL (TargetAddress, &ManualAddr->Address)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  ASSERT (Index != Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
+\r
+  if (IsDadPassed) {\r
+    NetMapInsertTail (&Instance->DadPassedMap, ManualAddr, NULL);\r
+  } else {\r
+    NetMapInsertTail (&Instance->DadFailedMap, ManualAddr, NULL);\r
+  }\r
+\r
+  DadPassCount = NetMapGetCount (&Instance->DadPassedMap);\r
+  DadFailCount = NetMapGetCount (&Instance->DadFailedMap);\r
+\r
+  if ((DadPassCount + DadFailCount) == (Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS))) {\r
+    //\r
+    // All addresses have finished the configuration process.\r
+    //\r
+    if (DadFailCount != 0) {\r
+      //\r
+      // There is at least one duplicate address.\r
+      //\r
+      FreePool (Item->Data.Ptr);\r
+\r
+      Item->DataSize = DadPassCount * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);\r
+      if (Item->DataSize == 0) {\r
+        //\r
+        // All failed, bad luck.\r
+        //\r
+        Item->Data.Ptr = NULL;\r
+        Item->Status   = EFI_NOT_FOUND;\r
+      } else {\r
+        //\r
+        // Part of addresses are detected to be duplicates, so update the\r
+        // data with those passed.\r
+        //\r
+        PassedAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AllocatePool (Item->DataSize);\r
+        ASSERT (PassedAddr != NULL);\r
+\r
+        Item->Data.Ptr = PassedAddr;\r
+        Item->Status   = EFI_SUCCESS;\r
+\r
+        while (!NetMapIsEmpty (&Instance->DadPassedMap)) {\r
+          ManualAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) NetMapRemoveHead (&Instance->DadPassedMap, NULL);\r
+          CopyMem (PassedAddr, ManualAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
+\r
+          PassedAddr++;\r
+        }\r
+\r
+        ASSERT ((UINTN) PassedAddr - (UINTN) Item->Data.Ptr == Item->DataSize);\r
+      }\r
+    } else {\r
+      //\r
+      // All addresses are valid.\r
+      //\r
+      Item->Status = EFI_SUCCESS;\r
+    }\r
+\r
+    //\r
+    // Remove the tags we put in the NET_MAPs.\r
+    //\r
+    while (!NetMapIsEmpty (&Instance->DadFailedMap)) {\r
+      NetMapRemoveHead (&Instance->DadFailedMap, NULL);\r
+    }\r
+\r
+    while (!NetMapIsEmpty (&Instance->DadPassedMap)) {\r
+      NetMapRemoveHead (&Instance->DadPassedMap, NULL);\r
+    }\r
+\r
+    //\r
+    // Signal the waiting events.\r
+    //\r
+    NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL);\r
+    IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+    Ip6ConfigWriteConfigData (IpSb->MacString, Instance);\r
+  }\r
+}\r
+\r
+/**\r
+  The work function for EfiIp6ConfigSetData() to set the station addresses manually\r
+  for the EFI IPv6 network stack. It is only configurable when the policy is\r
+  Ip6ConfigPolicyManual.\r
+\r
+  @param[in]     Instance Pointer to the IP6 configuration instance data.\r
+  @param[in]     DataSize Size of the buffer pointed to by Data in bytes.\r
+  @param[in]     Data     The data buffer to set.\r
+\r
+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type.\r
+  @retval EFI_WRITE_PROTECTED   The specified configuration data cannot be set\r
+                                under the current policy.\r
+  @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  Fail to allocate resource to complete the operation.\r
+  @retval EFI_NOT_READY         An asynchrous process is invoked to set the specified\r
+                                configuration data, and the process is not finished.\r
+  @retval EFI_ABORTED           The manual addresses to be set equal current\r
+                                configuration.\r
+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6\r
+                                network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetMaunualAddress (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN UINTN                DataSize,\r
+  IN VOID                 *Data\r
+  )\r
+{\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS  *NewAddress;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS  *TmpAddress;\r
+  IP6_CONFIG_DATA_ITEM           *DataItem;\r
+  UINTN                          NewAddressCount;\r
+  UINTN                          Index1;\r
+  UINTN                          Index2;\r
+  IP6_SERVICE                    *IpSb;\r
+  IP6_ADDRESS_INFO               *CurrentAddrInfo;\r
+  IP6_ADDRESS_INFO               *Copy;\r
+  LIST_ENTRY                     CurrentSourceList;\r
+  UINT32                         CurrentSourceCount;\r
+  LIST_ENTRY                     *Entry;\r
+  LIST_ENTRY                     *Entry2;\r
+  IP6_INTERFACE                  *IpIf;\r
+  IP6_PREFIX_LIST_ENTRY          *PrefixEntry;\r
+  EFI_STATUS                     Status;\r
+  BOOLEAN                        IsUpdated;\r
+\r
+  ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY);\r
+\r
+  if (((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0) || (DataSize == 0)) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  if (Instance->Policy != Ip6ConfigPolicyManual) {\r
+    return EFI_WRITE_PROTECTED;\r
+  }\r
+\r
+  NewAddressCount = DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);\r
+  NewAddress      = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) Data;\r
+\r
+  for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) {\r
+\r
+    if (NetIp6IsLinkLocalAddr (&NewAddress->Address)    ||\r
+        !NetIp6IsValidUnicast (&NewAddress->Address)    ||\r
+        (NewAddress->PrefixLength > 128)\r
+        ) {\r
+      //\r
+      // make sure the IPv6 address is unicast and not link-local address &&\r
+      // the prefix length is valid.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    TmpAddress = NewAddress + 1;\r
+    for (Index2 = Index1 + 1; Index2 < NewAddressCount; Index2++, TmpAddress++) {\r
+      //\r
+      // Any two addresses in the array can't be equal.\r
+      //\r
+      if (EFI_IP6_EQUAL (&TmpAddress->Address, &NewAddress->Address)) {\r
+\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+\r
+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+\r
+  //\r
+  // Build the current source address list.\r
+  //\r
+  InitializeListHead (&CurrentSourceList);\r
+  CurrentSourceCount = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);\r
+\r
+    NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {\r
+      CurrentAddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+      Copy            = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), CurrentAddrInfo);\r
+      if (Copy == NULL) {\r
+        break;\r
+      }\r
+\r
+      InsertTailList (&CurrentSourceList, &Copy->Link);\r
+      CurrentSourceCount++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Update the value... a long journey starts\r
+  //\r
+  NewAddress = AllocateCopyPool (DataSize, Data);\r
+  if (NewAddress == NULL) {\r
+    Ip6RemoveAddr (NULL, &CurrentSourceList, &CurrentSourceCount, NULL, 0);\r
+\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Store the new data, and init the DataItem status to EFI_NOT_READY because\r
+  // we may have an asynchronous configuration process.\r
+  //\r
+  DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];\r
+  if (DataItem->Data.Ptr != NULL) {\r
+    FreePool (DataItem->Data.Ptr);\r
+  }\r
+  DataItem->Data.Ptr = NewAddress;\r
+  DataItem->DataSize = DataSize;\r
+  DataItem->Status   = EFI_NOT_READY;\r
+\r
+  //\r
+  // Trigger DAD, it's an asynchronous process.\r
+  //\r
+  IsUpdated  = FALSE;\r
+\r
+  for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) {\r
+    if (Ip6IsOneOfSetAddress (IpSb, &NewAddress->Address, NULL, &CurrentAddrInfo)) {\r
+      ASSERT (CurrentAddrInfo != NULL);\r
+      //\r
+      // Remove this already existing source address from the CurrentSourceList\r
+      // built before.\r
+      //\r
+      Ip6RemoveAddr (\r
+        NULL,\r
+        &CurrentSourceList,\r
+        &CurrentSourceCount,\r
+        &CurrentAddrInfo->Address,\r
+        128\r
+        );\r
+\r
+      //\r
+      // This manual address is already in use, see whether prefix length is changed.\r
+      //\r
+      if (NewAddress->PrefixLength != CurrentAddrInfo->PrefixLength) {\r
+        //\r
+        // Remove the on-link prefix table, the route entry will be removed\r
+        // implicitly.\r
+        //\r
+        PrefixEntry = Ip6FindPrefixListEntry (\r
+                        IpSb,\r
+                        TRUE,\r
+                        CurrentAddrInfo->PrefixLength,\r
+                        &CurrentAddrInfo->Address\r
+                        );\r
+        if (PrefixEntry != NULL) {\r
+          Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE);\r
+        }\r
+\r
+        //\r
+        // Save the prefix length.\r
+        //\r
+        CurrentAddrInfo->PrefixLength = NewAddress->PrefixLength;\r
+        IsUpdated = TRUE;\r
+      }\r
+\r
+      //\r
+      // create a new on-link prefix entry.\r
+      //\r
+      PrefixEntry = Ip6FindPrefixListEntry (\r
+                      IpSb,\r
+                      TRUE,\r
+                      NewAddress->PrefixLength,\r
+                      &NewAddress->Address\r
+                      );\r
+      if (PrefixEntry == NULL) {\r
+        Ip6CreatePrefixListEntry (\r
+          IpSb,\r
+          TRUE,\r
+          (UINT32) IP6_INFINIT_LIFETIME,\r
+          (UINT32) IP6_INFINIT_LIFETIME,\r
+          NewAddress->PrefixLength,\r
+          &NewAddress->Address\r
+          );\r
+      }\r
+\r
+      CurrentAddrInfo->IsAnycast = NewAddress->IsAnycast;\r
+      //\r
+      // Artificially mark this address passed DAD be'coz it is already in use.\r
+      //\r
+      Ip6ManualAddrDadCallback (TRUE, &NewAddress->Address, Instance);\r
+    } else {\r
+      //\r
+      // A new address.\r
+      //\r
+      IsUpdated = TRUE;\r
+\r
+      //\r
+      // Set the new address, this will trigger DAD and activate the address if\r
+      // DAD succeeds.\r
+      //\r
+      Ip6SetAddress (\r
+        IpSb->DefaultInterface,\r
+        &NewAddress->Address,\r
+        NewAddress->IsAnycast,\r
+        NewAddress->PrefixLength,\r
+        (UINT32) IP6_INFINIT_LIFETIME,\r
+        (UINT32) IP6_INFINIT_LIFETIME,\r
+        Ip6ManualAddrDadCallback,\r
+        Instance\r
+        );\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check the CurrentSourceList, it now contains those addresses currently in\r
+  // use and will be removed.\r
+  //\r
+  IpIf = IpSb->DefaultInterface;\r
+\r
+  while (!IsListEmpty (&CurrentSourceList)) {\r
+    IsUpdated = TRUE;\r
+\r
+    CurrentAddrInfo = NET_LIST_HEAD (&CurrentSourceList, IP6_ADDRESS_INFO, Link);\r
+\r
+    //\r
+    // This local address is going to be removed, the IP instances that are\r
+    // currently using it will be destroyed.\r
+    //\r
+    Ip6RemoveAddr (\r
+      IpSb,\r
+      &IpIf->AddressList,\r
+      &IpIf->AddressCount,\r
+      &CurrentAddrInfo->Address,\r
+      128\r
+      );\r
+\r
+    //\r
+    // Remove the on-link prefix table, the route entry will be removed\r
+    // implicitly.\r
+    //\r
+    PrefixEntry = Ip6FindPrefixListEntry (\r
+                    IpSb,\r
+                    TRUE,\r
+                    CurrentAddrInfo->PrefixLength,\r
+                    &CurrentAddrInfo->Address\r
+                    );\r
+    if (PrefixEntry != NULL) {\r
+      Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE);\r
+    }\r
+\r
+    RemoveEntryList (&CurrentAddrInfo->Link);\r
+    FreePool (CurrentAddrInfo);\r
+  }\r
+\r
+  if (IsUpdated) {\r
+    if (DataItem->Status == EFI_NOT_READY) {\r
+      //\r
+      // If DAD is disabled on this interface, the configuration process is\r
+      // actually synchronous, and the data item's status will be changed to\r
+      // the final status before we reach here, just check it.\r
+      //\r
+      Status = EFI_NOT_READY;\r
+    } else {\r
+      Status = EFI_SUCCESS;\r
+    }\r
+  } else {\r
+    //\r
+    // No update is taken, reset the status to success and return EFI_ABORTED.\r
+    //\r
+    DataItem->Status = EFI_SUCCESS;\r
+    Status           = EFI_ABORTED;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The work function for EfiIp6ConfigSetData() to set the gateway addresses manually\r
+  for the EFI IPv6 network stack that is running on the communication device that\r
+  this EFI IPv6 Configuration Protocol manages. It is not configurable when the policy is\r
+  Ip6ConfigPolicyAutomatic. The gateway addresses must be unicast IPv6 addresses.\r
+\r
+  @param[in]     Instance The pointer to the IP6 config instance data.\r
+  @param[in]     DataSize The size of the buffer pointed to by Data in bytes.\r
+  @param[in]     Data     The data buffer to set. This points to an array of\r
+                          EFI_IPv6_ADDRESS instances.\r
+\r
+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type.\r
+  @retval EFI_WRITE_PROTECTED   The specified configuration data cannot be set\r
+                                under the current policy.\r
+  @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to complete the operation.\r
+  @retval EFI_ABORTED           The manual gateway addresses to be set equal the\r
+                                current configuration.\r
+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6\r
+                                network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetGateway (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN UINTN                DataSize,\r
+  IN VOID                 *Data\r
+  )\r
+{\r
+  UINTN                 Index1;\r
+  UINTN                 Index2;\r
+  EFI_IPv6_ADDRESS      *OldGateway;\r
+  EFI_IPv6_ADDRESS      *NewGateway;\r
+  UINTN                 OldGatewayCount;\r
+  UINTN                 NewGatewayCount;\r
+  IP6_CONFIG_DATA_ITEM  *Item;\r
+  BOOLEAN               OneRemoved;\r
+  BOOLEAN               OneAdded;\r
+  IP6_SERVICE           *IpSb;\r
+  IP6_DEFAULT_ROUTER    *DefaultRouter;\r
+  VOID                  *Tmp;\r
+\r
+  if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  if (Instance->Policy != Ip6ConfigPolicyManual) {\r
+    return EFI_WRITE_PROTECTED;\r
+  }\r
+\r
+  NewGateway      = (EFI_IPv6_ADDRESS *) Data;\r
+  NewGatewayCount = DataSize / sizeof (EFI_IPv6_ADDRESS);\r
+  for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {\r
+\r
+    if (!NetIp6IsValidUnicast (NewGateway + Index1)) {\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) {\r
+      if (EFI_IP6_EQUAL (NewGateway + Index1, NewGateway + Index2)) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+\r
+  IpSb            = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+  Item            = &Instance->DataItem[Ip6ConfigDataTypeGateway];\r
+  OldGateway      = Item->Data.Gateway;\r
+  OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);\r
+  OneRemoved      = FALSE;\r
+  OneAdded        = FALSE;\r
+\r
+  if (NewGatewayCount != OldGatewayCount) {\r
+    Tmp = AllocatePool (DataSize);\r
+    if (Tmp == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  } else {\r
+    Tmp = NULL;\r
+  }\r
+\r
+  for (Index1 = 0; Index1 < OldGatewayCount; Index1++) {\r
+    //\r
+    // Find the gateways that are no long in the new setting and remove them.\r
+    //\r
+    for (Index2 = 0; Index2 < NewGatewayCount; Index2++) {\r
+      if (EFI_IP6_EQUAL (OldGateway + Index1, NewGateway + Index2)) {\r
+        OneRemoved = TRUE;\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (Index2 == NewGatewayCount) {\r
+      //\r
+      // Remove this default router.\r
+      //\r
+      DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1);\r
+      if (DefaultRouter != NULL) {\r
+        Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+      }\r
+    }\r
+  }\r
+\r
+  for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {\r
+\r
+    DefaultRouter = Ip6FindDefaultRouter (IpSb, NewGateway + Index1);\r
+    if (DefaultRouter == NULL) {\r
+      Ip6CreateDefaultRouter (IpSb, NewGateway + Index1, IP6_INF_ROUTER_LIFETIME);\r
+      OneAdded = TRUE;\r
+    }\r
+  }\r
+\r
+  if (!OneRemoved && !OneAdded) {\r
+    Item->Status = EFI_SUCCESS;\r
+    return EFI_ABORTED;\r
+  } else {\r
+\r
+    if (Tmp != NULL) {\r
+      if (Item->Data.Ptr != NULL) {\r
+        FreePool (Item->Data.Ptr);\r
+      }\r
+      Item->Data.Ptr = Tmp;\r
+    }\r
+\r
+    CopyMem (Item->Data.Ptr, Data, DataSize);\r
+    Item->DataSize = DataSize;\r
+    Item->Status   = EFI_SUCCESS;\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+/**\r
+  The work function for EfiIp6ConfigSetData() to set the DNS server list for the\r
+  EFI IPv6 network stack running on the communication device that this EFI IPv6\r
+  Configuration Protocol manages. It is not configurable when the policy is\r
+  Ip6ConfigPolicyAutomatic. The DNS server addresses must be unicast IPv6 addresses.\r
+\r
+  @param[in]     Instance The pointer to the IP6 config instance data.\r
+  @param[in]     DataSize The size of the buffer pointed to by Data in bytes.\r
+  @param[in]     Data     The data buffer to set, points to an array of\r
+                          EFI_IPv6_ADDRESS instances.\r
+\r
+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type.\r
+  @retval EFI_WRITE_PROTECTED   The specified configuration data cannot be set\r
+                                under the current policy.\r
+  @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to complete the operation.\r
+  @retval EFI_ABORTED           The DNS server addresses to be set equal the current\r
+                                configuration.\r
+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6\r
+                                network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetDnsServer (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN UINTN                DataSize,\r
+  IN VOID                 *Data\r
+  )\r
+{\r
+  UINTN                 OldIndex;\r
+  UINTN                 NewIndex;\r
+  UINTN                 Index1;\r
+  EFI_IPv6_ADDRESS      *OldDns;\r
+  EFI_IPv6_ADDRESS      *NewDns;\r
+  UINTN                 OldDnsCount;\r
+  UINTN                 NewDnsCount;\r
+  IP6_CONFIG_DATA_ITEM  *Item;\r
+  BOOLEAN               OneAdded;\r
+  VOID                  *Tmp;\r
+\r
+  if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  if (Instance->Policy != Ip6ConfigPolicyManual) {\r
+    return EFI_WRITE_PROTECTED;\r
+  }\r
+\r
+  Item        = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];\r
+  NewDns      = (EFI_IPv6_ADDRESS *) Data;\r
+  OldDns      = Item->Data.DnsServers;\r
+  NewDnsCount = DataSize / sizeof (EFI_IPv6_ADDRESS);\r
+  OldDnsCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);\r
+  OneAdded    = FALSE;\r
+\r
+  if (NewDnsCount != OldDnsCount) {\r
+    Tmp = AllocatePool (DataSize);\r
+    if (Tmp == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  } else {\r
+    Tmp = NULL;\r
+  }\r
+\r
+  for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) {\r
+\r
+    if (!NetIp6IsValidUnicast (NewDns + NewIndex)) {\r
+      //\r
+      // The dns server address must be unicast.\r
+      //\r
+      FreePool (Tmp);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) {\r
+      if (EFI_IP6_EQUAL (NewDns + NewIndex, NewDns + Index1)) {\r
+        FreePool (Tmp);\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+\r
+    if (OneAdded) {\r
+      //\r
+      // If any address in the new setting is not in the old settings, skip the\r
+      // comparision below.\r
+      //\r
+      continue;\r
+    }\r
+\r
+    for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) {\r
+      if (EFI_IP6_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) {\r
+        //\r
+        // If found break out.\r
+        //\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (OldIndex == OldDnsCount) {\r
+      OneAdded = TRUE;\r
+    }\r
+  }\r
+\r
+  if (!OneAdded && (DataSize == Item->DataSize)) {\r
+    //\r
+    // No new item is added and the size is the same.\r
+    //\r
+    Item->Status = EFI_SUCCESS;\r
+    return EFI_ABORTED;\r
+  } else {\r
+    if (Tmp != NULL) {\r
+      if (Item->Data.Ptr != NULL) {\r
+        FreePool (Item->Data.Ptr);\r
+      }\r
+      Item->Data.Ptr = Tmp;\r
+    }\r
+\r
+    CopyMem (Item->Data.Ptr, Data, DataSize);\r
+    Item->DataSize = DataSize;\r
+    Item->Status   = EFI_SUCCESS;\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+/**\r
+  Generate the operational state of the interface this IP6 config instance manages\r
+  and output in EFI_IP6_CONFIG_INTERFACE_INFO.\r
+\r
+  @param[in]      IpSb     The pointer to the IP6 service binding instance.\r
+  @param[out]     IfInfo   The pointer to the IP6 configuration interface information structure.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigInitIfInfo (\r
+  IN  IP6_SERVICE                    *IpSb,\r
+  OUT EFI_IP6_CONFIG_INTERFACE_INFO  *IfInfo\r
+  )\r
+{\r
+  IfInfo->Name[0] = L'e';\r
+  IfInfo->Name[1] = L't';\r
+  IfInfo->Name[2] = L'h';\r
+  IfInfo->Name[3] = (CHAR16) (L'0' + IpSb->Ip6ConfigInstance.IfIndex);\r
+  IfInfo->Name[4] = 0;\r
+\r
+  IfInfo->IfType        = IpSb->SnpMode.IfType;\r
+  IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize;\r
+  CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize);\r
+}\r
+\r
+/**\r
+  Parse DHCPv6 reply packet to get the DNS server list.\r
+  It is the work function for Ip6ConfigOnDhcp6Reply and Ip6ConfigOnDhcp6Event.\r
+\r
+  @param[in]      Dhcp6    The pointer to the EFI_DHCP6_PROTOCOL instance.\r
+  @param[in, out] Instance The pointer to the IP6 configuration instance data.\r
+  @param[in]      Reply    The pointer to the DHCPv6 reply packet.\r
+\r
+  @retval EFI_SUCCESS      The DNS server address was retrieved from the reply packet.\r
+  @retval EFI_NOT_READY    The reply packet does not contain the DNS server option, or\r
+                           the DNS server address is not valid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigParseDhcpReply (\r
+  IN     EFI_DHCP6_PROTOCOL  *Dhcp6,\r
+  IN OUT IP6_CONFIG_INSTANCE *Instance,\r
+  IN     EFI_DHCP6_PACKET    *Reply\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  UINT32                   OptCount;\r
+  EFI_DHCP6_PACKET_OPTION  **OptList;\r
+  UINT16                   OpCode;\r
+  UINT16                   Length;\r
+  UINTN                    Index;\r
+  UINTN                    Index2;\r
+  EFI_IPv6_ADDRESS         *DnsServer;\r
+  IP6_CONFIG_DATA_ITEM     *Item;\r
+\r
+  //\r
+  // A DHCPv6 reply packet is received as the response to our InfoRequest\r
+  // packet.\r
+  //\r
+  OptCount = 0;\r
+  Status   = Dhcp6->Parse (Dhcp6, Reply, &OptCount, NULL);\r
+  if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  OptList = AllocatePool (OptCount * sizeof (EFI_DHCP6_PACKET_OPTION *));\r
+  if (OptList == NULL) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, OptList);\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_NOT_READY;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  for (Index = 0; Index < OptCount; Index++) {\r
+    //\r
+    // Go through all the options to check the ones we are interested in.\r
+    // The OpCode and Length are in network byte-order and may not be naturally\r
+    // aligned.\r
+    //\r
+    CopyMem (&OpCode, &OptList[Index]->OpCode, sizeof (OpCode));\r
+    OpCode = NTOHS (OpCode);\r
+\r
+    if (OpCode == IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS) {\r
+      CopyMem (&Length, &OptList[Index]->OpLen, sizeof (Length));\r
+      Length = NTOHS (Length);\r
+\r
+      if ((Length == 0) || ((Length % sizeof (EFI_IPv6_ADDRESS)) != 0)) {\r
+        //\r
+        // The length should be a multiple of 16 bytes.\r
+        //\r
+        Status = EFI_NOT_READY;\r
+        break;\r
+      }\r
+\r
+      //\r
+      // Validate the DnsServers: whether they are unicast addresses.\r
+      //\r
+      DnsServer = (EFI_IPv6_ADDRESS *) OptList[Index]->Data;\r
+      for (Index2 = 0; Index2 < Length / sizeof (EFI_IPv6_ADDRESS); Index2++) {\r
+        if (!NetIp6IsValidUnicast (DnsServer)) {\r
+          Status = EFI_NOT_READY;\r
+          goto ON_EXIT;\r
+        }\r
+\r
+        DnsServer++;\r
+      }\r
+\r
+      Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];\r
+\r
+      if (Item->DataSize != Length) {\r
+        if (Item->Data.Ptr != NULL) {\r
+          FreePool (Item->Data.Ptr);\r
+        }\r
+\r
+        Item->Data.Ptr = AllocatePool (Length);\r
+        ASSERT (Item->Data.Ptr != NULL);\r
+      }\r
+\r
+      CopyMem (Item->Data.Ptr, OptList[Index]->Data, Length);\r
+      Item->DataSize = Length;\r
+      Item->Status   = EFI_SUCCESS;\r
+\r
+      //\r
+      // Signal the waiting events.\r
+      //\r
+      NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL);\r
+\r
+      break;\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  FreePool (OptList);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The callback function for Ip6SetAddr. The prototype is defined\r
+  as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed\r
+  on the tentative address by DHCPv6 in Ip6ConfigOnDhcp6Event().\r
+\r
+  @param[in]     IsDadPassed   If TRUE, Duplicate Address Detection passes.\r
+  @param[in]     TargetAddress The tentative IPv6 address to be checked.\r
+  @param[in]     Context       Pointer to the IP6 configuration instance data.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigSetStatefulAddrCallback (\r
+  IN BOOLEAN           IsDadPassed,\r
+  IN EFI_IPv6_ADDRESS  *TargetAddress,\r
+  IN VOID              *Context\r
+  )\r
+{\r
+  IP6_CONFIG_INSTANCE  *Instance;\r
+\r
+  Instance = (IP6_CONFIG_INSTANCE *) Context;\r
+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+\r
+  //\r
+  // We should record the addresses that fail the DAD, and DECLINE them.\r
+  //\r
+  if (IsDadPassed) {\r
+    //\r
+    // Decrease the count, no interests in those passed DAD.\r
+    //\r
+    if (Instance->FailedIaAddressCount > 0 ) {\r
+      Instance->FailedIaAddressCount--;\r
+    }\r
+  } else {\r
+    //\r
+    // Record it.\r
+    //\r
+    IP6_COPY_ADDRESS (Instance->DeclineAddress + Instance->DeclineAddressCount, TargetAddress);\r
+    Instance->DeclineAddressCount++;\r
+  }\r
+\r
+  if (Instance->FailedIaAddressCount == Instance->DeclineAddressCount) {\r
+    //\r
+    // The checking on all addresses are finished.\r
+    //\r
+    if (Instance->DeclineAddressCount != 0) {\r
+      //\r
+      // Decline those duplicates.\r
+      //\r
+      Instance->Dhcp6->Decline (\r
+                         Instance->Dhcp6,\r
+                         Instance->DeclineAddressCount,\r
+                         Instance->DeclineAddress\r
+                         );\r
+    }\r
+\r
+    if (Instance->DeclineAddress != NULL) {\r
+      FreePool (Instance->DeclineAddress);\r
+    }\r
+    Instance->DeclineAddress      = NULL;\r
+    Instance->DeclineAddressCount = 0;\r
+  }\r
+}\r
+\r
+/**\r
+  The event handle routine when DHCPv6 process is finished or is updated.\r
+\r
+  @param[in]     Event         Not used.\r
+  @param[in]     Context       The pointer to the IP6 configuration instance data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6ConfigOnDhcp6Event (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  IP6_CONFIG_INSTANCE      *Instance;\r
+  EFI_DHCP6_PROTOCOL       *Dhcp6;\r
+  EFI_STATUS               Status;\r
+  EFI_DHCP6_MODE_DATA      Dhcp6ModeData;\r
+  EFI_DHCP6_IA             *Ia;\r
+  EFI_DHCP6_IA_ADDRESS     *IaAddr;\r
+  UINT32                   Index;\r
+  IP6_SERVICE              *IpSb;\r
+  IP6_ADDRESS_INFO         *AddrInfo;\r
+  IP6_INTERFACE            *IpIf;\r
+\r
+  Instance = (IP6_CONFIG_INSTANCE *) Context;\r
+\r
+  if ((Instance->Policy != Ip6ConfigPolicyAutomatic) || Instance->OtherInfoOnly) {\r
+    //\r
+    // IPv6 is not operating in the automatic policy now or\r
+    // the DHCPv6 information request message exchange is aborted.\r
+    //\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // The stateful address autoconfiguration is done or updated.\r
+  //\r
+  Dhcp6 = Instance->Dhcp6;\r
+\r
+  Status = Dhcp6->GetModeData (Dhcp6, &Dhcp6ModeData, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    return ;\r
+  }\r
+\r
+  IpSb   = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+  IpIf   = IpSb->DefaultInterface;\r
+  Ia     = Dhcp6ModeData.Ia;\r
+  IaAddr = Ia->IaAddress;\r
+\r
+  if (Instance->DeclineAddress != NULL) {\r
+    FreePool (Instance->DeclineAddress);\r
+  }\r
+\r
+  Instance->DeclineAddress = (EFI_IPv6_ADDRESS *) AllocatePool (Ia->IaAddressCount * sizeof (EFI_IPv6_ADDRESS));\r
+  if (Instance->DeclineAddress == NULL) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Instance->FailedIaAddressCount = Ia->IaAddressCount;\r
+  Instance->DeclineAddressCount   = 0;\r
+\r
+  for (Index = 0; Index < Ia->IaAddressCount; Index++, IaAddr++) {\r
+    if (Ia->IaAddress[Index].ValidLifetime != 0 && Ia->State == Dhcp6Bound) {\r
+      //\r
+      // Set this address, either it's a new address or with updated lifetimes.\r
+      // An appropriate prefix length will be set.\r
+      //\r
+      Ip6SetAddress (\r
+        IpIf,\r
+        &IaAddr->IpAddress,\r
+        FALSE,\r
+        0,\r
+        IaAddr->ValidLifetime,\r
+        IaAddr->PreferredLifetime,\r
+        Ip6ConfigSetStatefulAddrCallback,\r
+        Instance\r
+        );\r
+    } else {\r
+      //\r
+      // discard this address, artificially decrease the count as if this address\r
+      // passed DAD.\r
+      //\r
+      if (Ip6IsOneOfSetAddress (IpSb, &IaAddr->IpAddress, NULL, &AddrInfo)) {\r
+        ASSERT (AddrInfo != NULL);\r
+        Ip6RemoveAddr (\r
+          IpSb,\r
+          &IpIf->AddressList,\r
+          &IpIf->AddressCount,\r
+          &AddrInfo->Address,\r
+          AddrInfo->PrefixLength\r
+          );\r
+      }\r
+\r
+      if (Instance->FailedIaAddressCount > 0) {\r
+        Instance->FailedIaAddressCount--;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Parse the Reply packet to get the options we need.\r
+  //\r
+  if (Dhcp6ModeData.Ia->ReplyPacket != NULL) {\r
+    Ip6ConfigParseDhcpReply (Dhcp6, Instance, Dhcp6ModeData.Ia->ReplyPacket);\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  FreePool (Dhcp6ModeData.ClientId);\r
+  FreePool (Dhcp6ModeData.Ia);\r
+}\r
+\r
+/**\r
+  The event process routine when the DHCPv6 server is answered with a reply packet\r
+  for an information request.\r
+\r
+  @param[in]     This          Points to the EFI_DHCP6_PROTOCOL.\r
+  @param[in]     Context       The pointer to the IP6 configuration instance data.\r
+  @param[in]     Packet        The DHCPv6 reply packet.\r
+\r
+  @retval EFI_SUCCESS      The DNS server address was retrieved from the reply packet.\r
+  @retval EFI_NOT_READY    The reply packet does not contain the DNS server option, or\r
+                           the DNS server address is not valid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ConfigOnDhcp6Reply (\r
+  IN EFI_DHCP6_PROTOCOL  *This,\r
+  IN VOID                *Context,\r
+  IN EFI_DHCP6_PACKET    *Packet\r
+  )\r
+{\r
+  return Ip6ConfigParseDhcpReply (This, (IP6_CONFIG_INSTANCE *) Context, Packet);\r
+}\r
+\r
+/**\r
+  The event process routine when the DHCPv6 service binding protocol is installed\r
+  in the system.\r
+\r
+  @param[in]     Event         Not used.\r
+  @param[in]     Context       The pointer to the IP6 config instance data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6ConfigOnDhcp6SbInstalled (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  IP6_CONFIG_INSTANCE  *Instance;\r
+\r
+  Instance = (IP6_CONFIG_INSTANCE *) Context;\r
+\r
+  if ((Instance->Dhcp6Handle != NULL) || (Instance->Policy != Ip6ConfigPolicyAutomatic)) {\r
+    //\r
+    // The DHCP6 child is already created or the policy is no longer AUTOMATIC.\r
+    //\r
+    return ;\r
+  }\r
+\r
+  Ip6ConfigStartStatefulAutoConfig (Instance, Instance->OtherInfoOnly);\r
+}\r
+\r
+/**\r
+  Set the configuration for the EFI IPv6 network stack running on the communication\r
+  device this EFI IPv6 Configuration Protocol instance manages.\r
+\r
+  This function is used to set the configuration data of type DataType for the EFI\r
+  IPv6 network stack that is running on the communication device that this EFI IPv6\r
+  Configuration Protocol instance manages.\r
+\r
+  DataSize is used to calculate the count of structure instances in the Data for\r
+  a DataType in which multiple structure instances are allowed.\r
+\r
+  This function is always non-blocking. When setting some type of configuration data,\r
+  an asynchronous process is invoked to check the correctness of the data, such as\r
+  performing Duplicate Address Detection on the manually set local IPv6 addresses.\r
+  EFI_NOT_READY is returned immediately to indicate that such an asynchronous process\r
+  is invoked, and the process is not finished yet. The caller wanting to get the result\r
+  of the asynchronous process is required to call RegisterDataNotify() to register an\r
+  event on the specified configuration data. Once the event is signaled, the caller\r
+  can call GetData() to obtain the configuration data and know the result.\r
+  For other types of configuration data that do not require an asynchronous configuration\r
+  process, the result of the operation is immediately returned.\r
+\r
+  @param[in]     This           The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+  @param[in]     DataType       The type of data to set.\r
+  @param[in]     DataSize       Size of the buffer pointed to by Data in bytes.\r
+  @param[in]     Data           The data buffer to set. The type of the data buffer is\r
+                                associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6\r
+                                network stack was set successfully.\r
+  @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+                                - This is NULL.\r
+                                - Data is NULL.\r
+                                - One or more fields in Data do not match the requirement of the\r
+                                  data type indicated by DataType.\r
+  @retval EFI_WRITE_PROTECTED   The specified configuration data is read-only or the specified\r
+                                configuration data cannot be set under the current policy.\r
+  @retval EFI_ACCESS_DENIED     Another set operation on the specified configuration\r
+                                data is already in process.\r
+  @retval EFI_NOT_READY         An asynchronous process was invoked to set the specified\r
+                                configuration data, and the process is not finished yet.\r
+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type\r
+                                indicated by DataType.\r
+  @retval EFI_UNSUPPORTED       This DataType is not supported.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system error or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6ConfigSetData (\r
+  IN EFI_IP6_CONFIG_PROTOCOL    *This,\r
+  IN EFI_IP6_CONFIG_DATA_TYPE   DataType,\r
+  IN UINTN                      DataSize,\r
+  IN VOID                       *Data\r
+  )\r
+{\r
+  EFI_TPL              OldTpl;\r
+  EFI_STATUS           Status;\r
+  IP6_CONFIG_INSTANCE  *Instance;\r
+  IP6_SERVICE          *IpSb;\r
+\r
+  if ((This == NULL) || (Data == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DataType >= Ip6ConfigDataTypeMaximum) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb     = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Status = Instance->DataItem[DataType].Status;\r
+  if (Status != EFI_NOT_READY) {\r
+\r
+    if (Instance->DataItem[DataType].SetData == NULL) {\r
+      //\r
+      // This type of data is readonly.\r
+      //\r
+      Status = EFI_WRITE_PROTECTED;\r
+    } else {\r
+\r
+      Status = Instance->DataItem[DataType].SetData (Instance, DataSize, Data);\r
+      if (!EFI_ERROR (Status)) {\r
+        //\r
+        // Fire up the events registered with this type of data.\r
+        //\r
+        NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL);\r
+        Ip6ConfigWriteConfigData (IpSb->MacString, Instance);\r
+      } else if (Status == EFI_ABORTED) {\r
+        //\r
+        // The SetData is aborted because the data to set is the same with\r
+        // the one maintained.\r
+        //\r
+        Status = EFI_SUCCESS;\r
+        NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL);\r
+      }\r
+    }\r
+  } else {\r
+    //\r
+    // Another asynchornous process is on the way.\r
+    //\r
+    Status = EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get the configuration data for the EFI IPv6 network stack running on the communication\r
+  device that this EFI IPv6 Configuration Protocol instance manages.\r
+\r
+  This function returns the configuration data of type DataType for the EFI IPv6 network\r
+  stack running on the communication device that this EFI IPv6 Configuration Protocol instance\r
+  manages.\r
+\r
+  The caller is responsible for allocating the buffer used to return the specified\r
+  configuration data. The required size will be returned to the caller if the size of\r
+  the buffer is too small.\r
+\r
+  EFI_NOT_READY is returned if the specified configuration data is not ready due to an\r
+  asynchronous configuration process already in progress. The caller can call RegisterDataNotify()\r
+  to register an event on the specified configuration data. Once the asynchronous configuration\r
+  process is finished, the event will be signaled, and a subsequent GetData() call will return\r
+  the specified configuration data.\r
+\r
+  @param[in]      This           Pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+  @param[in]      DataType       The type of data to get.\r
+  @param[in, out] DataSize       On input, in bytes, the size of Data. On output, in bytes, the\r
+                                 size of buffer required to store the specified configuration data.\r
+  @param[in]     Data            The data buffer in which the configuration data is returned. The\r
+                                 type of the data buffer is associated with the DataType.\r
+                                 This is an optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+                                - This is NULL.\r
+                                - DataSize is NULL.\r
+                                - Data is NULL if *DataSize is not zero.\r
+  @retval EFI_BUFFER_TOO_SMALL  The size of Data is too small for the specified configuration data,\r
+                                and the required size is returned in DataSize.\r
+  @retval EFI_NOT_READY         The specified configuration data is not ready due to an\r
+                                asynchronous configuration process already in progress.\r
+  @retval EFI_NOT_FOUND         The specified configuration data is not found.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6ConfigGetData (\r
+  IN EFI_IP6_CONFIG_PROTOCOL    *This,\r
+  IN EFI_IP6_CONFIG_DATA_TYPE   DataType,\r
+  IN OUT UINTN                  *DataSize,\r
+  IN VOID                       *Data   OPTIONAL\r
+  )\r
+{\r
+  EFI_TPL               OldTpl;\r
+  EFI_STATUS            Status;\r
+  IP6_CONFIG_INSTANCE   *Instance;\r
+  IP6_CONFIG_DATA_ITEM  *DataItem;\r
+\r
+  if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DataType >= Ip6ConfigDataTypeMaximum) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);\r
+  DataItem = &Instance->DataItem[DataType];\r
+\r
+  Status   = Instance->DataItem[DataType].Status;\r
+  if (!EFI_ERROR (Status)) {\r
+\r
+    if (DataItem->GetData != NULL) {\r
+\r
+      Status = DataItem->GetData (Instance, DataSize, Data);\r
+    } else if (*DataSize < Instance->DataItem[DataType].DataSize) {\r
+      //\r
+      // Update the buffer length.\r
+      //\r
+      *DataSize = Instance->DataItem[DataType].DataSize;\r
+      Status    = EFI_BUFFER_TOO_SMALL;\r
+    } else {\r
+\r
+      *DataSize = Instance->DataItem[DataType].DataSize;\r
+      CopyMem (Data, Instance->DataItem[DataType].Data.Ptr, *DataSize);\r
+    }\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Register an event that is signaled whenever a configuration process on the specified\r
+  configuration data is done.\r
+\r
+  This function registers an event that is to be signaled whenever a configuration\r
+  process on the specified configuration data is performed. An event can be registered\r
+  for a different DataType simultaneously. The caller is responsible for determining\r
+  which type of configuration data causes the signaling of the event in such an event.\r
+\r
+  @param[in]     This           Pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+  @param[in]     DataType       The type of data to unregister the event for.\r
+  @param[in]     Event          The event to register.\r
+\r
+  @retval EFI_SUCCESS           The notification event for the specified configuration data is\r
+                                registered.\r
+  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.\r
+  @retval EFI_UNSUPPORTED       The configuration data type specified by DataType is not\r
+                                supported.\r
+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval EFI_ACCESS_DENIED     The Event is already registered for the DataType.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6ConfigRegisterDataNotify (\r
+  IN EFI_IP6_CONFIG_PROTOCOL    *This,\r
+  IN EFI_IP6_CONFIG_DATA_TYPE   DataType,\r
+  IN EFI_EVENT                  Event\r
+  )\r
+{\r
+  EFI_TPL              OldTpl;\r
+  EFI_STATUS           Status;\r
+  IP6_CONFIG_INSTANCE  *Instance;\r
+  NET_MAP              *EventMap;\r
+  NET_MAP_ITEM         *Item;\r
+\r
+  if ((This == NULL) || (Event == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DataType >= Ip6ConfigDataTypeMaximum) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  OldTpl    = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Instance  = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);\r
+  EventMap  = &Instance->DataItem[DataType].EventMap;\r
+\r
+  //\r
+  // Check whether this event is already registered for this DataType.\r
+  //\r
+  Item = NetMapFindKey (EventMap, Event);\r
+  if (Item == NULL) {\r
+\r
+    Status = NetMapInsertTail (EventMap, Event, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+  } else {\r
+\r
+    Status = EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Remove a previously registered event for the specified configuration data.\r
+\r
+  @param  This                   The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+  @param  DataType               The type of data to remove from the previously\r
+                                 registered event.\r
+  @param  Event                  The event to be unregistered.\r
+\r
+  @retval EFI_SUCCESS            The event registered for the specified\r
+                                 configuration data was removed.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL or Event is NULL.\r
+  @retval EFI_NOT_FOUND          The Event has not been registered for the\r
+                                 specified DataType.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6ConfigUnregisterDataNotify (\r
+  IN EFI_IP6_CONFIG_PROTOCOL    *This,\r
+  IN EFI_IP6_CONFIG_DATA_TYPE   DataType,\r
+  IN EFI_EVENT                  Event\r
+  )\r
+{\r
+  EFI_TPL              OldTpl;\r
+  EFI_STATUS           Status;\r
+  IP6_CONFIG_INSTANCE  *Instance;\r
+  NET_MAP_ITEM         *Item;\r
+\r
+  if ((This == NULL) || (Event == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DataType >= Ip6ConfigDataTypeMaximum) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);\r
+\r
+  Item = NetMapFindKey (&Instance->DataItem[DataType].EventMap, Event);\r
+  if (Item != NULL) {\r
+\r
+    NetMapRemoveItem (&Instance->DataItem[DataType].EventMap, Item, NULL);\r
+    Status = EFI_SUCCESS;\r
+  } else {\r
+\r
+    Status = EFI_NOT_FOUND;\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Initialize an IP6_CONFIG_INSTANCE.\r
+\r
+  @param[out]    Instance       The buffer of IP6_CONFIG_INSTANCE to be initialized.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to complete the operation.\r
+  @retval EFI_SUCCESS           The IP6_CONFIG_INSTANCE initialized successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigInitInstance (\r
+  OUT IP6_CONFIG_INSTANCE  *Instance\r
+  )\r
+{\r
+  IP6_SERVICE           *IpSb;\r
+  IP6_CONFIG_INSTANCE   *TmpInstance;\r
+  LIST_ENTRY            *Entry;\r
+  EFI_STATUS            Status;\r
+  UINTN                 Index;\r
+  UINT16                IfIndex;\r
+  IP6_CONFIG_DATA_ITEM  *DataItem;\r
+\r
+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+\r
+  Instance->Signature = IP6_CONFIG_INSTANCE_SIGNATURE;\r
+\r
+  //\r
+  // Determine the index of this interface.\r
+  //\r
+  IfIndex = 0;\r
+  NET_LIST_FOR_EACH (Entry, &mIp6ConfigInstanceList) {\r
+    TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_CONFIG_INSTANCE, Link, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+\r
+    if (TmpInstance->IfIndex > IfIndex) {\r
+      //\r
+      // There is a sequence hole because some interface is down.\r
+      //\r
+      break;\r
+    }\r
+\r
+    IfIndex++;\r
+  }\r
+\r
+  Instance->IfIndex = IfIndex;\r
+  NetListInsertBefore (Entry, &Instance->Link);\r
+\r
+  for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {\r
+    //\r
+    // Initialize the event map for each data item.\r
+    //\r
+    NetMapInit (&Instance->DataItem[Index].EventMap);\r
+  }\r
+\r
+  //\r
+  // Initialize the NET_MAPs used for DAD on manually configured source addresses.\r
+  //\r
+  NetMapInit (&Instance->DadFailedMap);\r
+  NetMapInit (&Instance->DadPassedMap);\r
+\r
+  //\r
+  // Initialize each data type: associate storage and set data size for the\r
+  // fixed size data types, hook the SetData function, set the data attribute.\r
+  //\r
+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo];\r
+  DataItem->GetData  = Ip6ConfigGetIfInfo;\r
+  DataItem->Data.Ptr = &Instance->InterfaceInfo;\r
+  DataItem->DataSize = sizeof (Instance->InterfaceInfo);\r
+  SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE);\r
+  Ip6ConfigInitIfInfo (IpSb, &Instance->InterfaceInfo);\r
+\r
+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId];\r
+  DataItem->SetData  = Ip6ConfigSetAltIfId;\r
+  DataItem->Data.Ptr = &Instance->AltIfId;\r
+  DataItem->DataSize = sizeof (Instance->AltIfId);\r
+  DataItem->Status   = EFI_NOT_FOUND;\r
+  SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);\r
+\r
+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypePolicy];\r
+  DataItem->SetData  = Ip6ConfigSetPolicy;\r
+  DataItem->Data.Ptr = &Instance->Policy;\r
+  DataItem->DataSize = sizeof (Instance->Policy);\r
+  Instance->Policy   = Ip6ConfigPolicyAutomatic;\r
+  SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);\r
+\r
+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits];\r
+  DataItem->SetData  = Ip6ConfigSetDadXmits;\r
+  DataItem->Data.Ptr = &Instance->DadXmits;\r
+  DataItem->DataSize = sizeof (Instance->DadXmits);\r
+  Instance->DadXmits.DupAddrDetectTransmits = IP6_CONFIG_DEFAULT_DAD_XMITS;\r
+  SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);\r
+\r
+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];\r
+  DataItem->SetData  = Ip6ConfigSetMaunualAddress;\r
+  DataItem->Status   = EFI_NOT_FOUND;\r
+\r
+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeGateway];\r
+  DataItem->SetData  = Ip6ConfigSetGateway;\r
+  DataItem->Status   = EFI_NOT_FOUND;\r
+\r
+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];\r
+  DataItem->SetData  = Ip6ConfigSetDnsServer;\r
+  DataItem->Status   = EFI_NOT_FOUND;\r
+\r
+  //\r
+  // Create the event used for DHCP.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Ip6ConfigOnDhcp6Event,\r
+                  Instance,\r
+                  &Instance->Dhcp6Event\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Instance->Configured  = TRUE;\r
+\r
+  //\r
+  // Try to read the config data from NV variable.\r
+  //\r
+  Status = Ip6ConfigReadConfigData (IpSb->MacString, Instance);\r
+  if (Status == EFI_NOT_FOUND) {\r
+    //\r
+    // The NV variable is not set, so generate a random IAID, and write down the\r
+    // fresh new configuration as the NV variable now.\r
+    //\r
+    Instance->IaId = NET_RANDOM (NetRandomInitSeed ());\r
+\r
+    for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) {\r
+      Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31));\r
+    }\r
+\r
+    Ip6ConfigWriteConfigData (IpSb->MacString, Instance);\r
+  } else if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Instance->Ip6Config.SetData              = EfiIp6ConfigSetData;\r
+  Instance->Ip6Config.GetData              = EfiIp6ConfigGetData;\r
+  Instance->Ip6Config.RegisterDataNotify   = EfiIp6ConfigRegisterDataNotify;\r
+  Instance->Ip6Config.UnregisterDataNotify = EfiIp6ConfigUnregisterDataNotify;\r
+\r
+\r
+  //\r
+  // Publish the IP6 configuration form\r
+  //\r
+  return Ip6ConfigFormInit (Instance);\r
+}\r
+\r
+/**\r
+  Release an IP6_CONFIG_INSTANCE.\r
+\r
+  @param[in, out] Instance    The buffer of IP6_CONFIG_INSTANCE to be freed.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigCleanInstance (\r
+  IN OUT IP6_CONFIG_INSTANCE  *Instance\r
+  )\r
+{\r
+  UINTN                 Index;\r
+  IP6_CONFIG_DATA_ITEM  *DataItem;\r
+\r
+  if (Instance->DeclineAddress != NULL) {\r
+    FreePool (Instance->DeclineAddress);\r
+  }\r
+\r
+  if (!Instance->Configured) {\r
+    return ;\r
+  }\r
+\r
+  if (Instance->Dhcp6Handle != NULL) {\r
+\r
+    Ip6ConfigDestroyDhcp6 (Instance);\r
+  }\r
+\r
+  //\r
+  // Close the event.\r
+  //\r
+  if (Instance->Dhcp6Event != NULL) {\r
+    gBS->CloseEvent (Instance->Dhcp6Event);\r
+  }\r
+\r
+  NetMapClean (&Instance->DadPassedMap);\r
+  NetMapClean (&Instance->DadFailedMap);\r
+\r
+  for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {\r
+\r
+    DataItem = &Instance->DataItem[Index];\r
+\r
+    if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {\r
+      if (DataItem->Data.Ptr != NULL) {\r
+        FreePool (DataItem->Data.Ptr);\r
+      }\r
+      DataItem->Data.Ptr = NULL;\r
+      DataItem->DataSize = 0;\r
+    }\r
+\r
+    NetMapClean (&Instance->DataItem[Index].EventMap);\r
+  }\r
+\r
+  Ip6ConfigFormUnload (Instance);\r
+\r
+  RemoveEntryList (&Instance->Link);\r
+}\r
+\r
+/**\r
+  Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources.\r
+\r
+  @param[in, out] Instance    The buffer of IP6_CONFIG_INSTANCE to be freed.\r
+\r
+  @retval EFI_SUCCESS         The child was successfully destroyed.\r
+  @retval Others              Failed to destory the child.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigDestroyDhcp6 (\r
+  IN OUT IP6_CONFIG_INSTANCE  *Instance\r
+  )\r
+{\r
+  IP6_SERVICE                 *IpSb;\r
+  EFI_STATUS                  Status;\r
+  EFI_DHCP6_PROTOCOL          *Dhcp6;\r
+\r
+  Dhcp6 = Instance->Dhcp6;\r
+  ASSERT (Dhcp6 != NULL);\r
+\r
+  Dhcp6->Stop (Dhcp6);\r
+  Dhcp6->Configure (Dhcp6, NULL);\r
+  Instance->Dhcp6 = NULL;\r
+\r
+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+\r
+  //\r
+  // Close DHCPv6 protocol and destroy the child.\r
+  //\r
+  Status = gBS->CloseProtocol (\r
+                  Instance->Dhcp6Handle,\r
+                  &gEfiDhcp6ProtocolGuid,\r
+                  IpSb->Image,\r
+                  IpSb->Controller\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = NetLibDestroyServiceChild (\r
+             IpSb->Controller,\r
+             IpSb->Image,\r
+             &gEfiDhcp6ServiceBindingProtocolGuid,\r
+             Instance->Dhcp6Handle\r
+             );\r
+\r
+  Instance->Dhcp6Handle = NULL;\r
+\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h
new file mode 100644 (file)
index 0000000..5ae4839
--- /dev/null
@@ -0,0 +1,295 @@
+/** @file\r
+  Definitions for EFI IPv6 Configuartion Protocol implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __IP6_CONFIG_IMPL_H__\r
+#define __IP6_CONFIG_IMPL_H__\r
+\r
+#define IP6_CONFIG_INSTANCE_SIGNATURE    SIGNATURE_32 ('I', 'P', '6', 'C')\r
+#define IP6_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'F', 'C', 'I')\r
+#define IP6_CONFIG_VARIABLE_ATTRIBUTE    (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)\r
+\r
+#define IP6_CONFIG_DEFAULT_DAD_XMITS        1\r
+#define IP6_CONFIG_DHCP6_OPTION_ORO         6\r
+#define IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS 23\r
+\r
+#define DATA_ATTRIB_SIZE_FIXED              0x1\r
+#define DATA_ATTRIB_VOLATILE                0x2\r
+\r
+#define DATA_ATTRIB_SET(Attrib, Bits)       (BOOLEAN)((Attrib) & (Bits))\r
+#define SET_DATA_ATTRIB(Attrib, Bits)       ((Attrib) |= (Bits))\r
+\r
+typedef struct _IP6_CONFIG_INSTANCE IP6_CONFIG_INSTANCE;\r
+\r
+#define IP6_CONFIG_INSTANCE_FROM_PROTOCOL(Proto) \\r
+  CR ((Proto), \\r
+      IP6_CONFIG_INSTANCE, \\r
+      Ip6Config, \\r
+      IP6_CONFIG_INSTANCE_SIGNATURE \\r
+      )\r
+\r
+\r
+#define IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK(Callback) \\r
+  CR ((Callback), \\r
+      IP6_CONFIG_INSTANCE, \\r
+      CallbackInfo, \\r
+      IP6_CONFIG_INSTANCE_SIGNATURE \\r
+      )\r
+\r
+#define IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE(Instance) \\r
+  CR ((Instance), \\r
+      IP6_SERVICE, \\r
+      Ip6ConfigInstance, \\r
+      IP6_SERVICE_SIGNATURE \\r
+      )\r
+\r
+#define IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(ConfigAccess) \\r
+  CR ((ConfigAccess), \\r
+      IP6_FORM_CALLBACK_INFO, \\r
+      HiiConfigAccess, \\r
+      IP6_FORM_CALLBACK_INFO_SIGNATURE \\r
+      )\r
+\r
+/**\r
+  The prototype of work function for EfiIp6ConfigSetData().\r
+\r
+  @param[in]     Instance The pointer to the IP6 config instance data.\r
+  @param[in]     DataSize In bytes, the size of the buffer pointed to by Data.\r
+  @param[in]     Data     The data buffer to set.\r
+\r
+  @retval EFI_BAD_BUFFER_SIZE  The DataSize does not match the size of the type,\r
+                               8 bytes.\r
+  @retval EFI_SUCCESS          The specified configuration data for the EFI IPv6\r
+                               network stack was set successfully.\r
+  \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IP6_CONFIG_SET_DATA) (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN UINTN                DataSize,\r
+  IN VOID                 *Data\r
+  );\r
+\r
+/**\r
+  The prototype of work function for EfiIp6ConfigGetData().\r
+\r
+  @param[in]      Instance The pointer to the IP6 config instance data.\r
+  @param[in, out] DataSize On input, in bytes, the size of Data. On output, in\r
+                           bytes, the size of buffer required to store the specified\r
+                           configuration data.\r
+  @param[in]      Data     The data buffer in which the configuration data is returned.  \r
+                           Ignored if DataSize is ZERO.\r
+\r
+  @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified\r
+                               configuration data, and the required size is \r
+                               returned in DataSize.\r
+  @retval EFI_SUCCESS          The specified configuration data was obtained successfully.                               \r
+  \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IP6_CONFIG_GET_DATA) (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN OUT UINTN            *DataSize,\r
+  IN VOID                 *Data      OPTIONAL\r
+  );\r
+\r
+typedef union {\r
+  VOID                                      *Ptr;\r
+  EFI_IP6_CONFIG_INTERFACE_INFO             *IfInfo;\r
+  EFI_IP6_CONFIG_INTERFACE_ID               *AltIfId;\r
+  EFI_IP6_CONFIG_POLICY                     *Policy;\r
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  *DadXmits;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS             *ManualAddress;\r
+  EFI_IPv6_ADDRESS                          *Gateway;\r
+  EFI_IPv6_ADDRESS                          *DnsServers;\r
+} IP6_CONFIG_DATA;\r
+\r
+typedef struct {\r
+  IP6_CONFIG_SET_DATA  SetData;\r
+  IP6_CONFIG_GET_DATA  GetData;\r
+  EFI_STATUS           Status;\r
+  UINT8                Attribute;\r
+  NET_MAP              EventMap;\r
+  IP6_CONFIG_DATA      Data;\r
+  UINTN                DataSize;\r
+} IP6_CONFIG_DATA_ITEM;\r
+\r
+typedef struct {\r
+  UINT16                    Offset;\r
+  UINTN                     DataSize;\r
+  EFI_IP6_CONFIG_DATA_TYPE  DataType;\r
+} IP6_CONFIG_DATA_RECORD;\r
+\r
+#pragma pack(1)\r
+\r
+//\r
+// heap data that contains the data for each data record.\r
+//\r
+//  BOOLEAN                                   IsAltIfIdSet;\r
+//  EFI_IP6_CONFIG_POLICY                     Policy;\r
+//  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  DadXmits;\r
+//  UINT32                                    ManualaddressCount;\r
+//  UINT32                                    GatewayCount;\r
+//  UINT32                                    DnsServersCount;\r
+//  EFI_IP6_CONFIG_INTERFACE_ID               AltIfId;\r
+//  EFI_IP6_CONFIG_MANUAL_ADDRESS             ManualAddress[];\r
+//  EFI_IPv6_ADDRESS                          Gateway[];\r
+//  EFI_IPv6_ADDRESS                          DnsServers[];\r
+//\r
+typedef struct {\r
+  UINT32                  IaId;\r
+  UINT16                  Checksum;\r
+  UINT16                  DataRecordCount;\r
+  IP6_CONFIG_DATA_RECORD  DataRecord[1];\r
+} IP6_CONFIG_VARIABLE;\r
+\r
+#pragma pack()\r
+\r
+typedef struct {\r
+  LIST_ENTRY                  Link;\r
+  EFI_IP6_ADDRESS_INFO        AddrInfo;\r
+} IP6_ADDRESS_INFO_ENTRY;\r
+\r
+typedef struct {\r
+  EFI_IP6_CONFIG_POLICY                    Policy;              ///< manual or automatic  \r
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadTransmitCount;    ///< dad transmits count\r
+  EFI_IP6_CONFIG_INTERFACE_ID              InterfaceId;         ///< alternative interface id \r
+  LIST_ENTRY                               ManualAddress;       ///< IP addresses\r
+  UINT32                                   ManualAddressCount;  ///< IP addresses count\r
+  LIST_ENTRY                               GatewayAddress;      ///< Gateway address\r
+  UINT32                                   GatewayAddressCount; ///< Gateway address count\r
+  LIST_ENTRY                               DnsAddress;          ///< DNS server address\r
+  UINT32                                   DnsAddressCount;     ///< DNS server address count\r
+} IP6_CONFIG_NVDATA;\r
+\r
+typedef struct _IP6_FORM_CALLBACK_INFO {\r
+  UINT32                           Signature;\r
+  EFI_HANDLE                       ChildHandle;\r
+  EFI_HII_CONFIG_ACCESS_PROTOCOL   HiiConfigAccess;\r
+  EFI_DEVICE_PATH_PROTOCOL         *HiiVendorDevicePath;\r
+  EFI_HII_HANDLE                   RegisteredHandle;\r
+} IP6_FORM_CALLBACK_INFO;\r
+\r
+struct _IP6_CONFIG_INSTANCE {\r
+  UINT32                                    Signature;\r
+  BOOLEAN                                   Configured;\r
+  LIST_ENTRY                                Link;\r
+  UINT16                                    IfIndex;\r
+\r
+  EFI_IP6_CONFIG_INTERFACE_INFO             InterfaceInfo;\r
+  EFI_IP6_CONFIG_INTERFACE_ID               AltIfId;\r
+  EFI_IP6_CONFIG_POLICY                     Policy;\r
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  DadXmits;\r
+\r
+  IP6_CONFIG_DATA_ITEM                      DataItem[Ip6ConfigDataTypeMaximum];\r
+  NET_MAP                                   DadFailedMap;\r
+  NET_MAP                                   DadPassedMap;\r
+\r
+  EFI_IP6_CONFIG_PROTOCOL                   Ip6Config;\r
+\r
+  EFI_EVENT                                 Dhcp6SbNotifyEvent;\r
+  VOID                                      *Registration;\r
+  EFI_HANDLE                                Dhcp6Handle;\r
+  EFI_DHCP6_PROTOCOL                        *Dhcp6;\r
+  BOOLEAN                                   OtherInfoOnly;\r
+  UINT32                                    IaId;\r
+  EFI_EVENT                                 Dhcp6Event;\r
+  UINT32                                    FailedIaAddressCount;\r
+  EFI_IPv6_ADDRESS                          *DeclineAddress;\r
+  UINT32                                    DeclineAddressCount;\r
+\r
+  IP6_FORM_CALLBACK_INFO                    CallbackInfo;\r
+  IP6_CONFIG_NVDATA                         Ip6NvData;\r
+};\r
+\r
+/**\r
+  The event process routine when the DHCPv6 server is answered with a reply packet\r
+  for an information request.\r
+  \r
+  @param[in]     This          Points to the EFI_DHCP6_PROTOCOL.\r
+  @param[in]     Context       The pointer to the IP6 configuration instance data.\r
+  @param[in]     Packet        The DHCPv6 reply packet.\r
+\r
+  @retval EFI_SUCCESS      The DNS server address was retrieved from the reply packet.\r
+  @retval EFI_NOT_READY    The reply packet does not contain the DNS server option, or \r
+                           the DNS server address is not valid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ConfigOnDhcp6Reply (\r
+  IN EFI_DHCP6_PROTOCOL  *This,\r
+  IN VOID                *Context,\r
+  IN EFI_DHCP6_PACKET    *Packet\r
+  );\r
+\r
+/**\r
+  The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration.\r
+  \r
+  @param[in]     Instance      Pointer to the IP6 config instance data.\r
+  @param[in]     OtherInfoOnly If FALSE, get stateful address and other information\r
+                               via DHCPv6. Otherwise, only get the other information.\r
+\r
+  @retval    EFI_SUCCESS       The operation finished successfully.\r
+  @retval    EFI_UNSUPPORTED   The DHCP6 driver is not available.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigStartStatefulAutoConfig (\r
+  IN IP6_CONFIG_INSTANCE  *Instance,\r
+  IN BOOLEAN              OtherInfoOnly\r
+  );\r
+\r
+/**\r
+  Initialize an IP6_CONFIG_INSTANCE.\r
+\r
+  @param[out]    Instance       The buffer of IP6_CONFIG_INSTANCE to be initialized.\r
+  \r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to complete the operation.\r
+  @retval EFI_SUCCESS           The IP6_CONFIG_INSTANCE initialized successfully.\r
+  \r
+**/\r
+EFI_STATUS\r
+Ip6ConfigInitInstance (\r
+  OUT IP6_CONFIG_INSTANCE  *Instance\r
+  );\r
+\r
+/**\r
+  Release an IP6_CONFIG_INSTANCE.\r
+\r
+  @param[in, out] Instance    The buffer of IP6_CONFIG_INSTANCE to be freed.\r
+  \r
+**/\r
+VOID\r
+Ip6ConfigCleanInstance (\r
+  IN OUT IP6_CONFIG_INSTANCE  *Instance\r
+  );\r
+\r
+/**\r
+  Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources.\r
+\r
+  @param[in, out] Instance    The buffer of IP6_CONFIG_INSTANCE to be freed.\r
+\r
+  @retval EFI_SUCCESS         The child was successfully destroyed.\r
+  @retval Others              Failed to destory the child.\r
+  \r
+**/\r
+EFI_STATUS\r
+Ip6ConfigDestroyDhcp6 (\r
+  IN OUT IP6_CONFIG_INSTANCE  *Instance\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c
new file mode 100644 (file)
index 0000000..9ec4886
--- /dev/null
@@ -0,0 +1,2116 @@
+/** @file\r
+  Helper functions for configuring or obtaining the parameters relating to IP6.\r
+\r
+  Copyright (c) 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_GUID  mIp6HiiVendorDevicePathGuid = IP6_HII_VENDOR_DEVICE_PATH_GUID;\r
+EFI_GUID  mIp6ConfigNvDataGuid        = IP6_CONFIG_NVDATA_GUID;\r
+CHAR16    mIp6ConfigStorageName[]     = L"IP6_CONFIG_IFR_NVDATA";\r
+\r
+/**\r
+  The notify function of create event when performing a manual configuration.\r
+\r
+  @param[in]    Event        The pointer of Event.\r
+  @param[in]    Context      The pointer of Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6ConfigManualAddressNotify (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+  *((BOOLEAN *) Context) = TRUE;\r
+}\r
+\r
+/**\r
+  Get the configuration data for the EFI IPv6 network stack running on the\r
+  communication. It is a help function to the call EfiIp6ConfigGetData().\r
+\r
+  @param[in]      Ip6Config      The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+  @param[in]      DataType       The type of data to get.\r
+  @param[out]     DataSize       The size of buffer required in bytes.\r
+  @param[out]     Data           The data buffer in which the configuration data is returned. The\r
+                                 type of the data buffer associated with the DataType.\r
+                                 It is the caller's responsibility to free the resource.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+                                - Ip6Config is NULL or invalid.\r
+                                - DataSize is NULL.\r
+                                - Data is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  Fail to perform the operation due to lack of resources.\r
+  @retval EFI_NOT_READY         The specified configuration data is not ready due to an\r
+                                asynchronous configuration process already in progress.\r
+  @retval EFI_NOT_FOUND         The specified configuration data was not found.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigNvGetData (\r
+  IN  EFI_IP6_CONFIG_PROTOCOL                *Ip6Config,\r
+  IN  EFI_IP6_CONFIG_DATA_TYPE               DataType,\r
+  OUT UINTN                                  *DataSize,\r
+  OUT VOID                                   **Data\r
+  )\r
+{\r
+  UINTN                   BufferSize;\r
+  VOID                    *Buffer;\r
+  EFI_STATUS              Status;\r
+\r
+  if ((Ip6Config == NULL) || (Data == NULL) || (DataSize == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  BufferSize = 0;\r
+  Status = Ip6Config->GetData (\r
+                        Ip6Config,\r
+                        DataType,\r
+                        &BufferSize,\r
+                        NULL\r
+                        );\r
+  if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    return Status;\r
+  }\r
+\r
+  Buffer = AllocateZeroPool (BufferSize);\r
+  if (Buffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = Ip6Config->GetData (\r
+                        Ip6Config,\r
+                        DataType,\r
+                        &BufferSize,\r
+                        Buffer\r
+                        );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Buffer);\r
+    return Status;\r
+  }\r
+\r
+  *DataSize = BufferSize;\r
+  *Data     = Buffer;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Free all nodes in IP6_ADDRESS_INFO_ENTRY in the list array specified\r
+  with ListHead.\r
+\r
+  @param[in]      ListHead  The head of the list array in IP6_ADDRESS_INFO_ENTRY.\r
+\r
+**/\r
+VOID\r
+Ip6FreeAddressInfoList (\r
+  IN LIST_ENTRY                  *ListHead\r
+  )\r
+{\r
+  IP6_ADDRESS_INFO_ENTRY         *Node;\r
+  LIST_ENTRY                     *Entry;\r
+  LIST_ENTRY                     *NextEntry;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, ListHead) {\r
+    Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link);\r
+    RemoveEntryList (&Node->Link);\r
+    FreePool (Node);\r
+  }\r
+}\r
+\r
+/**\r
+  Convert the IPv6 address into a formatted string.\r
+\r
+  @param[in]  Ip6       The IPv6 address.\r
+  @param[out] Str       The formatted IP string.\r
+\r
+**/\r
+VOID\r
+Ip6ToStr (\r
+  IN  EFI_IPv6_ADDRESS  *Ip6,\r
+  OUT CHAR16            *Str\r
+  )\r
+{\r
+  UINTN                 Index;\r
+  BOOLEAN               Short;\r
+  UINTN                 Number;\r
+  CHAR16                FormatString[8];\r
+\r
+  Short = FALSE;\r
+\r
+  for (Index = 0; Index < 15; Index = Index + 2) {\r
+    if (!Short &&\r
+        Index % 2 == 0 &&\r
+        Ip6->Addr[Index] == 0 &&\r
+        Ip6->Addr[Index + 1] == 0\r
+        ) {\r
+      //\r
+      // Deal with the case of ::.\r
+      //\r
+      if (Index == 0) {\r
+        *Str       = L':';\r
+        *(Str + 1) = L':';\r
+        Str        = Str + 2;\r
+      } else {\r
+        *Str       = L':';\r
+        Str        = Str + 1;\r
+      }\r
+\r
+      while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) {\r
+        Index = Index + 2;\r
+      }\r
+\r
+      Short = TRUE;\r
+\r
+      if (Index == 16) {\r
+        //\r
+        // :: is at the end of the address.\r
+        //\r
+        *Str = L'\0';\r
+        break;\r
+      }\r
+    }\r
+\r
+    ASSERT (Index < 15);\r
+\r
+    if (Ip6->Addr[Index] == 0) {\r
+      Number = UnicodeSPrint (Str, 2 * IP6_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]);\r
+    } else {\r
+      if (Ip6->Addr[Index + 1] < 0x10) {\r
+        CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:"));\r
+      } else {\r
+        CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:"));\r
+      }\r
+\r
+      Number = UnicodeSPrint (\r
+                 Str,\r
+                 2 * IP6_STR_MAX_SIZE,\r
+                 (CONST CHAR16 *) FormatString,\r
+                 (UINTN) Ip6->Addr[Index],\r
+                 (UINTN) Ip6->Addr[Index + 1]\r
+                 );\r
+    }\r
+\r
+    Str = Str + Number;\r
+\r
+    if (Index + 2 == 16) {\r
+      *Str = L'\0';\r
+      if (*(Str - 1) == L':') {\r
+        *(Str - 1) = L'\0';\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Convert EFI_IP6_CONFIG_INTERFACE_ID to string format.\r
+\r
+  @param[out]      String  The buffer to store the converted string.\r
+  @param[in]       IfId    The pointer of EFI_IP6_CONFIG_INTERFACE_ID.\r
+\r
+  @retval EFI_SUCCESS              The string converted successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertInterfaceIdToString (\r
+  OUT CHAR16                         *String,\r
+  IN  EFI_IP6_CONFIG_INTERFACE_ID    *IfId\r
+  )\r
+{\r
+  UINT8                          Index;\r
+  UINTN                          Number;\r
+\r
+  if ((String == NULL) || (IfId == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  for (Index = 0; Index < 8; Index++) {\r
+    Number = UnicodeSPrint (\r
+               String,\r
+               2 * INTERFACE_ID_STR_STORAGE,\r
+               L"%x:",\r
+               (UINTN) IfId->Id[Index]\r
+               );\r
+    String = String + Number;\r
+  }\r
+\r
+  *(String - 1) = '\0';\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Parse InterfaceId in string format and convert it to EFI_IP6_CONFIG_INTERFACE_ID.\r
+\r
+  @param[in]        String  The buffer of the string to be parsed.\r
+  @param[out]       IfId    The pointer of EFI_IP6_CONFIG_INTERFACE_ID.\r
+\r
+  @retval EFI_SUCCESS              The operation finished successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ParseInterfaceIdFromString (\r
+  IN CONST CHAR16                    *String,\r
+  OUT EFI_IP6_CONFIG_INTERFACE_ID    *IfId\r
+  )\r
+{\r
+  UINT8                          Index;\r
+  CHAR16                         *IfIdStr;\r
+  CHAR16                         *TempStr;\r
+  UINTN                          NodeVal;\r
+\r
+  if ((String == NULL) || (IfId == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IfIdStr = (CHAR16 *) String;\r
+\r
+  ZeroMem (IfId, sizeof (EFI_IP6_CONFIG_INTERFACE_ID));\r
+\r
+  for (Index = 0; Index < 8; Index++) {\r
+    TempStr = IfIdStr;\r
+\r
+    while ((*IfIdStr != L'\0') && (*IfIdStr != L':')) {\r
+      IfIdStr++;\r
+    }\r
+\r
+    //\r
+    // The InterfaceId format is X:X:X:X, the number of X should not exceed 8.\r
+    // If the number of X is less than 8, zero is appended to the InterfaceId.\r
+    //\r
+    if ((*IfIdStr == ':') && (Index == 7)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Convert the string to interface id. AsciiStrHexToUintn stops at the\r
+    // first character that is not a valid hex character, ':' or '\0' here.\r
+    //\r
+    NodeVal = StrHexToUintn (TempStr);\r
+    if (NodeVal > 0xFF) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    IfId->Id[Index] = (UINT8) NodeVal;\r
+\r
+    IfIdStr++;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Create Hii Extend Label OpCode as the start opcode and end opcode. It is\r
+  a help function.\r
+\r
+  @param[in]  StartLabelNumber   The number of start label.\r
+  @param[out] StartOpCodeHandle  Points to the start opcode handle.\r
+  @param[out] StartLabel         Points to the created start opcode.\r
+  @param[out] EndOpCodeHandle    Points to the end opcode handle.\r
+  @param[out] EndLabel           Points to the created end opcode.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this\r
+                                 operation.\r
+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CreateOpCode (\r
+  IN  UINT16                        StartLabelNumber,\r
+  OUT VOID                          **StartOpCodeHandle,\r
+  OUT EFI_IFR_GUID_LABEL            **StartLabel,\r
+  OUT VOID                          **EndOpCodeHandle,\r
+  OUT EFI_IFR_GUID_LABEL            **EndLabel\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  EFI_IFR_GUID_LABEL                *InternalStartLabel;\r
+  EFI_IFR_GUID_LABEL                *InternalEndLabel;\r
+\r
+  if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *StartOpCodeHandle = NULL;\r
+  *EndOpCodeHandle   = NULL;\r
+  Status             = EFI_OUT_OF_RESOURCES;\r
+\r
+  //\r
+  // Initialize the container for dynamic opcodes.\r
+  //\r
+  *StartOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+  if (*StartOpCodeHandle == NULL) {\r
+    return Status;\r
+  }\r
+\r
+  *EndOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+  if (*EndOpCodeHandle == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Create Hii Extend Label OpCode as the start opcode.\r
+  //\r
+  InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (\r
+                                                *StartOpCodeHandle,\r
+                                                &gEfiIfrTianoGuid,\r
+                                                NULL,\r
+                                                sizeof (EFI_IFR_GUID_LABEL)\r
+                                                );\r
+  if (InternalStartLabel == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+  InternalStartLabel->Number       = StartLabelNumber;\r
+\r
+  //\r
+  // Create Hii Extend Label OpCode as the end opcode.\r
+  //\r
+  InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (\r
+                                              *EndOpCodeHandle,\r
+                                              &gEfiIfrTianoGuid,\r
+                                              NULL,\r
+                                              sizeof (EFI_IFR_GUID_LABEL)\r
+                                              );\r
+  if (InternalEndLabel == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+  InternalEndLabel->Number       = LABEL_END;\r
+\r
+  *StartLabel = InternalStartLabel;\r
+  *EndLabel   = InternalEndLabel;\r
+\r
+  return EFI_SUCCESS;\r
+\r
+Exit:\r
+\r
+  if (*StartOpCodeHandle != NULL) {\r
+    HiiFreeOpCodeHandle (*StartOpCodeHandle);\r
+  }\r
+\r
+  if (*EndOpCodeHandle != NULL) {\r
+    HiiFreeOpCodeHandle (*EndOpCodeHandle);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function converts the different format of address list to string format and\r
+  then generates the corresponding text opcode to illustarate the address info in\r
+  IP6 configuration page. Currently, the following formats are supported:\r
+  EFI_IP6_ADDRESS_INFO AddressType: Ip6ConfigNvHostAddress;\r
+  EFI_IPv6_ADDRESS     AddressType: Ip6ConfigNvGatewayAddress and Ip6ConfigNvDnsAddress;\r
+  EFI_IP6_ROUTE_TABLE  AddressType: Ip6ConfigNvRouteTable.\r
+\r
+  @param[in, out] String           The pointer to the buffer to store the converted\r
+                                   string.\r
+  @param[in]      HiiHandle        A handle that was previously registered in the\r
+                                   HII Database.\r
+  @param[in]      AddressType      The address type.\r
+  @param[in]      AddressInfo      Pointer to the address list.\r
+  @param[in]      AddressCount     The address count of the address list.\r
+\r
+  @retval EFI_SUCCESS              The operation finished successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+  @retval EFI_UNSUPPORTED          The AddressType is not supported.\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertAddressListToString (\r
+  IN OUT CHAR16                         *String,\r
+  IN     EFI_HII_HANDLE                 HiiHandle,\r
+  IN     IP6_CONFIG_NV_ADDRESS_TYPE     AddressType,\r
+  IN     VOID                           *AddressInfo,\r
+  IN     UINTN                          AddressCount\r
+  )\r
+{\r
+  UINTN                          Index;\r
+  UINTN                          Number;\r
+  CHAR16                         *TempStr;\r
+  EFI_STATUS                     Status;\r
+  VOID                           *StartOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL             *StartLabel;\r
+  VOID                           *EndOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL             *EndLabel;\r
+  UINT16                         StartLabelNumber;\r
+  EFI_STRING_ID                  TextTwo;\r
+  UINT8                          *AddressHead;\r
+  UINT8                          PrefixLength;\r
+  EFI_IPv6_ADDRESS               *Address;\r
+\r
+  if ((String == NULL) || (HiiHandle == NULL) || (AddressInfo == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (AddressType == Ip6ConfigNvHostAddress) {\r
+    StartLabelNumber = HOST_ADDRESS_LABEL;\r
+  } else if (AddressType == Ip6ConfigNvGatewayAddress) {\r
+    StartLabelNumber = GATEWAY_ADDRESS_LABEL;\r
+  } else if (AddressType == Ip6ConfigNvDnsAddress) {\r
+    StartLabelNumber = DNS_ADDRESS_LABEL;\r
+  } else if (AddressType == Ip6ConfigNvRouteTable) {\r
+    StartLabelNumber = ROUTE_TABLE_LABEL;\r
+  } else {\r
+    ASSERT (FALSE);\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Status = Ip6CreateOpCode (\r
+             StartLabelNumber,\r
+             &StartOpCodeHandle,\r
+             &StartLabel,\r
+             &EndOpCodeHandle,\r
+             &EndLabel\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  AddressHead = (UINT8 *) AddressInfo;\r
+\r
+  for (Index = 0; Index < AddressCount; Index++) {\r
+    if (AddressType == Ip6ConfigNvHostAddress) {\r
+      AddressInfo = AddressHead + sizeof (EFI_IP6_ADDRESS_INFO) * Index;\r
+      Address     = &((EFI_IP6_ADDRESS_INFO *) AddressInfo)->Address;\r
+    } else if (AddressType == Ip6ConfigNvRouteTable) {\r
+      AddressInfo = AddressHead + sizeof (EFI_IP6_ROUTE_TABLE) * Index;\r
+      Address     = &((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Destination;\r
+    } else {\r
+      AddressInfo = AddressHead + sizeof (EFI_IPv6_ADDRESS) * Index;\r
+      Address     = AddressInfo;\r
+    }\r
+\r
+    //\r
+    // Convert the IP address info to string.\r
+    //\r
+    Ip6ToStr (Address, String);\r
+    TempStr = String + StrLen (String);\r
+\r
+    if ((AddressType == Ip6ConfigNvHostAddress) || (AddressType == Ip6ConfigNvRouteTable)) {\r
+      if (AddressType == Ip6ConfigNvHostAddress) {\r
+        PrefixLength = ((EFI_IP6_ADDRESS_INFO *) AddressInfo)->PrefixLength;\r
+      } else {\r
+        PrefixLength = ((EFI_IP6_ROUTE_TABLE *) AddressInfo)->PrefixLength;\r
+      }\r
+\r
+      //\r
+      // Append the prefix length to the string.\r
+      //\r
+      *TempStr = L'/';\r
+      TempStr++;\r
+      Number  = UnicodeSPrint (TempStr, 6, L"%d", PrefixLength);\r
+      TempStr = TempStr + Number;\r
+    }\r
+\r
+    if (AddressType == Ip6ConfigNvRouteTable) {\r
+      //\r
+      // Append " >> " to the string.\r
+      //\r
+      Number   = UnicodeSPrint (TempStr, 8, L" >>  ");\r
+      TempStr  = TempStr + Number;\r
+\r
+      //\r
+      // Append the gateway address to the string.\r
+      //\r
+      Ip6ToStr (&((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Gateway, TempStr);\r
+      TempStr = TempStr + StrLen (TempStr);\r
+    }\r
+\r
+    //\r
+    // Generate a text opcode and update the UI.\r
+    //\r
+    TextTwo = HiiSetString (HiiHandle, 0, String, NULL);\r
+    if (TextTwo == 0) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto Exit;\r
+    }\r
+\r
+    HiiCreateTextOpCode (StartOpCodeHandle, STR_NULL, STR_NULL, TextTwo);\r
+\r
+    String = TempStr;\r
+    *String = IP6_ADDRESS_DELIMITER;\r
+    String++;\r
+  }\r
+\r
+  *(String - 1) = '\0';\r
+\r
+  Status = HiiUpdateForm (\r
+             HiiHandle,                       // HII handle\r
+             &mIp6ConfigNvDataGuid,           // Formset GUID\r
+             FORMID_MAIN_FORM,                // Form ID\r
+             StartOpCodeHandle,               // Label for where to insert opcodes\r
+             EndOpCodeHandle                  // Replace data\r
+             );\r
+\r
+Exit:\r
+  HiiFreeOpCodeHandle (StartOpCodeHandle);\r
+  HiiFreeOpCodeHandle (EndOpCodeHandle);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Parse address list in string format and convert it to a list array of node in\r
+  IP6_ADDRESS_INFO_ENTRY.\r
+\r
+  @param[in]        String         The buffer to string to be parsed.\r
+  @param[out]       ListHead       The list head of array.\r
+  @param[out]       AddressCount   The number of list nodes in the array.\r
+\r
+  @retval EFI_SUCCESS              The operation finished successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     Failed to perform the operation due to lack of resource.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ParseAddressListFromString (\r
+  IN CONST CHAR16                    *String,\r
+  OUT LIST_ENTRY                     *ListHead,\r
+  OUT UINT32                         *AddressCount\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  CHAR16                         *LocalString;\r
+  CHAR16                         *Temp;\r
+  CHAR16                         *TempStr;\r
+  EFI_IP6_ADDRESS_INFO           AddressInfo;\r
+  IP6_ADDRESS_INFO_ENTRY         *Node;\r
+  BOOLEAN                        Last;\r
+  UINT32                         Count;\r
+\r
+  if ((String == NULL) || (ListHead == NULL) || (AddressCount == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  LocalString = (CHAR16 *) AllocateCopyPool (StrSize (String), String);\r
+  if (LocalString == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Clean the original address list.\r
+  //\r
+  Ip6FreeAddressInfoList (ListHead);\r
+\r
+  Temp  = LocalString;\r
+  Last  = FALSE;\r
+  Count = 0;\r
+\r
+  while (*LocalString != L'\0') {\r
+    TempStr = LocalString;\r
+    while ((*LocalString != L'\0') && (*LocalString != IP6_ADDRESS_DELIMITER)) {\r
+      LocalString++;\r
+    }\r
+\r
+    if (*LocalString == L'\0') {\r
+      Last = TRUE;\r
+    }\r
+\r
+    *LocalString = L'\0';\r
+\r
+    Status = NetLibStrToIp6andPrefix (TempStr, &AddressInfo.Address, &AddressInfo.PrefixLength);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+\r
+    if (AddressInfo.PrefixLength == 0xFF) {\r
+      AddressInfo.PrefixLength = 0;\r
+    }\r
+\r
+    if (!NetIp6IsValidUnicast (&AddressInfo.Address)) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto Error;\r
+    }\r
+\r
+    Node = AllocatePool (sizeof (IP6_ADDRESS_INFO_ENTRY));\r
+    if (Node == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto Error;\r
+    }\r
+\r
+    CopyMem (&Node->AddrInfo, &AddressInfo, sizeof (EFI_IP6_ADDRESS_INFO));\r
+    InsertTailList (ListHead, &Node->Link);\r
+    Count++;\r
+\r
+    if (Last) {\r
+      break;\r
+    }\r
+\r
+    LocalString++;\r
+  }\r
+\r
+  FreePool (Temp);\r
+  *AddressCount = Count;\r
+  return EFI_SUCCESS;\r
+\r
+Error:\r
+  Ip6FreeAddressInfoList (ListHead);\r
+  FreePool (Temp);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function converts the interface info to string and draws it to the IP6 UI.\r
+  The interface information includes interface name, interface type, hardware address,\r
+  address info, and route table information. The address information is also used as the\r
+  content of manual addresses in IP6 UI.\r
+\r
+  @param[in]       IfInfo          The pointer of EFI_IP6_CONFIG_INTERFACE_INFO.\r
+  @param[in]       HiiHandle       The handle that was previously registered in the\r
+                                   HII Database.\r
+  @param[in, out]  IfrNvData       Points to IP6_CONFIG_IFR_NVDATA.\r
+\r
+  @retval EFI_SUCCESS              The operation finished successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     The operation failed due to lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertInterfaceInfoToString (\r
+  IN     EFI_IP6_CONFIG_INTERFACE_INFO  *IfInfo,\r
+  IN     EFI_HII_HANDLE                 HiiHandle,\r
+  IN OUT IP6_CONFIG_IFR_NVDATA          *IfrNvData\r
+  )\r
+{\r
+  UINT32                         Index;\r
+  UINTN                          Number;\r
+  CHAR16                         *String;\r
+  CHAR16                         *LinkLocalStr;\r
+  CHAR16                         PortString[ADDRESS_STR_MAX_SIZE];\r
+  CHAR16                         FormatString[8];\r
+  EFI_STRING_ID                  StringId;\r
+  EFI_STATUS                     Status;\r
+\r
+  if ((IfInfo == NULL) || (HiiHandle == NULL) || (IfrNvData == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Print the interface name.\r
+  //\r
+  StringId = HiiSetString (\r
+               HiiHandle,\r
+               STRING_TOKEN (STR_IP6_INTERFACE_NAME_CONTENT),\r
+               IfInfo->Name,\r
+               NULL\r
+               );\r
+  if (StringId == 0) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Print the interface type.\r
+  //\r
+  if (IfInfo->IfType == Ip6InterfaceTypeEthernet) {\r
+    StrCpy (PortString, IP6_ETHERNET);\r
+  } else if (IfInfo->IfType == Ip6InterfaceTypeExperimentalEthernet) {\r
+    StrCpy (PortString, IP6_EXPERIMENTAL_ETHERNET);\r
+  } else {\r
+    //\r
+    // Refer to RFC1700, chapter Number Hardware Type.\r
+    //\r
+    UnicodeSPrint (PortString, 6, L"%d", IfInfo->IfType);\r
+  }\r
+\r
+  StringId = HiiSetString (\r
+               HiiHandle,\r
+               STRING_TOKEN (STR_IP6_INTERFACE_TYPE_CONTENT),\r
+               PortString,\r
+               NULL\r
+               );\r
+  if (StringId == 0) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Convert the hardware address.\r
+  //\r
+  String = PortString;\r
+  ASSERT (IfInfo->HwAddressSize <= 32);\r
+\r
+  for (Index = 0; Index < IfInfo->HwAddressSize; Index++) {\r
+\r
+    if (IfInfo->HwAddress.Addr[Index] < 0x10) {\r
+      StrCpy (FormatString, L"0%x-");\r
+    } else {\r
+      StrCpy (FormatString, L"%x-");\r
+    }\r
+\r
+    Number = UnicodeSPrint (\r
+               String,\r
+               8,\r
+               (CONST CHAR16 *) FormatString,\r
+               (UINTN) IfInfo->HwAddress.Addr[Index]\r
+               );\r
+    String = String + Number;\r
+  }\r
+\r
+  if (Index != 0) {\r
+    ASSERT (String > PortString);\r
+    String--;\r
+    *String = '\0';\r
+  }\r
+\r
+  //\r
+  // Print the hardware address.\r
+  //\r
+  StringId = HiiSetString (\r
+               HiiHandle,\r
+               STRING_TOKEN (STR_IP6_MAC_ADDRESS_CONTENT),\r
+               PortString,\r
+               NULL\r
+               );\r
+  if (StringId == 0) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Print the host address Information.\r
+  //\r
+  Status = Ip6ConvertAddressListToString (\r
+             PortString,\r
+             HiiHandle,\r
+             Ip6ConfigNvHostAddress,\r
+             IfInfo->AddressInfo,\r
+             IfInfo->AddressInfoCount\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Copy the Host Address Info to manual address field.\r
+  // Do not copy the link local address.\r
+  //\r
+  LinkLocalStr = StrStr (PortString, IP6_LINK_LOCAL_PREFIX);\r
+  if (LinkLocalStr != NULL) {\r
+    Number = LinkLocalStr - PortString;\r
+    if (Number > 0) {\r
+      CopyMem (IfrNvData->ManualAddress, PortString, Number * sizeof (CHAR16));\r
+    }\r
+\r
+    while ((*LinkLocalStr != L' ') && (*LinkLocalStr != L'\0')) {\r
+      LinkLocalStr++;\r
+    }\r
+\r
+    if (*LinkLocalStr != L'\0') {\r
+      LinkLocalStr++;\r
+      StrCat (IfrNvData->ManualAddress, LinkLocalStr);\r
+    }\r
+  } else {\r
+    StrCpy (IfrNvData->ManualAddress, PortString);\r
+  }\r
+\r
+  //\r
+  // Print the route table information.\r
+  //\r
+  Status = Ip6ConvertAddressListToString (\r
+             PortString,\r
+             HiiHandle,\r
+             Ip6ConfigNvRouteTable,\r
+             IfInfo->RouteTable,\r
+             IfInfo->RouteCount\r
+             );\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Build the address info list from list array of node in IP6_ADDRESS_INFO_ENTRY.\r
+\r
+  @param[in]      Instance         Points to IP6 config instance data.\r
+  @param[in]      AddressType      The address type.\r
+  @param[out]     AddressInfo      The pointer to the buffer to store the address list.\r
+  @param[out]     AddressSize      The address size of the address list.\r
+\r
+  @retval EFI_SUCCESS              The operation finished successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+  @retval EFI_UNSUPPORTED          The AddressType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildNvAddressInfo (\r
+  IN  IP6_CONFIG_INSTANCE            *Instance,\r
+  IN  IP6_CONFIG_NV_ADDRESS_TYPE     AddressType,\r
+  OUT VOID                           **AddressInfo,\r
+  OUT UINTN                          *AddressSize\r
+  )\r
+{\r
+  IP6_CONFIG_NVDATA                  *Ip6NvData;\r
+  LIST_ENTRY                         *Entry;\r
+  LIST_ENTRY                         *ListHead;\r
+  IP6_ADDRESS_INFO_ENTRY             *Node;\r
+  VOID                               *AddressList;\r
+  VOID                               *TmpStr;\r
+  UINTN                              DataSize;\r
+  EFI_IPv6_ADDRESS                   *Ip6Address;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS      *ManualAddress;\r
+\r
+  if ((Instance == NULL) || (AddressInfo == NULL) || (AddressSize == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+\r
+  Ip6NvData = &Instance->Ip6NvData;\r
+\r
+  if (AddressType == Ip6ConfigNvHostAddress) {\r
+    ListHead = &Ip6NvData->ManualAddress;\r
+    DataSize = sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS) * Ip6NvData->ManualAddressCount;\r
+  } else if (AddressType == Ip6ConfigNvGatewayAddress) {\r
+    ListHead = &Ip6NvData->GatewayAddress;\r
+    DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->GatewayAddressCount;\r
+  } else if (AddressType == Ip6ConfigNvDnsAddress) {\r
+    ListHead = &Ip6NvData->DnsAddress;\r
+    DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->DnsAddressCount;\r
+  } else {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  AddressList = AllocateZeroPool (DataSize);\r
+  if (AddressList  == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TmpStr = AddressList;\r
+\r
+  NET_LIST_FOR_EACH (Entry, ListHead) {\r
+    Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link);\r
+    if (AddressType == Ip6ConfigNvHostAddress) {\r
+      ManualAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AddressList;\r
+      IP6_COPY_ADDRESS (&ManualAddress->Address, &Node->AddrInfo.Address);\r
+      ManualAddress->PrefixLength = Node->AddrInfo.PrefixLength;\r
+      AddressList = (UINT8 *) AddressList + sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);\r
+    } else {\r
+      Ip6Address = (EFI_IPv6_ADDRESS *) AddressList;\r
+      IP6_COPY_ADDRESS (Ip6Address, &Node->AddrInfo.Address);\r
+      AddressList = (UINT8 *) AddressList + sizeof (EFI_IPv6_ADDRESS);\r
+    }\r
+  }\r
+\r
+  *AddressInfo = TmpStr;\r
+  *AddressSize = DataSize;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Convert the IP6 configuration data into the IFR data.\r
+\r
+  @param[in, out]  IfrNvData       The IFR NV data.\r
+  @param[in]       Instance        The IP6 config instance data.\r
+\r
+  @retval EFI_SUCCESS              The operation finished successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+  @retval EFI_UNSUPPORTED          The policy is not supported in the current implementation.\r
+  @retval Others                   Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertConfigNvDataToIfrNvData (\r
+  IN OUT IP6_CONFIG_IFR_NVDATA       *IfrNvData,\r
+  IN     IP6_CONFIG_INSTANCE         *Instance\r
+  )\r
+{\r
+  EFI_IP6_CONFIG_PROTOCOL                    *Ip6Config;\r
+  UINTN                                      DataSize;\r
+  VOID                                       *Data;\r
+  EFI_STATUS                                 Status;\r
+  EFI_IP6_CONFIG_INTERFACE_ID                InterfaceId;\r
+  EFI_IP6_CONFIG_POLICY                      Policy;\r
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS   DadXmits;\r
+  EFI_HII_HANDLE                             HiiHandle;\r
+\r
+  if ((IfrNvData == NULL) || (Instance == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+\r
+  Ip6Config = &Instance->Ip6Config;\r
+  Data      = NULL;\r
+  DataSize  = 0;\r
+  HiiHandle = Instance->CallbackInfo.RegisteredHandle;\r
+\r
+  //\r
+  // Get the current interface info.\r
+  //\r
+  Status = Ip6ConfigNvGetData (\r
+             Ip6Config,\r
+             Ip6ConfigDataTypeInterfaceInfo,\r
+             &DataSize,\r
+             (VOID **) &Data\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Convert the interface info to string and print.\r
+  //\r
+  Status = Ip6ConvertInterfaceInfoToString (\r
+             (EFI_IP6_CONFIG_INTERFACE_INFO *) Data,\r
+             HiiHandle,\r
+             IfrNvData\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Get the interface id.\r
+  //\r
+  DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);\r
+  ZeroMem (&InterfaceId, DataSize);\r
+  Status = Ip6Config->GetData (\r
+                        Ip6Config,\r
+                        Ip6ConfigDataTypeAltInterfaceId,\r
+                        &DataSize,\r
+                        &InterfaceId\r
+                        );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  Ip6ConvertInterfaceIdToString (IfrNvData->InterfaceId, &InterfaceId);\r
+\r
+  //\r
+  // Get current policy.\r
+  //\r
+  DataSize = sizeof (EFI_IP6_CONFIG_POLICY);\r
+  Status   = Ip6Config->GetData (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypePolicy,\r
+                          &DataSize,\r
+                          &Policy\r
+                          );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  if (Policy == Ip6ConfigPolicyManual) {\r
+    IfrNvData->Policy = IP6_POLICY_MANUAL;\r
+  } else if (Policy == Ip6ConfigPolicyAutomatic) {\r
+    IfrNvData->Policy = IP6_POLICY_AUTO;\r
+  } else {\r
+    ASSERT (FALSE);\r
+    Status = EFI_UNSUPPORTED;\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Get Duplicate Address Detection Transmits count.\r
+  //\r
+  DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+  Status   = Ip6Config->GetData (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+                          &DataSize,\r
+                          &DadXmits\r
+                          );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  IfrNvData->DadTransmitCount = DadXmits.DupAddrDetectTransmits;\r
+\r
+  //\r
+  // Get DNS server list.\r
+  //\r
+  FreePool (Data);\r
+  Data     = NULL;\r
+  DataSize = 0;\r
+  Status   = Ip6ConfigNvGetData (\r
+               Ip6Config,\r
+               Ip6ConfigDataTypeDnsServer,\r
+               &DataSize,\r
+               (VOID **) &Data\r
+               );\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+    goto Exit;\r
+  }\r
+\r
+  if (DataSize > 0) {\r
+    //\r
+    // Convert the DNS server address to string and draw it to UI.\r
+    //\r
+    Status = Ip6ConvertAddressListToString (\r
+               IfrNvData->DnsAddress,\r
+               HiiHandle,\r
+               Ip6ConfigNvDnsAddress,\r
+               Data,\r
+               DataSize / sizeof (EFI_IPv6_ADDRESS)\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    FreePool (Data);\r
+    Data = NULL;\r
+  }\r
+\r
+  //\r
+  // Get gateway adderss list.\r
+  //\r
+  DataSize = 0;\r
+  Status   = Ip6ConfigNvGetData (\r
+               Ip6Config,\r
+               Ip6ConfigDataTypeGateway,\r
+               &DataSize,\r
+               (VOID **) &Data\r
+               );\r
+\r
+  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+    goto Exit;\r
+  }\r
+\r
+  if (DataSize > 0) {\r
+    //\r
+    // Convert the gateway address to string and draw it to UI.\r
+    //\r
+    Status = Ip6ConvertAddressListToString (\r
+               IfrNvData->GatewayAddress,\r
+               HiiHandle,\r
+               Ip6ConfigNvGatewayAddress,\r
+               Data,\r
+               DataSize / sizeof (EFI_IPv6_ADDRESS)\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+  if (Data != NULL) {\r
+     FreePool (Data);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Convert IFR data into IP6 configuration data. The policy, alternative interface\r
+  ID, and DAD transmit counts, and will be saved. If under manual policy, the configured\r
+  manual address, gateway address, and DNS server address will be saved.\r
+\r
+  @param[in]       IfrNvData       The IFR NV data.\r
+  @param[in, out]  Instance        The IP6 config instance data.\r
+\r
+  @retval EFI_SUCCESS              The operation finished successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+  @retval Others                   Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertIfrNvDataToConfigNvData (\r
+  IN     IP6_CONFIG_IFR_NVDATA       *IfrNvData,\r
+  IN OUT IP6_CONFIG_INSTANCE         *Instance\r
+  )\r
+{\r
+  IP6_CONFIG_NVDATA                  *Ip6NvData;\r
+  EFI_IP6_CONFIG_PROTOCOL            *Ip6Config;\r
+  EFI_STATUS                         Status;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS      *ManualAddress;\r
+  EFI_IPv6_ADDRESS                   *Address;\r
+  BOOLEAN                            IsAddressOk;\r
+  EFI_EVENT                          SetAddressEvent;\r
+  EFI_EVENT                          TimeoutEvent;\r
+  UINTN                              DataSize;\r
+\r
+  if ((IfrNvData == NULL) || (Instance == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+  Ip6NvData = &Instance->Ip6NvData;\r
+  Ip6Config = &Instance->Ip6Config;\r
+\r
+  //\r
+  // Update those fields which don't have INTERACTIVE attribute.\r
+  //\r
+  if (IfrNvData->Policy == IP6_POLICY_AUTO) {\r
+    Ip6NvData->Policy = Ip6ConfigPolicyAutomatic;\r
+  } else if (IfrNvData->Policy == IP6_POLICY_MANUAL) {\r
+    Ip6NvData->Policy = Ip6ConfigPolicyManual;\r
+  }\r
+\r
+  Ip6NvData->DadTransmitCount.DupAddrDetectTransmits = IfrNvData->DadTransmitCount;\r
+\r
+  //\r
+  // Set the configured policy.\r
+  //\r
+  Status = Ip6Config->SetData (\r
+                        Ip6Config,\r
+                        Ip6ConfigDataTypePolicy,\r
+                        sizeof (EFI_IP6_CONFIG_POLICY),\r
+                        &Ip6NvData->Policy\r
+                        );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Set the duplicate address detection transmits count.\r
+  //\r
+  Status = Ip6Config->SetData (\r
+                        Ip6Config,\r
+                        Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+                        sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS),\r
+                        &Ip6NvData->DadTransmitCount\r
+                        );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Set the alternative interface ID\r
+  //\r
+  Status = Ip6Config->SetData (\r
+                        Ip6Config,\r
+                        Ip6ConfigDataTypeAltInterfaceId,\r
+                        sizeof (EFI_IP6_CONFIG_INTERFACE_ID),\r
+                        &Ip6NvData->InterfaceId\r
+                        );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+\r
+  if (Ip6NvData->Policy == Ip6ConfigPolicyAutomatic) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Create events & timers for asynchronous settings.\r
+  //\r
+  SetAddressEvent = NULL;\r
+  TimeoutEvent    = NULL;\r
+  ManualAddress   = NULL;\r
+  Address         = NULL;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  Ip6ConfigManualAddressNotify,\r
+                  &IsAddressOk,\r
+                  &SetAddressEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &TimeoutEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Set the manual address list. This is an asynchronous process.\r
+  //\r
+  if (!IsListEmpty (&Ip6NvData->ManualAddress) && (Ip6NvData->ManualAddressCount != 0)) {\r
+    Status = Ip6BuildNvAddressInfo (\r
+               Instance,\r
+               Ip6ConfigNvHostAddress,\r
+               (VOID **) &ManualAddress,\r
+               &DataSize\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    IsAddressOk = FALSE;\r
+\r
+    Status = Ip6Config->RegisterDataNotify (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypeManualAddress,\r
+                          SetAddressEvent\r
+                          );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    Status = Ip6Config->SetData (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypeManualAddress,\r
+                          DataSize,\r
+                          (VOID *) ManualAddress\r
+                          );\r
+    if (Status == EFI_NOT_READY) {\r
+      gBS->SetTimer (TimeoutEvent, TimerRelative, 50000000);\r
+      while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+        if (IsAddressOk) {\r
+          Status = EFI_SUCCESS;\r
+        }\r
+        break;\r
+      }\r
+    }\r
+\r
+    Status = Ip6Config->UnregisterDataNotify (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypeManualAddress,\r
+                          SetAddressEvent\r
+                          );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Set gateway address list.\r
+  //\r
+  if (!IsListEmpty (&Ip6NvData->GatewayAddress) && (Ip6NvData->GatewayAddressCount != 0)) {\r
+    Status = Ip6BuildNvAddressInfo (\r
+               Instance,\r
+               Ip6ConfigNvGatewayAddress,\r
+               (VOID **) &Address,\r
+               &DataSize\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    Status = Ip6Config->SetData (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypeGateway,\r
+                          DataSize,\r
+                          (VOID *) Address\r
+                          );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    FreePool (Address);\r
+    Address = NULL;\r
+  }\r
+\r
+  //\r
+  // Set DNS server address list.\r
+  //\r
+  if (!IsListEmpty (&Ip6NvData->DnsAddress) && (Ip6NvData->DnsAddressCount != 0)) {\r
+    Status = Ip6BuildNvAddressInfo (\r
+               Instance,\r
+               Ip6ConfigNvDnsAddress,\r
+               (VOID **) &Address,\r
+               &DataSize\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    Status = Ip6Config->SetData (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypeDnsServer,\r
+                          DataSize,\r
+                          (VOID *) Address\r
+                          );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+  if (SetAddressEvent != NULL) {\r
+    gBS->CloseEvent (SetAddressEvent);\r
+  }\r
+\r
+  if (TimeoutEvent != NULL) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+  }\r
+\r
+  if (ManualAddress != NULL) {\r
+    FreePool (ManualAddress);\r
+  }\r
+\r
+  if (Address != NULL) {\r
+    FreePool (Address);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function allows the caller to request the current\r
+  configuration for one or more named elements. The resulting\r
+  string is in <ConfigAltResp> format. Any and all alternative\r
+  configuration strings shall also be appended to the end of the\r
+  current configuration string. If they are, they must appear\r
+  after the current configuration. They must contain the same\r
+  routing (GUID, NAME, PATH) as the current configuration string.\r
+  They must have an additional description indicating the type of\r
+  alternative configuration the string represents,\r
+  "ALTCFG=<StringToken>". That <StringToken> (when\r
+  converted from Hex UNICODE to binary) is a reference to a\r
+  string in the associated string pack.\r
+\r
+  @param[in] This       Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param[in] Request    A null-terminated Unicode string in\r
+                        <ConfigRequest> format. Note that this\r
+                        includes the routing information as well as\r
+                        the configurable name / value pairs. It is\r
+                        invalid for this string to be in\r
+                        <MultiConfigRequest> format.\r
+  @param[out] Progress  On return, points to a character in the\r
+                        Request string. Points to the string's null\r
+                        terminator if request was successful. Points\r
+                        to the most recent "&" before the first\r
+                        failing name / value pair (or the beginning\r
+                        of the string if the failure is in the first\r
+                        name / value pair) if the request was not\r
+                        successful.\r
+  @param[out] Results   A null-terminated Unicode string in\r
+                        <ConfigAltResp> format which has all values\r
+                        filled in for the names in the Request string.\r
+                        String to be allocated by the called function.\r
+\r
+  @retval EFI_SUCCESS             The Results string is filled with the\r
+                                  values corresponding to all requested\r
+                                  names.\r
+  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
+                                  parts of the results that must be\r
+                                  stored awaiting possible future\r
+                                  protocols.\r
+  @retval EFI_INVALID_PARAMETER   For example, passing in a NULL\r
+                                  for the Request parameter\r
+                                  would result in this type of\r
+                                  error. In this case, the\r
+                                  Progress parameter would be\r
+                                  set to NULL.\r
+  @retval EFI_NOT_FOUND           Routing data doesn't match any\r
+                                  known driver. Progress set to the\r
+                                  first character in the routing header.\r
+                                  Note: There is no requirement that the\r
+                                  driver validate the routing data. It\r
+                                  must skip the <ConfigHdr> in order to\r
+                                  process the names.\r
+  @retval EFI_INVALID_PARAMETER   Illegal syntax. Progress set\r
+                                  to most recent & before the\r
+                                  error or the beginning of the\r
+                                  string.\r
+  @retval EFI_INVALID_PARAMETER   Unknown name. Progress points\r
+                                  to the & before the name in\r
+                                  question. Currently not implemented.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6FormExtractConfig (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  CONST EFI_STRING                       Request,\r
+  OUT EFI_STRING                             *Progress,\r
+  OUT EFI_STRING                             *Results\r
+  )\r
+{\r
+\r
+  EFI_STATUS                                 Status;\r
+  IP6_FORM_CALLBACK_INFO                     *Private;\r
+  IP6_CONFIG_INSTANCE                        *Ip6ConfigInstance;\r
+  IP6_CONFIG_IFR_NVDATA                      *IfrNvData;\r
+  EFI_STRING                                 ConfigRequestHdr;\r
+  EFI_STRING                                 ConfigRequest;\r
+  BOOLEAN                                    AllocatedRequest;\r
+  UINTN                                      Size;\r
+  UINTN                                      BufferSize;\r
+\r
+  if (This == NULL || Progress == NULL || Results == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *Progress = Request;\r
+  if ((Request != NULL) &&\r
+      !HiiIsConfigHdrMatch (Request, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  ConfigRequestHdr = NULL;\r
+  ConfigRequest    = NULL;\r
+  AllocatedRequest = FALSE;\r
+  Size             = 0;\r
+\r
+  Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);\r
+  Ip6ConfigInstance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private);\r
+  BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);\r
+\r
+  IfrNvData = (IP6_CONFIG_IFR_NVDATA *) AllocateZeroPool (BufferSize);\r
+  if (IfrNvData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = Ip6ConvertConfigNvDataToIfrNvData (IfrNvData, Ip6ConfigInstance);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  ConfigRequest = Request;\r
+  if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {\r
+    //\r
+    // Request has no request element, construct full request string.\r
+    // Allocate and fill a buffer large enough to hold the <ConfigHdr> template\r
+    // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator.\r
+    //\r
+    ConfigRequestHdr = HiiConstructConfigHdr (\r
+                         &mIp6ConfigNvDataGuid,\r
+                         mIp6ConfigStorageName,\r
+                         Private->ChildHandle\r
+                         );\r
+    Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);\r
+    ConfigRequest = AllocateZeroPool (Size);\r
+    ASSERT (ConfigRequest != NULL);\r
+    AllocatedRequest = TRUE;\r
+    UnicodeSPrint (\r
+      ConfigRequest,\r
+      Size,\r
+      L"%s&OFFSET=0&WIDTH=%016LX",\r
+      ConfigRequestHdr,\r
+      (UINT64) BufferSize\r
+      );\r
+    FreePool (ConfigRequestHdr);\r
+  }\r
+\r
+  //\r
+  // Convert buffer data to <ConfigResp> by helper function BlockToConfig()\r
+  //\r
+  Status = gHiiConfigRouting->BlockToConfig (\r
+                                gHiiConfigRouting,\r
+                                ConfigRequest,\r
+                                (UINT8 *) IfrNvData,\r
+                                BufferSize,\r
+                                Results,\r
+                                Progress\r
+                                );\r
+\r
+Exit:\r
+  FreePool (IfrNvData);\r
+  //\r
+  // Free the allocated config request string.\r
+  //\r
+  if (AllocatedRequest) {\r
+    FreePool (ConfigRequest);\r
+    ConfigRequest = NULL;\r
+  }\r
+  //\r
+  // Set Progress string to the original request string.\r
+  //\r
+  if (Request == NULL) {\r
+    *Progress = NULL;\r
+  } else if (StrStr (Request, L"OFFSET") == NULL) {\r
+    *Progress = Request + StrLen (Request);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function applies changes in a driver's configuration.\r
+  Input is a Configuration, which has the routing data for this\r
+  driver followed by name / value configuration pairs. The driver\r
+  must apply those pairs to its configurable storage. If the\r
+  driver's configuration is stored in a linear block of data\r
+  and the driver's name / value pairs are in <BlockConfig>\r
+  format, it may use the ConfigToBlock helper function (above) to\r
+  simplify the job. Currently not implemented.\r
+\r
+  @param[in]  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param[in]  Configuration  A null-terminated Unicode string in\r
+                             <ConfigString> format.\r
+  @param[out] Progress       A pointer to a string filled in with the\r
+                             offset of the most recent '&' before the\r
+                             first failing name / value pair (or the\r
+                             beginn ing of the string if the failure\r
+                             is in the first name / value pair) or\r
+                             the terminating NULL if all was\r
+                             successful.\r
+\r
+  @retval EFI_SUCCESS             The results have been distributed or are\r
+                                  awaiting distribution.\r
+  @retval EFI_OUT_OF_MEMORY       Not enough memory to store the\r
+                                  parts of the results that must be\r
+                                  stored awaiting possible future\r
+                                  protocols.\r
+  @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the\r
+                                  Results parameter would result\r
+                                  in this type of error.\r
+  @retval EFI_NOT_FOUND           Target for the specified routing data\r
+                                  was not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6FormRouteConfig (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  CONST EFI_STRING                       Configuration,\r
+  OUT EFI_STRING                             *Progress\r
+  )\r
+{\r
+  if (This == NULL || Configuration == NULL || Progress == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check routing data in <ConfigHdr>.\r
+  // Note: if only one Storage is used, then this checking could be skipped.\r
+  //\r
+  if (!HiiIsConfigHdrMatch (Configuration, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) {\r
+    *Progress = Configuration;\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  *Progress = Configuration + StrLen (Configuration);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function is called to provide results data to the driver.\r
+  This data consists of a unique key that is used to identify\r
+  which data is either being passed back or being asked for.\r
+\r
+  @param[in]  This               Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param[in]  Action             Specifies the type of action taken by the browser.\r
+  @param[in]  QuestionId         A unique value which is sent to the original\r
+                                 exporting driver so that it can identify the type\r
+                                 of data to expect. The format of the data tends to\r
+                                 vary based on the opcode that generated the callback.\r
+  @param[in]  Type               The type of value for the question.\r
+  @param[in]  Value              A pointer to the data being sent to the original\r
+                                 exporting driver.\r
+  @param[out]  ActionRequest     On return, points to the action requested by the\r
+                                 callback function.\r
+\r
+  @retval EFI_SUCCESS            The callback successfully handled the action.\r
+  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the\r
+                                 variable and its data.\r
+  @retval EFI_DEVICE_ERROR       The variable could not be saved.\r
+  @retval EFI_UNSUPPORTED        The specified Action is not supported by the\r
+                                 callback. Currently not implemented.\r
+  @retval EFI_INVALID_PARAMETER  Passed in the wrong parameter.\r
+  @retval Others                 Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6FormCallback (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  EFI_BROWSER_ACTION                     Action,\r
+  IN  EFI_QUESTION_ID                        QuestionId,\r
+  IN  UINT8                                  Type,\r
+  IN  EFI_IFR_TYPE_VALUE                     *Value,\r
+  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest\r
+  )\r
+{\r
+  IP6_FORM_CALLBACK_INFO        *Private;\r
+  UINTN                         BufferSize;\r
+  IP6_CONFIG_IFR_NVDATA         *IfrNvData;\r
+  IP6_CONFIG_IFR_NVDATA         OldIfrNvData;\r
+  EFI_STATUS                    Status;\r
+  EFI_INPUT_KEY                 Key;\r
+  IP6_CONFIG_INSTANCE           *Instance;\r
+  IP6_CONFIG_NVDATA             *Ip6NvData;\r
+  EFI_IP6_CONFIG_PROTOCOL       *Ip6Config;\r
+  EFI_IP6_CONFIG_INTERFACE_INFO *Data;\r
+  UINTN                         DataSize;\r
+  CHAR16                        PortString[ADDRESS_STR_MAX_SIZE];\r
+  EFI_HII_HANDLE                HiiHandle;\r
+  EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private   = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);\r
+  Instance  = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private);\r
+  Ip6NvData = &Instance->Ip6NvData;\r
+\r
+  if (Action == EFI_BROWSER_ACTION_FORM_OPEN) {\r
+    //\r
+    // Update main Form when main Form is opened.\r
+    // This will be done only in FORM_OPEN CallBack of question with KEY_INTERFACE_ID from main Form.\r
+    //\r
+    if (QuestionId != KEY_INTERFACE_ID) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    Ip6Config = &Instance->Ip6Config;\r
+    HiiHandle = Instance->CallbackInfo.RegisteredHandle;\r
+\r
+    //\r
+    // Get the current interface info.\r
+    //\r
+    Status = Ip6ConfigNvGetData (\r
+               Ip6Config,\r
+               Ip6ConfigDataTypeInterfaceInfo,\r
+               &DataSize,\r
+               (VOID **) &Data\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Generate the dynamic text opcode for host address and draw it.\r
+    //\r
+    IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data;\r
+    Status = Ip6ConvertAddressListToString (\r
+               PortString,\r
+               HiiHandle,\r
+               Ip6ConfigNvHostAddress,\r
+               IfInfo->AddressInfo,\r
+               IfInfo->AddressInfoCount\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Generate the dynamic text opcode for route table and draw it.\r
+    //\r
+    Status = Ip6ConvertAddressListToString (\r
+               PortString,\r
+               HiiHandle,\r
+               Ip6ConfigNvRouteTable,\r
+               IfInfo->RouteTable,\r
+               IfInfo->RouteCount\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Get DNS server list.\r
+    //\r
+    DataSize = 0;\r
+    Status = Ip6ConfigNvGetData (\r
+               Ip6Config,\r
+               Ip6ConfigDataTypeDnsServer,\r
+               &DataSize,\r
+               (VOID **) &Data\r
+               );\r
+    if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+      goto Exit;\r
+    }\r
+\r
+    if (DataSize > 0) {\r
+      //\r
+      // Generate the dynamic text opcode for DNS server and draw it.\r
+      //\r
+      Status = Ip6ConvertAddressListToString (\r
+                 PortString,\r
+                 HiiHandle,\r
+                 Ip6ConfigNvDnsAddress,\r
+                 Data,\r
+                 DataSize / sizeof (EFI_IPv6_ADDRESS)\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Exit;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Get gateway adderss list.\r
+    //\r
+    DataSize = 0;\r
+    Status = Ip6ConfigNvGetData (\r
+               Ip6Config,\r
+               Ip6ConfigDataTypeGateway,\r
+               &DataSize,\r
+               (VOID **) &Data\r
+               );\r
+    if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+      goto Exit;\r
+    }\r
+\r
+    if (DataSize > 0) {\r
+      //\r
+      // Generate the dynamic text opcode for gateway and draw it.\r
+      //\r
+      Status = Ip6ConvertAddressListToString (\r
+                 PortString,\r
+                 HiiHandle,\r
+                 Ip6ConfigNvGatewayAddress,\r
+                 Data,\r
+                 DataSize / sizeof (EFI_IPv6_ADDRESS)\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Exit;\r
+      }\r
+    }\r
+\r
+Exit:\r
+    FreePool (Data);\r
+    return Status;\r
+  }\r
+\r
+  if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) {\r
+    //\r
+    // Do nothing for UEFI FORM_CLOSE action\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if ((Value == NULL) || (ActionRequest == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Retrieve uncommitted data from Browser\r
+  //\r
+\r
+  BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);\r
+  IfrNvData = AllocateZeroPool (BufferSize);\r
+  if (IfrNvData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  ZeroMem (&OldIfrNvData, BufferSize);\r
+\r
+  HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData);\r
+\r
+  CopyMem (&OldIfrNvData, IfrNvData, BufferSize);\r
+\r
+  switch (QuestionId) {\r
+  case KEY_INTERFACE_ID:\r
+    Status = Ip6ParseInterfaceIdFromString (IfrNvData->InterfaceId, &Ip6NvData->InterfaceId);\r
+    if (EFI_ERROR (Status)) {\r
+      CreatePopUp (\r
+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+        &Key,\r
+        L"Invalid Interface ID!",\r
+        NULL\r
+        );\r
+    }\r
+\r
+    break;\r
+\r
+  case KEY_MANUAL_ADDRESS:\r
+    Status = Ip6ParseAddressListFromString (\r
+               IfrNvData->ManualAddress,\r
+               &Ip6NvData->ManualAddress,\r
+               &Ip6NvData->ManualAddressCount\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      CreatePopUp (\r
+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+        &Key,\r
+        L"Invalid Host Addresses!",\r
+        NULL\r
+        );\r
+    }\r
+\r
+    break;\r
+\r
+  case KEY_GATEWAY_ADDRESS:\r
+    Status = Ip6ParseAddressListFromString (\r
+               IfrNvData->GatewayAddress,\r
+               &Ip6NvData->GatewayAddress,\r
+               &Ip6NvData->GatewayAddressCount\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      CreatePopUp (\r
+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+        &Key,\r
+        L"Invalid Gateway Addresses!",\r
+        NULL\r
+        );\r
+    }\r
+\r
+    break;\r
+\r
+  case KEY_DNS_ADDRESS:\r
+    Status = Ip6ParseAddressListFromString (\r
+               IfrNvData->DnsAddress,\r
+               &Ip6NvData->DnsAddress,\r
+               &Ip6NvData->DnsAddressCount\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      CreatePopUp (\r
+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+        &Key,\r
+        L"Invalid DNS Addresses!",\r
+        NULL\r
+        );\r
+    }\r
+\r
+    break;\r
+\r
+  case KEY_SAVE_CONFIG_CHANGES:\r
+    CopyMem (&OldIfrNvData, IfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));\r
+    *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+    break;\r
+\r
+  case KEY_IGNORE_CONFIG_CHANGES:\r
+    CopyMem (IfrNvData, &OldIfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));\r
+    *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+    break;\r
+\r
+  case KEY_SAVE_CHANGES:\r
+    Status = Ip6ConvertIfrNvDataToConfigNvData (IfrNvData, Instance);\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Pass changed uncommitted data back to Form Browser.\r
+    //\r
+    BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);\r
+    HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL);\r
+  }\r
+\r
+  FreePool (IfrNvData);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Install HII Config Access protocol for network device and allocate resources.\r
+\r
+  @param[in, out]  Instance      The IP6_CONFIG_INSTANCE to create a form.\r
+\r
+  @retval EFI_SUCCESS            The HII Config Access protocol is installed.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.\r
+  @retval Others                 Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigFormInit (\r
+  IN OUT IP6_CONFIG_INSTANCE     *Instance\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  IP6_SERVICE                    *IpSb;\r
+  IP6_FORM_CALLBACK_INFO         *CallbackInfo;\r
+  EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;\r
+  VENDOR_DEVICE_PATH             VendorDeviceNode;\r
+  EFI_SERVICE_BINDING_PROTOCOL   *MnpSb;\r
+  CHAR16                         *MacString;\r
+  CHAR16                         MenuString[128];\r
+  CHAR16                         PortString[128];\r
+  CHAR16                         *OldMenuString;\r
+  EFI_DEVICE_PATH_PROTOCOL       *ParentDevicePath;\r
+\r
+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+  ASSERT (IpSb != NULL);\r
+\r
+  Status = gBS->HandleProtocol (\r
+                  IpSb->Controller,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **) &ParentDevicePath\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  CallbackInfo = &Instance->CallbackInfo;\r
+  CallbackInfo->Signature = IP6_FORM_CALLBACK_INFO_SIGNATURE;\r
+\r
+  //\r
+  // Construct device path node for EFI HII Config Access protocol,\r
+  // which consists of controller physical device path and one hardware\r
+  // vendor guid node.\r
+  //\r
+  ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH));\r
+  VendorDeviceNode.Header.Type    = HARDWARE_DEVICE_PATH;\r
+  VendorDeviceNode.Header.SubType = HW_VENDOR_DP;\r
+\r
+  CopyGuid (&VendorDeviceNode.Guid, &mIp6HiiVendorDevicePathGuid);\r
+\r
+  SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH));\r
+  CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode (\r
+                                        ParentDevicePath,\r
+                                        (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode\r
+                                        );\r
+  if (CallbackInfo->HiiVendorDevicePath == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  ConfigAccess                = &CallbackInfo->HiiConfigAccess;\r
+  ConfigAccess->ExtractConfig = Ip6FormExtractConfig;\r
+  ConfigAccess->RouteConfig   = Ip6FormRouteConfig;\r
+  ConfigAccess->Callback      = Ip6FormCallback;\r
+\r
+  //\r
+  // Install Device Path Protocol and Config Access protocol on new handle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &CallbackInfo->ChildHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  CallbackInfo->HiiVendorDevicePath,\r
+                  &gEfiHiiConfigAccessProtocolGuid,\r
+                  ConfigAccess,\r
+                  NULL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Open the Parent Handle for the child\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    IpSb->Controller,\r
+                    &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+                    (VOID **) &MnpSb,\r
+                    IpSb->Image,\r
+                    CallbackInfo->ChildHandle,\r
+                    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                    );\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error;\r
+  }\r
+\r
+  //\r
+  // Publish our HII data\r
+  //\r
+  CallbackInfo->RegisteredHandle = HiiAddPackages (\r
+                                     &mIp6ConfigNvDataGuid,\r
+                                     CallbackInfo->ChildHandle,\r
+                                     Ip6DxeStrings,\r
+                                     Ip6ConfigBin,\r
+                                     NULL\r
+                                     );\r
+  if (CallbackInfo->RegisteredHandle == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  //\r
+  // Append MAC string in the menu string and tile string\r
+  //\r
+  Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &MacString);\r
+  if (!EFI_ERROR (Status)) {\r
+    OldMenuString = HiiGetString (\r
+                      CallbackInfo->RegisteredHandle,\r
+                      STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),\r
+                      NULL)\r
+                      ;\r
+    UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);\r
+    HiiSetString (\r
+      CallbackInfo->RegisteredHandle,\r
+      STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),\r
+      MenuString,\r
+      NULL\r
+      );\r
+    UnicodeSPrint (PortString, 128, L"MAC:%s", MacString);\r
+    HiiSetString (\r
+      CallbackInfo->RegisteredHandle,\r
+      STRING_TOKEN (STR_IP6_DEVICE_FORM_TITLE),\r
+      PortString,\r
+      NULL\r
+      );\r
+\r
+    FreePool (MacString);\r
+    FreePool (OldMenuString);\r
+\r
+    InitializeListHead (&Instance->Ip6NvData.ManualAddress);\r
+    InitializeListHead (&Instance->Ip6NvData.GatewayAddress);\r
+    InitializeListHead (&Instance->Ip6NvData.DnsAddress);\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+Error:\r
+  Ip6ConfigFormUnload (Instance);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Uninstall the HII Config Access protocol for network devices and free up the resources.\r
+\r
+  @param[in, out]  Instance      The IP6_CONFIG_INSTANCE to unload a form.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigFormUnload (\r
+  IN OUT IP6_CONFIG_INSTANCE     *Instance\r
+  )\r
+{\r
+  IP6_SERVICE                    *IpSb;\r
+  IP6_FORM_CALLBACK_INFO         *CallbackInfo;\r
+  IP6_CONFIG_NVDATA              *Ip6NvData;\r
+\r
+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+  ASSERT (IpSb != NULL);\r
+\r
+  CallbackInfo = &Instance->CallbackInfo;\r
+\r
+  if (CallbackInfo->ChildHandle != NULL) {\r
+\r
+    //\r
+    // Close the child handle\r
+    //\r
+    gBS->CloseProtocol (\r
+           IpSb->Controller,\r
+           &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+           IpSb->Image,\r
+           CallbackInfo->ChildHandle\r
+           );\r
+    //\r
+    // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL\r
+    //\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           CallbackInfo->ChildHandle,\r
+           &gEfiDevicePathProtocolGuid,\r
+           CallbackInfo->HiiVendorDevicePath,\r
+           &gEfiHiiConfigAccessProtocolGuid,\r
+           &CallbackInfo->HiiConfigAccess,\r
+           NULL\r
+           );\r
+  }\r
+\r
+  if (CallbackInfo->HiiVendorDevicePath != NULL) {\r
+    FreePool (CallbackInfo->HiiVendorDevicePath);\r
+  }\r
+\r
+  if (CallbackInfo->RegisteredHandle != NULL) {\r
+    //\r
+    // Remove HII package list\r
+    //\r
+    HiiRemovePackages (CallbackInfo->RegisteredHandle);\r
+  }\r
+\r
+  Ip6NvData = &Instance->Ip6NvData;\r
+\r
+  Ip6FreeAddressInfoList (&Ip6NvData->ManualAddress);\r
+  Ip6FreeAddressInfoList (&Ip6NvData->GatewayAddress);\r
+  Ip6FreeAddressInfoList (&Ip6NvData->DnsAddress);\r
+\r
+  Ip6NvData->ManualAddressCount  = 0;\r
+  Ip6NvData->GatewayAddressCount = 0;\r
+  Ip6NvData->DnsAddressCount     = 0;\r
+}\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h
new file mode 100644 (file)
index 0000000..d184776
--- /dev/null
@@ -0,0 +1,73 @@
+/** @file\r
+  The header file of Ip6ConfigNv.c.\r
+\r
+  Copyright (c) 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _IP6_CONFIGNV_H_\r
+#define _IP6_CONFIGNV_H_\r
+\r
+#include "Ip6NvData.h"\r
+#include "Ip6ConfigImpl.h"\r
+\r
+extern UINT8  Ip6ConfigBin[];\r
+extern UINT8  Ip6DxeStrings[];\r
+\r
+#define IP6_HII_VENDOR_DEVICE_PATH_GUID \\r
+  { \\r
+    0x13288098, 0xb11f, 0x45b9, { 0xbc, 0x4f, 0x91, 0xb5, 0x4b, 0xa3, 0x39, 0xb9 } \\r
+  }\r
+\r
+#define IP6_ETHERNET              L"Ethernet"\r
+#define IP6_EXPERIMENTAL_ETHERNET L"Experimental Ethernet"\r
+#define IP6_ADDRESS_DELIMITER     L' '\r
+#define IP6_LINK_LOCAL_PREFIX     L"FE80::"\r
+\r
+typedef enum {\r
+  Ip6InterfaceTypeEthernet = 1,\r
+  Ip6InterfaceTypeExperimentalEthernet\r
+} IP6_INTERFACE_TYPE;\r
+\r
+typedef enum {\r
+  Ip6ConfigNvHostAddress,\r
+  Ip6ConfigNvGatewayAddress,\r
+  Ip6ConfigNvDnsAddress,\r
+  Ip6ConfigNvRouteTable\r
+} IP6_CONFIG_NV_ADDRESS_TYPE;\r
+\r
+/**\r
+  Install HII Config Access protocol for network device and allocate resources.\r
+\r
+  @param[in, out]  Instance      The IP6_CONFIG_INSTANCE to create a form.\r
+\r
+  @retval EFI_SUCCESS            The HII Config Access protocol is installed.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.\r
+  @retval Others                 Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigFormInit (\r
+  IN OUT IP6_CONFIG_INSTANCE     *Instance\r
+  );\r
+\r
+/**\r
+  Uninstall HII Config Access protocol for network device and free resource.\r
+\r
+  @param[in, out]  Instance      The IP6_CONFIG_INSTANCE to unload a form.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigFormUnload (\r
+  IN OUT IP6_CONFIG_INSTANCE     *Instance\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.c b/NetworkPkg/Ip6Dxe/Ip6Driver.c
new file mode 100644 (file)
index 0000000..388dade
--- /dev/null
@@ -0,0 +1,930 @@
+/** @file\r
+  The driver binding and service binding protocol for IP6 driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding = {\r
+  Ip6DriverBindingSupported,\r
+  Ip6DriverBindingStart,\r
+  Ip6DriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+/**\r
+  This is the declaration of an EFI image entry point. This entry point is\r
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including\r
+  both device drivers and bus drivers.\r
+\r
+  The entry point for IP6 driver which installs the driver\r
+  binding and component name protocol on its image.\r
+\r
+  @param[in]  ImageHandle           The firmware allocated handle for the UEFI image.\r
+  @param[in]  SystemTable           A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverEntryPoint (\r
+  IN EFI_HANDLE             ImageHandle,\r
+  IN EFI_SYSTEM_TABLE       *SystemTable\r
+  )\r
+{\r
+  return EfiLibInstallDriverBindingComponentName2 (\r
+           ImageHandle,\r
+           SystemTable,\r
+           &gIp6DriverBinding,\r
+           ImageHandle,\r
+           &gIp6ComponentName,\r
+           &gIp6ComponentName2\r
+           );\r
+}\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle.\r
+\r
+  @param[in]  This                   Protocol instance pointer.\r
+  @param[in]  ControllerHandle       Handle of device to test.\r
+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                     device to start.\r
+\r
+  @retval EFI_SUCCESS                This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED        This driver is already running on this device.\r
+  @retval other                      This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  //\r
+  // Test for the MNP service binding Protocol\r
+  //\r
+  return gBS->OpenProtocol (\r
+                ControllerHandle,\r
+                &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+                NULL,\r
+                This->DriverBindingHandle,\r
+                ControllerHandle,\r
+                EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                );\r
+}\r
+\r
+/**\r
+  Clean up an IP6 service binding instance. It releases all\r
+  the resource allocated by the instance. The instance may be\r
+  partly initialized, or partly destroyed. If a resource is\r
+  destroyed, it is marked as that in case the destory failed and\r
+  being called again later.\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance to clean up.\r
+\r
+  @retval EFI_SUCCESS            The resource used by the instance are cleaned up.\r
+  @retval Others                 Failed to clean up some of the resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CleanService (\r
+  IN IP6_SERVICE            *IpSb\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_IPv6_ADDRESS          AllNodes;\r
+  IP6_NEIGHBOR_ENTRY        *NeighborCache;\r
+\r
+  Ip6ConfigCleanInstance (&IpSb->Ip6ConfigInstance);\r
+\r
+  //\r
+  // Leave link-scope all-nodes multicast address (FF02::1)\r
+  //\r
+  Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);\r
+\r
+  Status = Ip6LeaveGroup (IpSb, &AllNodes);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (IpSb->DefaultInterface != NULL) {\r
+    Ip6CleanInterface (IpSb->DefaultInterface, NULL);\r
+    IpSb->DefaultInterface = NULL;\r
+  }\r
+\r
+  Ip6CleanDefaultRouterList (IpSb);\r
+\r
+  Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix);\r
+  Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix);\r
+\r
+  if (IpSb->RouteTable != NULL) {\r
+    Ip6CleanRouteTable (IpSb->RouteTable);\r
+    IpSb->RouteTable = NULL;\r
+  }\r
+\r
+  if (IpSb->InterfaceId != NULL) {\r
+    FreePool (IpSb->InterfaceId);\r
+  }\r
+\r
+  IpSb->InterfaceId = NULL;\r
+\r
+  Ip6CleanAssembleTable (&IpSb->Assemble);\r
+\r
+  if (IpSb->MnpChildHandle != NULL) {\r
+    if (IpSb->Mnp != NULL) {\r
+      IpSb->Mnp->Cancel (IpSb->Mnp, NULL);\r
+      IpSb->Mnp->Configure (IpSb->Mnp, NULL);\r
+      gBS->CloseProtocol (\r
+            IpSb->MnpChildHandle,\r
+            &gEfiManagedNetworkProtocolGuid,\r
+            IpSb->Image,\r
+            IpSb->Controller\r
+            );\r
+\r
+      IpSb->Mnp = NULL;\r
+    }\r
+\r
+    NetLibDestroyServiceChild (\r
+      IpSb->Controller,\r
+      IpSb->Image,\r
+      &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+      IpSb->MnpChildHandle\r
+      );\r
+\r
+    IpSb->MnpChildHandle = NULL;\r
+  }\r
+\r
+  if (IpSb->RecvRequest.MnpToken.Event != NULL) {\r
+    gBS->CloseEvent (IpSb->RecvRequest.MnpToken.Event);\r
+  }\r
+\r
+  if (IpSb->Timer != NULL) {\r
+    gBS->SetTimer (IpSb->Timer, TimerCancel, 0);\r
+    gBS->CloseEvent (IpSb->Timer);\r
+\r
+    IpSb->Timer = NULL;\r
+  }\r
+\r
+  if (IpSb->FasterTimer != NULL) {\r
+    gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);\r
+    gBS->CloseEvent (IpSb->FasterTimer);\r
+\r
+    IpSb->FasterTimer = NULL;\r
+  }\r
+  //\r
+  // Free the Neighbor Discovery resources\r
+  //\r
+  while (!IsListEmpty (&IpSb->NeighborTable)) {\r
+    NeighborCache = NET_LIST_HEAD (&IpSb->NeighborTable, IP6_NEIGHBOR_ENTRY, Link);\r
+    Ip6FreeNeighborEntry (IpSb, NeighborCache, FALSE, TRUE, EFI_SUCCESS, NULL, NULL);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Create a new IP6 driver service binding protocol.\r
+\r
+  @param[in]  Controller         The controller that has MNP service binding\r
+                                 installed.\r
+  @param[in]  ImageHandle        The IP6 driver's image handle.\r
+  @param[out]  Service           The variable to receive the newly created IP6\r
+                                 service.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.\r
+  @retval EFI_SUCCESS            A new IP6 service binding private is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CreateService (\r
+  IN  EFI_HANDLE            Controller,\r
+  IN  EFI_HANDLE            ImageHandle,\r
+  OUT IP6_SERVICE           **Service\r
+  )\r
+{\r
+  IP6_SERVICE                           *IpSb;\r
+  EFI_STATUS                            Status;\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;\r
+  EFI_MANAGED_NETWORK_CONFIG_DATA       *Config;\r
+  IP6_CONFIG_DATA_ITEM                  *DataItem;\r
+\r
+  ASSERT (Service != NULL);\r
+\r
+  *Service = NULL;\r
+\r
+  //\r
+  // allocate a service private data then initialize all the filed to\r
+  // empty resources, so if any thing goes wrong when allocating\r
+  // resources, Ip6CleanService can be called to clean it up.\r
+  //\r
+  IpSb = AllocateZeroPool (sizeof (IP6_SERVICE));\r
+\r
+  if (IpSb == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  IpSb->Signature                   = IP6_SERVICE_SIGNATURE;\r
+  IpSb->ServiceBinding.CreateChild  = Ip6ServiceBindingCreateChild;\r
+  IpSb->ServiceBinding.DestroyChild = Ip6ServiceBindingDestroyChild;\r
+  IpSb->State                       = IP6_SERVICE_UNSTARTED;\r
+  IpSb->InDestroy                   = FALSE;\r
+\r
+  IpSb->NumChildren                 = 0;\r
+  InitializeListHead (&IpSb->Children);\r
+\r
+  InitializeListHead (&IpSb->Interfaces);\r
+  IpSb->DefaultInterface            = NULL;\r
+  IpSb->RouteTable                  = NULL;\r
+\r
+  IpSb->RecvRequest.Signature       = IP6_LINK_RX_SIGNATURE;\r
+  IpSb->RecvRequest.CallBack        = NULL;\r
+  IpSb->RecvRequest.Context         = NULL;\r
+  MnpToken                          = &IpSb->RecvRequest.MnpToken;\r
+  MnpToken->Event                   = NULL;\r
+  MnpToken->Status                  = EFI_NOT_READY;\r
+  MnpToken->Packet.RxData           = NULL;\r
+\r
+  Ip6CreateAssembleTable (&IpSb->Assemble);\r
+\r
+  IpSb->MldCtrl.Mldv1QuerySeen      = 0;\r
+  InitializeListHead (&IpSb->MldCtrl.Groups);\r
+\r
+  ZeroMem (&IpSb->LinkLocalAddr, sizeof (EFI_IPv6_ADDRESS));\r
+  IpSb->LinkLocalOk                 = FALSE;\r
+  IpSb->LinkLocalDadFail            = FALSE;\r
+  IpSb->Dhcp6NeedStart              = FALSE;\r
+  IpSb->Dhcp6NeedInfoRequest        = FALSE;\r
+\r
+  IpSb->CurHopLimit                 = IP6_HOP_LIMIT;\r
+  IpSb->LinkMTU                     = IP6_MIN_LINK_MTU;\r
+  IpSb->BaseReachableTime           = IP6_REACHABLE_TIME;\r
+  Ip6UpdateReachableTime (IpSb);\r
+  //\r
+  // RFC4861 RETRANS_TIMER: 1,000 milliseconds\r
+  //\r
+  IpSb->RetransTimer                = IP6_RETRANS_TIMER;\r
+\r
+  IpSb->RoundRobin                  = 0;\r
+\r
+  InitializeListHead (&IpSb->NeighborTable);\r
+  InitializeListHead (&IpSb->DefaultRouterList);\r
+  InitializeListHead (&IpSb->OnlinkPrefix);\r
+  InitializeListHead (&IpSb->AutonomousPrefix);\r
+\r
+  IpSb->InterfaceIdLen              = IP6_IF_ID_LEN;\r
+  IpSb->InterfaceId                 = NULL;\r
+\r
+  IpSb->RouterAdvertiseReceived     = FALSE;\r
+  IpSb->SolicitTimer                = IP6_MAX_RTR_SOLICITATIONS;\r
+  IpSb->Ticks                       = 0;\r
+\r
+  IpSb->Image                       = ImageHandle;\r
+  IpSb->Controller                  = Controller;\r
+\r
+  IpSb->MnpChildHandle              = NULL;\r
+  IpSb->Mnp                         = NULL;\r
+\r
+  Config                            = &IpSb->MnpConfigData;\r
+  Config->ReceivedQueueTimeoutValue = 0;\r
+  Config->TransmitQueueTimeoutValue = 0;\r
+  Config->ProtocolTypeFilter        = IP6_ETHER_PROTO;\r
+  Config->EnableUnicastReceive      = TRUE;\r
+  Config->EnableMulticastReceive    = TRUE;\r
+  Config->EnableBroadcastReceive    = TRUE;\r
+  Config->EnablePromiscuousReceive  = FALSE;\r
+  Config->FlushQueuesOnReset        = TRUE;\r
+  Config->EnableReceiveTimestamps   = FALSE;\r
+  Config->DisableBackgroundPolling  = FALSE;\r
+\r
+  ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));\r
+\r
+  IpSb->Timer                       = NULL;\r
+  IpSb->FasterTimer                 = NULL;\r
+\r
+  ZeroMem (&IpSb->Ip6ConfigInstance, sizeof (IP6_CONFIG_INSTANCE));\r
+\r
+  IpSb->MacString                   = NULL;\r
+\r
+  //\r
+  // Create various resources. First create the route table, timer\r
+  // event, MNP token event and MNP child.\r
+  //\r
+\r
+  IpSb->RouteTable = Ip6CreateRouteTable ();\r
+  if (IpSb->RouteTable == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  Ip6TimerTicking,\r
+                  IpSb,\r
+                  &IpSb->Timer\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  Ip6NdFasterTimerTicking,\r
+                  IpSb,\r
+                  &IpSb->FasterTimer\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = NetLibCreateServiceChild (\r
+             Controller,\r
+             ImageHandle,\r
+             &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+             &IpSb->MnpChildHandle\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  IpSb->MnpChildHandle,\r
+                  &gEfiManagedNetworkProtocolGuid,\r
+                  (VOID **) (&IpSb->Mnp),\r
+                  ImageHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Ip6ServiceConfigMnp (IpSb, TRUE);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  IpSb->MaxPacketSize = IP6_MIN_LINK_MTU - sizeof (EFI_IP6_HEADER);\r
+  if (NetLibGetVlanId (IpSb->Controller) != 0) {\r
+    //\r
+    // This is a VLAN device, reduce MTU by VLAN tag length\r
+    //\r
+    IpSb->MaxPacketSize -= NET_VLAN_TAG_LEN;\r
+  }\r
+  IpSb->OldMaxPacketSize = IpSb->MaxPacketSize;\r
+\r
+  //\r
+  // Currently only ETHERNET is supported in IPv6 stack, since\r
+  // link local address requires an IEEE 802 48-bit MACs for\r
+  // EUI-64 format interface identifier mapping.\r
+  //\r
+  if (IpSb->SnpMode.IfType != NET_IFTYPE_ETHERNET) {\r
+    Status = EFI_UNSUPPORTED;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Ip6InitMld (IpSb);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // The timer expires every 100 (IP6_TIMER_INTERVAL_IN_MS) milliseconds.\r
+  //\r
+  Status = gBS->SetTimer (IpSb->FasterTimer, TimerPeriodic, TICKS_PER_MS * IP6_TIMER_INTERVAL_IN_MS);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // The timer expires every 1000 (IP6_ONE_SECOND_IN_MS) milliseconds.\r
+  //\r
+  Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_MS * IP6_ONE_SECOND_IN_MS);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  Ip6OnFrameReceived,\r
+                  &IpSb->RecvRequest,\r
+                  &MnpToken->Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Ip6ConfigInitInstance (&IpSb->Ip6ConfigInstance);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  IpSb->DefaultInterface = Ip6CreateInterface (IpSb, TRUE);\r
+  if (IpSb->DefaultInterface == NULL) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // If there is any manual address, set it.\r
+  //\r
+  DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress];\r
+  if (DataItem->Data.Ptr != NULL) {\r
+    DataItem->SetData (\r
+                &IpSb->Ip6ConfigInstance,\r
+                DataItem->DataSize,\r
+                DataItem->Data.Ptr\r
+                );\r
+  }\r
+\r
+  InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link);\r
+\r
+  *Service = IpSb;\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  Ip6CleanService (IpSb);\r
+  FreePool (IpSb);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  @param[in]  This                Protocol instance pointer.\r
+  @param[in]  ControllerHandle    Handle of device to bind driver to.\r
+  @param[in]  RemainingDevicePath Optional parameter used to pick a specific child\r
+                                  device to start.\r
+\r
+  @retval EFI_SUCCES              This driver is added to ControllerHandle.\r
+  @retval EFI_ALREADY_STARTED     This driver is already running on ControllerHandle.\r
+  @retval other                   This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  IP6_SERVICE               *IpSb;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // Test for the Ip6 service binding protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  Status = Ip6CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  ASSERT (IpSb != NULL);\r
+\r
+  //\r
+  // Install the Ip6ServiceBinding Protocol onto ControlerHandle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &ControllerHandle,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  &IpSb->ServiceBinding,\r
+                  &gEfiIp6ConfigProtocolGuid,\r
+                  &IpSb->Ip6ConfigInstance.Ip6Config,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    Ip6CleanService (IpSb);\r
+    FreePool (IpSb);\r
+  } else {\r
+    //\r
+    // Initialize the IP6 ID\r
+    //\r
+    mIp6Id = NET_RANDOM (NetRandomInitSeed ());\r
+\r
+    Ip6SetVariableData (IpSb);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  ControllerHandle   Handle of device to stop driver on.\r
+  @param[in]  NumberOfChildren   Number of Handles in ChildHandleBuffer. If number\r
+                                 of children is zero, stop the entire  bus driver.\r
+  @param[in]  ChildHandleBuffer  An array of child handles to be freed. May be NULL\r
+                                 if NumberOfChildren is 0.\r
+\r
+  @retval EFI_SUCCESS           The device was stopped.\r
+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL\r
+  )\r
+{\r
+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
+  IP6_SERVICE                   *IpSb;\r
+  IP6_PROTOCOL                  *IpInstance;\r
+  EFI_HANDLE                    NicHandle;\r
+  EFI_STATUS                    Status;\r
+  BOOLEAN                       IsDhcp6;\r
+  EFI_TPL                       OldTpl;\r
+  INTN                          State;\r
+\r
+  IsDhcp6   = FALSE;\r
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);\r
+\r
+  if (NicHandle != NULL) {\r
+    //\r
+    // DriverBindingStop is triggered by the uninstallation of the EFI DHCPv6\r
+    // Protocol used by Ip6Config.\r
+    //\r
+    IsDhcp6 = TRUE;\r
+  } else {\r
+\r
+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);\r
+\r
+    if (NicHandle == NULL) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  NicHandle,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  (VOID **) &ServiceBinding,\r
+                  This->DriverBindingHandle,\r
+                  NicHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  IpSb = IP6_SERVICE_FROM_PROTOCOL (ServiceBinding);\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (IpSb->InDestroy) {\r
+    Status = EFI_SUCCESS;\r
+    goto Exit;\r
+  }\r
+\r
+  if (IsDhcp6) {\r
+\r
+    Status = Ip6ConfigDestroyDhcp6 (&IpSb->Ip6ConfigInstance);\r
+    gBS->CloseEvent (IpSb->Ip6ConfigInstance.Dhcp6Event);\r
+    IpSb->Ip6ConfigInstance.Dhcp6Event = NULL;\r
+  } else if (NumberOfChildren == 0) {\r
+\r
+    IpSb->InDestroy = TRUE;\r
+    State           = IpSb->State;\r
+    IpSb->State     = IP6_SERVICE_DESTROY;\r
+\r
+    //\r
+    // Clear the variable data.\r
+    //\r
+    Ip6ClearVariableData (IpSb);\r
+\r
+    Status = Ip6CleanService (IpSb);\r
+    if (EFI_ERROR (Status)) {\r
+      IpSb->State = State;\r
+      goto Exit;\r
+    }\r
+\r
+    Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                    NicHandle,\r
+                    &gEfiIp6ServiceBindingProtocolGuid,\r
+                    ServiceBinding,\r
+                    &gEfiIp6ConfigProtocolGuid,\r
+                    &IpSb->Ip6ConfigInstance.Ip6Config,\r
+                    NULL\r
+                    );\r
+    ASSERT_EFI_ERROR (Status);\r
+    FreePool (IpSb);\r
+  } else {\r
+    //\r
+    // NumberOfChildren is not zero, destroy all IP6 children instances.\r
+    //\r
+    while (!IsListEmpty (&IpSb->Children)) {\r
+      IpInstance = NET_LIST_HEAD (&IpSb->Children, IP6_PROTOCOL, Link);\r
+      ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle);\r
+    }\r
+\r
+    if (IpSb->NumChildren != 0) {\r
+      Status = EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+Exit:\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Creates a child handle with a set of I/O services.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  ChildHandle        Pointer to the handle of the child to create.   If\r
+                                 it is NULL, then a new handle is created.   If it\r
+                                 is not NULL, then the I/O services are added to\r
+                                 the existing child handle.\r
+\r
+  @retval EFI_SUCCES             The child handle was created with the I/O services.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources availabe to create\r
+                                 the child.\r
+  @retval other                  The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ServiceBindingCreateChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    *ChildHandle\r
+  )\r
+{\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_PROTOCOL              *IpInstance;\r
+  EFI_TPL                   OldTpl;\r
+  EFI_STATUS                Status;\r
+  VOID                      *Mnp;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpSb       = IP6_SERVICE_FROM_PROTOCOL (This);\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  IpInstance = AllocatePool (sizeof (IP6_PROTOCOL));\r
+\r
+  if (IpInstance == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ip6InitProtocol (IpSb, IpInstance);\r
+\r
+  //\r
+  // Install Ip6 onto ChildHandle\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiIp6ProtocolGuid,\r
+                  &IpInstance->Ip6Proto,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  IpInstance->Handle = *ChildHandle;\r
+\r
+  //\r
+  // Open the Managed Network protocol BY_CHILD.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  IpSb->MnpChildHandle,\r
+                  &gEfiManagedNetworkProtocolGuid,\r
+                  (VOID **) &Mnp,\r
+                  gIp6DriverBinding.DriverBindingHandle,\r
+                  IpInstance->Handle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           ChildHandle,\r
+           &gEfiIp6ProtocolGuid,\r
+           &IpInstance->Ip6Proto,\r
+           NULL\r
+           );\r
+\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Insert it into the service binding instance.\r
+  //\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  InsertTailList (&IpSb->Children, &IpInstance->Link);\r
+  IpSb->NumChildren++;\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+ON_ERROR:\r
+\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    Ip6CleanProtocol (IpInstance);\r
+\r
+    FreePool (IpInstance);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Destroys a child handle with a set of I/O services.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  ChildHandle        Handle of the child to destroy.\r
+\r
+  @retval EFI_SUCCES             The I/O services were removed from the child\r
+                                 handle.\r
+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services\r
+                                  that are being removed.\r
+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.\r
+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because\r
+                                 its I/O services are being used.\r
+  @retval other                  The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_PROTOCOL              *IpInstance;\r
+  EFI_IP6_PROTOCOL          *Ip6;\r
+  EFI_TPL                   OldTpl;\r
+  INTN                      State;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Retrieve the private context data structures\r
+  //\r
+  IpSb   = IP6_SERVICE_FROM_PROTOCOL (This);\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ChildHandle,\r
+                  &gEfiIp6ProtocolGuid,\r
+                  (VOID **) &Ip6,\r
+                  gIp6DriverBinding.DriverBindingHandle,\r
+                  ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (Ip6);\r
+\r
+  if (IpInstance->Service != IpSb) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // A child can be destroyed more than once. For example,\r
+  // Ip6DriverBindingStop will destory all of its children.\r
+  // when UDP driver is being stopped, it will destory all\r
+  // the IP child it opens.\r
+  //\r
+  if (IpInstance->State == IP6_STATE_DESTROY) {\r
+    gBS->RestoreTPL (OldTpl);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  State             = IpInstance->State;\r
+  IpInstance->State = IP6_STATE_DESTROY;\r
+\r
+  //\r
+  // Close the Managed Network protocol.\r
+  //\r
+  gBS->CloseProtocol (\r
+         IpSb->MnpChildHandle,\r
+         &gEfiManagedNetworkProtocolGuid,\r
+         gIp6DriverBinding.DriverBindingHandle,\r
+         ChildHandle\r
+         );\r
+\r
+  //\r
+  // Uninstall the IP6 protocol first. Many thing happens during\r
+  // this:\r
+  // 1. The consumer of the IP6 protocol will be stopped if it\r
+  // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is\r
+  // stopped, IP driver's stop function will be called, and uninstall\r
+  // EFI_IP6_PROTOCOL will trigger the UDP's stop function. This\r
+  // makes it possible to create the network stack bottom up, and\r
+  // stop it top down.\r
+  // 2. the upper layer will recycle the received packet. The recycle\r
+  // event's TPL is higher than this function. The recycle events\r
+  // will be called back before preceeding. If any packets not recycled,\r
+  // that means there is a resource leak.\r
+  //\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  ChildHandle,\r
+                  &gEfiIp6ProtocolGuid,\r
+                  &IpInstance->Ip6Proto\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = Ip6CleanProtocol (IpInstance);\r
+\r
+  Ip6SetVariableData (IpSb);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->InstallMultipleProtocolInterfaces (\r
+           &ChildHandle,\r
+           &gEfiIp6ProtocolGuid,\r
+           Ip6,\r
+           NULL\r
+           );\r
+\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  RemoveEntryList (&IpInstance->Link);\r
+  ASSERT (IpSb->NumChildren > 0);\r
+  IpSb->NumChildren--;\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  FreePool (IpInstance);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  IpInstance->State = State;\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.h b/NetworkPkg/Ip6Dxe/Ip6Driver.h
new file mode 100644 (file)
index 0000000..fcb92ab
--- /dev/null
@@ -0,0 +1,185 @@
+/** @file\r
+  The driver binding and service binding protocol for IP6 driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_DRIVER_H__\r
+#define __EFI_IP6_DRIVER_H__\r
+\r
+extern EFI_DRIVER_BINDING_PROTOCOL  gIp6DriverBinding;\r
+extern EFI_COMPONENT_NAME_PROTOCOL  gIp6ComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2;\r
+\r
+/**\r
+  Clean up an IP6 service binding instance. It releases all\r
+  the resource allocated by the instance. The instance may be\r
+  partly initialized, or partly destroyed. If a resource is\r
+  destroyed, it is marked as that in case the destory failed and\r
+  being called again later.\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance to clean up.\r
+\r
+  @retval EFI_SUCCESS            The resource used by the instance are cleaned up.\r
+  @retval Others                 Failed to clean up some of the resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CleanService (\r
+  IN IP6_SERVICE            *IpSb\r
+  );\r
+\r
+//\r
+// Function prototype for the driver's entry point\r
+//\r
+\r
+/**\r
+  This is the declaration of an EFI image entry point. This entry point is\r
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including\r
+  both device drivers and bus drivers.\r
+\r
+  The entry point for IP6 driver which installs the driver\r
+  binding and component name protocol on its image.\r
+\r
+  @param[in]  ImageHandle           The firmware allocated handle for the UEFI image.\r
+  @param[in]  SystemTable           A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverEntryPoint (\r
+  IN EFI_HANDLE             ImageHandle,\r
+  IN EFI_SYSTEM_TABLE       *SystemTable\r
+  );\r
+\r
+//\r
+// Function prototypes for the Drivr Binding Protocol\r
+//\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle.\r
+\r
+  @param[in]  This                   Protocol instance pointer.\r
+  @param[in]  ControllerHandle       Handle of device to test.\r
+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                     device to start.\r
+\r
+  @retval EFI_SUCCESS                This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED        This driver is already running on this device.\r
+  @retval other                      This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  @param[in]  This                Protocol instance pointer.\r
+  @param[in]  ControllerHandle    Handle of device to bind driver to.\r
+  @param[in]  RemainingDevicePath Optional parameter used to pick a specific child\r
+                                  device to start.\r
+\r
+  @retval EFI_SUCCES              This driver is added to ControllerHandle.\r
+  @retval EFI_ALREADY_STARTED     This driver is already running on ControllerHandle.\r
+  @retval other                   This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  ControllerHandle   Handle of device to stop driver on.\r
+  @param[in]  NumberOfChildren   Number of Handles in ChildHandleBuffer. If number\r
+                                 of children is zero, stop the entire  bus driver.\r
+  @param[in]  ChildHandleBuffer  An array of child handles to be freed. May be NULL\r
+                                 if NumberOfChildren is 0.\r
+\r
+  @retval EFI_SUCCESS           The device was stopped.\r
+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL\r
+  );\r
+\r
+//\r
+// Function ptototypes for the ServiceBinding Prococol\r
+//\r
+\r
+/**\r
+  Creates a child handle with a set of I/O services.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  ChildHandle        Pointer to the handle of the child to create.   If\r
+                                 it is NULL, then a new handle is created.   If it\r
+                                 is not NULL, then the I/O services are added to\r
+                                 the existing child handle.\r
+\r
+  @retval EFI_SUCCES             The child handle was created with the I/O services.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources availabe to create\r
+                                 the child.\r
+  @retval other                  The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ServiceBindingCreateChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    *ChildHandle\r
+  );\r
+\r
+/**\r
+  Destroys a child handle with a set of I/O services.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  ChildHandle        Handle of the child to destroy.\r
+\r
+  @retval EFI_SUCCES             The I/O services were removed from the child\r
+                                 handle.\r
+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services\r
+                                  that are being removed.\r
+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.\r
+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because\r
+                                 its  I/O services are being used.\r
+  @retval other                  The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Dxe.inf b/NetworkPkg/Ip6Dxe/Ip6Dxe.inf
new file mode 100644 (file)
index 0000000..aeb341c
--- /dev/null
@@ -0,0 +1,100 @@
+## @file\r
+#  Component description file for Ip6 module.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = Ip6Dxe\r
+  FILE_GUID                      = 5BEDB5CC-D830-4eb2-8742-2D4CC9B54F2C\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = Ip6DriverEntryPoint\r
+  UNLOAD_IMAGE                   = NetLibDefaultUnload\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+#  DRIVER_BINDING                =  gIp6DriverBinding\r
+#  COMPONENT_NAME                =  gIp6ComponentName\r
+#  COMPONENT_NAME2               =  gIp6ComponentName2\r
+#\r
+\r
+[Sources]\r
+  Ip6Output.h\r
+  Ip6Option.h\r
+  Ip6Input.h\r
+  Ip6Nd.h\r
+  Ip6Mld.h\r
+  Ip6Impl.c\r
+  Ip6Driver.c\r
+  ComponentName.c\r
+  Ip6Nd.c\r
+  Ip6Input.c\r
+  Ip6ConfigImpl.c\r
+  Ip6ConfigImpl.h\r
+  Ip6Impl.h\r
+  Ip6Option.c\r
+  Ip6If.h\r
+  Ip6Icmp.h\r
+  Ip6Mld.c\r
+  Ip6Common.c\r
+  Ip6Route.c\r
+  Ip6If.c\r
+  Ip6Driver.h\r
+  Ip6Output.c\r
+  Ip6Icmp.c\r
+  Ip6Common.h\r
+  Ip6Route.h\r
+  Ip6DxeStrings.uni\r
+  Ip6NvData.h\r
+  Ip6ConfigNv.c\r
+  Ip6ConfigNv.h\r
+  Ip6Config.vfr\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  BaseMemoryLib\r
+  DevicePathLib\r
+  HiiLib\r
+  UefiHiiServicesLib\r
+  PrintLib\r
+  MemoryAllocationLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  UefiRuntimeServicesTableLib\r
+  UefiLib\r
+  DebugLib\r
+  NetLib\r
+  DpcLib\r
+\r
+[Protocols]\r
+  gEfiManagedNetworkServiceBindingProtocolGuid\r
+  gEfiManagedNetworkProtocolGuid\r
+  gEfiIp6ServiceBindingProtocolGuid\r
+  gEfiIp6ProtocolGuid\r
+  gEfiIp6ConfigProtocolGuid\r
+  gEfiDhcp6ServiceBindingProtocolGuid\r
+  gEfiDhcp6ProtocolGuid\r
+  gEfiIpSecProtocolGuid\r
+  gEfiHiiConfigAccessProtocolGuid\r
+\r
+[Guids]\r
+  gEfiIfrTianoGuid                              ## CONSUMES ## GUID\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
new file mode 100644 (file)
index 0000000..cf85e08
Binary files /dev/null and b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni differ
diff --git a/NetworkPkg/Ip6Dxe/Ip6Icmp.c b/NetworkPkg/Ip6Dxe/Ip6Icmp.c
new file mode 100644 (file)
index 0000000..db40b81
--- /dev/null
@@ -0,0 +1,684 @@
+/** @file\r
+  The ICMPv6 handle routines to process the ICMPv6 control messages.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[23] = {\r
+\r
+  {\r
+    ICMP_V6_DEST_UNREACHABLE,\r
+    ICMP_V6_NO_ROUTE_TO_DEST\r
+  },\r
+  {\r
+    ICMP_V6_DEST_UNREACHABLE,\r
+    ICMP_V6_COMM_PROHIBITED\r
+  },\r
+  {\r
+    ICMP_V6_DEST_UNREACHABLE,\r
+    ICMP_V6_BEYOND_SCOPE\r
+  },\r
+  {\r
+    ICMP_V6_DEST_UNREACHABLE,\r
+    ICMP_V6_ADDR_UNREACHABLE\r
+  },\r
+  {\r
+    ICMP_V6_DEST_UNREACHABLE,\r
+    ICMP_V6_PORT_UNREACHABLE\r
+  },\r
+  {\r
+    ICMP_V6_DEST_UNREACHABLE,\r
+    ICMP_V6_SOURCE_ADDR_FAILED\r
+  },\r
+  {\r
+    ICMP_V6_DEST_UNREACHABLE,\r
+    ICMP_V6_ROUTE_REJECTED\r
+  },\r
+\r
+  {\r
+    ICMP_V6_PACKET_TOO_BIG,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+\r
+  {\r
+    ICMP_V6_TIME_EXCEEDED,\r
+    ICMP_V6_TIMEOUT_HOP_LIMIT\r
+  },\r
+  {\r
+    ICMP_V6_TIME_EXCEEDED,\r
+    ICMP_V6_TIMEOUT_REASSEMBLE\r
+  },\r
+\r
+  {\r
+    ICMP_V6_PARAMETER_PROBLEM,\r
+    ICMP_V6_ERRONEOUS_HEADER\r
+  },\r
+  {\r
+    ICMP_V6_PARAMETER_PROBLEM,\r
+    ICMP_V6_UNRECOGNIZE_NEXT_HDR\r
+  },\r
+  {\r
+    ICMP_V6_PARAMETER_PROBLEM,\r
+    ICMP_V6_UNRECOGNIZE_OPTION\r
+  },\r
+\r
+  {\r
+    ICMP_V6_ECHO_REQUEST,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+  {\r
+    ICMP_V6_ECHO_REPLY,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+\r
+  {\r
+    ICMP_V6_LISTENER_QUERY,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+  {\r
+    ICMP_V6_LISTENER_REPORT,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+  {\r
+    ICMP_V6_LISTENER_REPORT_2,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+  {\r
+    ICMP_V6_LISTENER_DONE,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+\r
+  {\r
+    ICMP_V6_ROUTER_SOLICIT,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+  {\r
+    ICMP_V6_ROUTER_ADVERTISE,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+  {\r
+    ICMP_V6_NEIGHBOR_SOLICIT,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+  {\r
+    ICMP_V6_NEIGHBOR_ADVERTISE,\r
+    ICMP_V6_DEFAULT_CODE\r
+  },\r
+};\r
+\r
+/**\r
+  Reply an ICMPv6 echo request.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the ICMPv6 informational message.\r
+  @param[in]  Packet             The content of the ICMPv6 message with the IP head\r
+                                 removed.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.\r
+  @retval EFI_SUCCESS            Successfully answered the ICMPv6 Echo request.\r
+  @retval Others                 Failed to answer the ICMPv6 Echo request.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IcmpReplyEcho (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_INFORMATION_HEAD *Icmp;\r
+  NET_BUF                   *Data;\r
+  EFI_STATUS                Status;\r
+  EFI_IP6_HEADER            ReplyHead;\r
+\r
+  Status = EFI_OUT_OF_RESOURCES;\r
+  //\r
+  // make a copy the packet, it is really a bad idea to\r
+  // send the MNP's buffer back to MNP.\r
+  //\r
+  Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN);\r
+  if (Data == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Change the ICMP type to echo reply, exchange the source\r
+  // and destination, then send it. The source is updated to\r
+  // use specific destination. See RFC1122. SRR/RR option\r
+  // update is omitted.\r
+  //\r
+  Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Data, 0, NULL);\r
+  if (Icmp == NULL) {\r
+    NetbufFree (Data);\r
+    goto Exit;\r
+  }\r
+\r
+  Icmp->Head.Type     = ICMP_V6_ECHO_REPLY;\r
+  Icmp->Head.Checksum = 0;\r
+\r
+  //\r
+  // Generate the IPv6 basic header\r
+  // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address,\r
+  // the Source address of the Echo Reply must be the same address.\r
+  //\r
+  ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER));\r
+\r
+  ReplyHead.PayloadLength  = HTONS ((UINT16) (Packet->TotalSize));\r
+  ReplyHead.NextHeader     = IP6_ICMP;\r
+  ReplyHead.HopLimit       = IpSb->CurHopLimit;\r
+  IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress);\r
+\r
+  if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {\r
+    IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress);\r
+  }\r
+\r
+  //\r
+  // If source is unspecified, Ip6Output will select a source for us\r
+  //\r
+  Status = Ip6Output (\r
+             IpSb,\r
+             NULL,\r
+             NULL,\r
+             Data,\r
+             &ReplyHead,\r
+             NULL,\r
+             0,\r
+             Ip6SysPacketSent,\r
+             NULL\r
+             );\r
+\r
+Exit:\r
+  NetbufFree (Packet);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Process Packet Too Big message sent by a router in response to a packet that\r
+  it cannot forward because the packet is larger than the MTU of outgoing link.\r
+  Since this driver already uses IPv6 minimum link MTU as the maximum packet size,\r
+  if Packet Too Big message is still received, do not reduce the packet size, but\r
+  rather include a Fragment header in the subsequent packets.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the ICMPv6 error packet.\r
+  @param[in]  Packet             The content of the ICMPv6 error with the IP head\r
+                                 removed.\r
+\r
+  @retval EFI_SUCCESS            The ICMPv6 error processed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to finish the operation due to lack of\r
+                                 resource.\r
+  @retval EFI_NOT_FOUND          The packet too big message is not sent to us.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessPacketTooBig (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_ERROR_HEAD       Icmp;\r
+  UINT32                    Mtu;\r
+  IP6_ROUTE_ENTRY           *RouteEntry;\r
+  EFI_IPv6_ADDRESS          *DestAddress;\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+  Mtu         = NTOHL (Icmp.Fourth);\r
+  DestAddress = &Icmp.IpHead.DestinationAddress;\r
+\r
+  if (Mtu < IP6_MIN_LINK_MTU) {\r
+    //\r
+    // Normally the multicast address is considered to be on-link and not recorded\r
+    // in route table. Here it is added into the table since the MTU information\r
+    // need be recorded.\r
+    //\r
+    if (IP6_IS_MULTICAST (DestAddress)) {\r
+      RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL);\r
+      if (RouteEntry == NULL) {\r
+        NetbufFree (Packet);\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG;\r
+      InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link);\r
+      IpSb->RouteTable->TotalNum++;\r
+    } else {\r
+      RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL);\r
+      if (RouteEntry == NULL) {\r
+        NetbufFree (Packet);\r
+        return EFI_NOT_FOUND;\r
+      }\r
+\r
+      RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG;\r
+\r
+      Ip6FreeRouteEntry (RouteEntry);\r
+    }\r
+  }\r
+\r
+  NetbufFree (Packet);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Process the ICMPv6 error packet, and deliver the packet to upper layer.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the ICMPv6 error packet.\r
+  @param[in]  Packet             The content of the ICMPv6 error with the IP head\r
+                                 removed.\r
+\r
+  @retval EFI_SUCCESS            The ICMPv6 error processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval Others                 Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessIcmpError (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_ERROR_HEAD       Icmp;\r
+\r
+  //\r
+  // Check the validity of the packet\r
+  //\r
+  if (Packet->TotalSize < sizeof (Icmp)) {\r
+    goto DROP;\r
+  }\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+  if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) {\r
+    return Ip6ProcessPacketTooBig (IpSb, Head, Packet);\r
+  }\r
+\r
+  //\r
+  // Notify the upper-layer process that an ICMPv6 eror message is received.\r
+  //\r
+  IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;\r
+  return Ip6Demultiplex (IpSb, Head, Packet);\r
+\r
+DROP:\r
+  NetbufFree (Packet);\r
+  Packet = NULL;\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+/**\r
+  Process the ICMPv6 informational messages. If it is an ICMPv6 echo\r
+  request, answer it. If it is a MLD message, trigger MLD routines to\r
+  process it. If it is a ND message, trigger ND routines to process it.\r
+  Otherwise, deliver it to upper layer.\r
+\r
+  @param[in]  IpSb               The IP service that receivd the packet.\r
+  @param[in]  Head               The IP head of the ICMPv6 informational packet.\r
+  @param[in]  Packet             The content of the ICMPv6 informational packet\r
+                                 with IP head removed.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval EFI_SUCCESS            The ICMPv6 informational message processed.\r
+  @retval Others                 Failed to process ICMPv6 informational message.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessIcmpInformation (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_INFORMATION_HEAD Icmp;\r
+  EFI_STATUS                Status;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);\r
+  ASSERT (Head != NULL);\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+  Status = EFI_INVALID_PARAMETER;\r
+\r
+  switch (Icmp.Head.Type) {\r
+  case ICMP_V6_ECHO_REQUEST:\r
+    //\r
+    // If ICMPv6 echo, reply it\r
+    //\r
+    if (Icmp.Head.Code == 0) {\r
+      Status = Ip6IcmpReplyEcho (IpSb, Head, Packet);\r
+    }\r
+    break;\r
+  case ICMP_V6_LISTENER_QUERY:\r
+    Status = Ip6ProcessMldQuery (IpSb, Head, Packet);\r
+    break;\r
+  case ICMP_V6_LISTENER_REPORT:\r
+  case ICMP_V6_LISTENER_REPORT_2:\r
+    Status = Ip6ProcessMldReport (IpSb, Head, Packet);\r
+    break;\r
+  case ICMP_V6_NEIGHBOR_SOLICIT:\r
+    Status = Ip6ProcessNeighborSolicit (IpSb, Head, Packet);\r
+    break;\r
+  case ICMP_V6_NEIGHBOR_ADVERTISE:\r
+    Status = Ip6ProcessNeighborAdvertise (IpSb, Head, Packet);\r
+    break;\r
+  case ICMP_V6_ROUTER_ADVERTISE:\r
+    Status = Ip6ProcessRouterAdvertise (IpSb, Head, Packet);\r
+    break;\r
+  case ICMP_V6_REDIRECT:\r
+    Status = Ip6ProcessRedirect (IpSb, Head, Packet);\r
+    break;\r
+  case ICMP_V6_ECHO_REPLY:\r
+    Status = Ip6Demultiplex (IpSb, Head, Packet);\r
+    break;\r
+  default:\r
+    Status = EFI_INVALID_PARAMETER;\r
+    break;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Handle the ICMPv6 packet. First validate the message format,\r
+  then, according to the message types, process it as an informational packet or\r
+  an error packet.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the ICMPv6 packet.\r
+  @param[in]  Packet             The content of the ICMPv6 packet with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet is malformated.\r
+  @retval EFI_SUCCESS            The ICMPv6 message successfully processed.\r
+  @retval Others                 Failed to handle the ICMPv6 packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IcmpHandle (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_HEAD             Icmp;\r
+  UINT16                    PseudoCheckSum;\r
+  UINT16                    CheckSum;\r
+\r
+  //\r
+  // Check the validity of the incoming packet.\r
+  //\r
+  if (Packet->TotalSize < sizeof (Icmp)) {\r
+    goto DROP;\r
+  }\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+\r
+  //\r
+  // Make sure checksum is valid.\r
+  //\r
+  PseudoCheckSum = NetIp6PseudoHeadChecksum (\r
+                     &Head->SourceAddress,\r
+                     &Head->DestinationAddress,\r
+                     IP6_ICMP,\r
+                     Packet->TotalSize\r
+                     );\r
+  CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet));\r
+  if (CheckSum != 0) {\r
+    goto DROP;\r
+  }\r
+\r
+  //\r
+  // According to the packet type, call corresponding process\r
+  //\r
+  if (Icmp.Type <= ICMP_V6_ERROR_MAX) {\r
+    return Ip6ProcessIcmpError (IpSb, Head, Packet);\r
+  } else {\r
+    return Ip6ProcessIcmpInformation (IpSb, Head, Packet);\r
+  }\r
+\r
+DROP:\r
+  NetbufFree (Packet);\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+/**\r
+  Retrieve the Prefix address according to the PrefixLength by clear the useless\r
+  bits.\r
+\r
+  @param[in]       PrefixLength  The prefix length of the prefix.\r
+  @param[in, out]  Prefix        On input, points to the original prefix address\r
+                                 with dirty bits; on output, points to the updated\r
+                                 address with useless bit clear.\r
+\r
+**/\r
+VOID\r
+Ip6GetPrefix (\r
+  IN     UINT8              PrefixLength,\r
+  IN OUT EFI_IPv6_ADDRESS   *Prefix\r
+  )\r
+{\r
+  UINT8                     Byte;\r
+  UINT8                     Bit;\r
+  UINT8                     Mask;\r
+  UINT8                     Value;\r
+\r
+  ASSERT ((Prefix != NULL) && (PrefixLength < IP6_PREFIX_NUM));\r
+\r
+  if (PrefixLength == 0) {\r
+    ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS));\r
+    return ;\r
+  }\r
+\r
+  if (PrefixLength == IP6_PREFIX_NUM - 1) {\r
+    return ;\r
+  }\r
+\r
+  Byte  = (UINT8) (PrefixLength / 8);\r
+  Bit   = (UINT8) (PrefixLength % 8);\r
+  Value = Prefix->Addr[Byte];\r
+\r
+  if ((Byte > 0) && (Byte < 16)) {\r
+    ZeroMem (Prefix->Addr + Byte, 16 - Byte);\r
+  }\r
+\r
+  if (Bit > 0) {\r
+    Mask = (UINT8) (0xFF << (8 - Bit));\r
+    Prefix->Addr[Byte] = (UINT8) (Value & Mask);\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Check whether the DestinationAddress is an anycast address.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  DestinationAddress Points to the Destination Address of the packet.\r
+\r
+  @retval TRUE                   The DestinationAddress is anycast address.\r
+  @retval FALSE                  The DestinationAddress is not anycast address.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsAnycast (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *DestinationAddress\r
+  )\r
+{\r
+  IP6_PREFIX_LIST_ENTRY     *PrefixEntry;\r
+  EFI_IPv6_ADDRESS          Prefix;\r
+  BOOLEAN                   Flag;\r
+\r
+  ZeroMem (&Prefix, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  Flag = FALSE;\r
+\r
+  //\r
+  // If the address is known as on-link or autonomous prefix, record it as\r
+  // anycast address.\r
+  //\r
+  do {\r
+    PrefixEntry = Ip6FindPrefixListEntry (IpSb, Flag, 255, DestinationAddress);\r
+    if (PrefixEntry != NULL) {\r
+      IP6_COPY_ADDRESS (&Prefix, &PrefixEntry->Prefix);\r
+      Ip6GetPrefix (PrefixEntry->PrefixLength, &Prefix);\r
+      if (EFI_IP6_EQUAL (&Prefix, DestinationAddress)) {\r
+        return TRUE;\r
+      }\r
+    }\r
+\r
+    Flag = (BOOLEAN) !Flag;\r
+  } while (Flag);\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Generate ICMPv6 error message and send it out to DestinationAddress. Currently\r
+  Destination Unreachable message, Time Exceeded message and Parameter Problem\r
+  message are supported.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Packet             The packet which invoking ICMPv6 error.\r
+  @param[in]  SourceAddress      If not NULL, points to the SourceAddress.\r
+                                 Otherwise, the IP layer will select a source address\r
+                                 according to the DestinationAddress.\r
+  @param[in]  DestinationAddress Points to the Destination Address of the ICMPv6\r
+                                 error message.\r
+  @param[in]  Type               The type of the ICMPv6 message.\r
+  @param[in]  Code               The additional level of the ICMPv6 message.\r
+  @param[in]  Pointer            If not NULL, identifies the octet offset within\r
+                                 the invoking packet where the error was detected.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet is malformated.\r
+  @retval EFI_OUT_OF_RESOURCES   There is no sufficient resource to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The ICMPv6 message was successfully sent out.\r
+  @retval Others                 Failed to generate the ICMPv6 packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendIcmpError (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_IPv6_ADDRESS       *SourceAddress       OPTIONAL,\r
+  IN EFI_IPv6_ADDRESS       *DestinationAddress,\r
+  IN UINT8                  Type,\r
+  IN UINT8                  Code,\r
+  IN UINT32                 *Pointer             OPTIONAL\r
+  )\r
+{\r
+  UINT32                    PacketLen;\r
+  NET_BUF                   *ErrorMsg;\r
+  UINT16                    PayloadLen;\r
+  EFI_IP6_HEADER            Head;\r
+  IP6_ICMP_INFORMATION_HEAD *IcmpHead;\r
+  UINT8                     *ErrorBody;\r
+\r
+  if (DestinationAddress == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // An ICMPv6 error message must not be originated as a result of receiving\r
+  // a packet whose source address does not uniquely identify a single node --\r
+  // e.g., the IPv6 Unspecified Address, an IPv6 multicast address, or an address\r
+  // known by the ICMP message originator to be an IPv6 anycast address.\r
+  //\r
+  if (NetIp6IsUnspecifiedAddr (DestinationAddress) ||\r
+      IP6_IS_MULTICAST (DestinationAddress)        ||\r
+      Ip6IsAnycast (IpSb, DestinationAddress)\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  switch (Type) {\r
+  case ICMP_V6_DEST_UNREACHABLE:\r
+  case ICMP_V6_TIME_EXCEEDED:\r
+    break;\r
+\r
+  case ICMP_V6_PARAMETER_PROBLEM:\r
+    if (Pointer == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    break;\r
+\r
+  default:\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  PacketLen = sizeof (IP6_ICMP_ERROR_HEAD) + Packet->TotalSize;\r
+\r
+  if (PacketLen > IpSb->MaxPacketSize) {\r
+    PacketLen = IpSb->MaxPacketSize;\r
+  }\r
+\r
+  ErrorMsg = NetbufAlloc (PacketLen);\r
+  if (ErrorMsg == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  PayloadLen = (UINT16) (PacketLen - sizeof (EFI_IP6_HEADER));\r
+\r
+  //\r
+  // Create the basic IPv6 header.\r
+  //\r
+  ZeroMem (&Head, sizeof (EFI_IP6_HEADER));\r
+\r
+  Head.PayloadLength  = HTONS (PayloadLen);\r
+  Head.NextHeader     = IP6_ICMP;\r
+  Head.HopLimit       = IpSb->CurHopLimit;\r
+\r
+  if (SourceAddress != NULL) {\r
+    IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);\r
+  } else {\r
+    ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
+  }\r
+\r
+  IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);\r
+\r
+  NetbufReserve (ErrorMsg, sizeof (EFI_IP6_HEADER));\r
+\r
+  //\r
+  // Fill in the ICMP error message head\r
+  //\r
+  IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (ErrorMsg, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);\r
+  if (IcmpHead == NULL) {\r
+    NetbufFree (ErrorMsg);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));\r
+  IcmpHead->Head.Type = Type;\r
+  IcmpHead->Head.Code = Code;\r
+\r
+  if (Pointer != NULL) {\r
+    IcmpHead->Fourth = HTONL (*Pointer);\r
+  }\r
+\r
+  //\r
+  // Fill in the ICMP error message body\r
+  //\r
+  PayloadLen -= sizeof (IP6_ICMP_INFORMATION_HEAD);\r
+  ErrorBody =  NetbufAllocSpace (ErrorMsg, PayloadLen, FALSE);\r
+  if (ErrorBody != NULL) {\r
+    ZeroMem (ErrorBody, PayloadLen);\r
+    NetbufCopy (Packet, 0, PayloadLen, ErrorBody);\r
+  }\r
+\r
+  //\r
+  // Transmit the packet\r
+  //\r
+  return Ip6Output (IpSb, NULL, NULL, ErrorMsg, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Icmp.h b/NetworkPkg/Ip6Dxe/Ip6Icmp.h
new file mode 100644 (file)
index 0000000..6852ae5
--- /dev/null
@@ -0,0 +1,108 @@
+/** @file\r
+  Header file for ICMPv6 protocol.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_ICMP_H__\r
+#define __EFI_IP6_ICMP_H__\r
+\r
+#define ICMP_V6_DEFAULT_CODE          0\r
+\r
+#define ICMP_V6_ERROR_MAX             127\r
+\r
+//\r
+// ICMPv6 message classes, each class of ICMPv6 message shares\r
+// a common message format. INVALID_MESSAGE is only a flag.\r
+//\r
+#define ICMP_V6_INVALID_MESSAGE       0\r
+#define ICMP_V6_ERROR_MESSAGE         1\r
+#define ICMP_V6_INFORMATION_MESSAGE   2\r
+\r
+\r
+extern EFI_IP6_ICMP_TYPE  mIp6SupportedIcmp[];\r
+\r
+/**\r
+  Handle the ICMPv6 packet. First validate the message format,\r
+  then, according to the message types, process it as an informational packet or\r
+  an error packet.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the ICMPv6 packet.\r
+  @param[in]  Packet             The content of the ICMPv6 packet with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet is malformated.\r
+  @retval EFI_SUCCESS            The ICMPv6 message successfully processed.\r
+  @retval Others                 Failed to handle the ICMPv6 packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IcmpHandle (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  );\r
+\r
+/**\r
+  Check whether the DestinationAddress is an anycast address.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  DestinationAddress Points to the Destination Address of the packet.\r
+\r
+  @retval TRUE                   The DestinationAddress is anycast address.\r
+  @retval FALSE                  The DestinationAddress is not anycast address.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsAnycast (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *DestinationAddress\r
+  );\r
+\r
+/**\r
+  Generate ICMPv6 error message and send it out to DestinationAddress. Currently\r
+  Destination Unreachable message, Time Exceeded message and Parameter Problem\r
+  message are supported.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Packet             The packet which invoking ICMPv6 error.\r
+  @param[in]  SourceAddress      If not NULL, points to the SourceAddress.\r
+                                 Otherwise, the IP layer will select a source address\r
+                                 according to the DestinationAddress.\r
+  @param[in]  DestinationAddress Points to the Destination Address of the ICMPv6\r
+                                 error message.\r
+  @param[in]  Type               The type of the ICMPv6 message.\r
+  @param[in]  Code               The additional level of the ICMPv6 message.\r
+  @param[in]  Pointer            If not NULL, identifies the octet offset within\r
+                                 the invoking packet where the error was detected.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The packet is malformated.\r
+  @retval EFI_OUT_OF_RESOURCES   There is no sufficient resource to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The ICMPv6 message was successfully sent out.\r
+  @retval Others                 Failed to generate the ICMPv6 packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendIcmpError (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_IPv6_ADDRESS       *SourceAddress       OPTIONAL,\r
+  IN EFI_IPv6_ADDRESS       *DestinationAddress,\r
+  IN UINT8                  Type,\r
+  IN UINT8                  Code,\r
+  IN UINT32                 *Pointer             OPTIONAL\r
+  );\r
+\r
+#endif\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6If.c b/NetworkPkg/Ip6Dxe/Ip6If.c
new file mode 100644 (file)
index 0000000..198b547
--- /dev/null
@@ -0,0 +1,802 @@
+/** @file\r
+  Implement IP6 pesudo interface.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+  Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.\r
+\r
+  @param[in]  Event              The transmit token's event.\r
+  @param[in]  Context            The Context which is pointed to the token.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameSent (\r
+  IN EFI_EVENT               Event,\r
+  IN VOID                    *Context\r
+  );\r
+\r
+/**\r
+  Fileter function to cancel all the frame related to an IP instance.\r
+\r
+  @param[in]  Frame             The transmit request to test whether to cancel.\r
+  @param[in]  Context           The context which is the Ip instance that issued\r
+                                the transmit.\r
+\r
+  @retval TRUE                  The frame belongs to this instance and is to be\r
+                                removed.\r
+  @retval FALSE                 The frame doesn't belong to this instance.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6CancelInstanceFrame (\r
+  IN IP6_LINK_TX_TOKEN *Frame,\r
+  IN VOID              *Context\r
+  )\r
+{\r
+  if (Frame->IpInstance == (IP6_PROTOCOL *) Context) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Set the interface's address. This will trigger the DAD process for the\r
+  address to set. To set an already set address, the lifetimes wil be\r
+  updated to the new value passed in.\r
+\r
+  @param[in]  Interface             The interface to set the address.\r
+  @param[in]  Ip6Addr               The interface's to be assigned IPv6 address.\r
+  @param[in]  IsAnycast             If TRUE, the unicast IPv6 address is anycast.\r
+                                    Otherwise, it is not anycast.\r
+  @param[in]  PrefixLength          The prefix length of the Ip6Addr.\r
+  @param[in]  ValidLifetime         The valid lifetime for this address.\r
+  @param[in]  PreferredLifetime     The preferred lifetime for this address.\r
+  @param[in]  DadCallback           The caller's callback to trigger when DAD finishes.\r
+                                    This is an optional parameter that may be NULL.\r
+  @param[in]  Context               The context that will be passed to DadCallback.\r
+                                    This is an optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS               The interface is scheduled to be configured with\r
+                                    the specified address.\r
+  @retval EFI_OUT_OF_RESOURCES      Failed to set the interface's address due to\r
+                                    lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetAddress (\r
+  IN IP6_INTERFACE          *Interface,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Addr,\r
+  IN BOOLEAN                IsAnycast,\r
+  IN UINT8                  PrefixLength,\r
+  IN UINT32                 ValidLifetime,\r
+  IN UINT32                 PreferredLifetime,\r
+  IN IP6_DAD_CALLBACK       DadCallback  OPTIONAL,\r
+  IN VOID                   *Context     OPTIONAL\r
+  )\r
+{\r
+  IP6_SERVICE            *IpSb;\r
+  IP6_ADDRESS_INFO       *AddressInfo;\r
+  LIST_ENTRY             *Entry;\r
+  IP6_PREFIX_LIST_ENTRY  *PrefixEntry;\r
+  UINT64                 Delay;\r
+  IP6_DELAY_JOIN_LIST    *DelayNode;\r
+\r
+  NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);\r
+\r
+  IpSb = Interface->Service;\r
+\r
+  if (Ip6IsOneOfSetAddress (IpSb, Ip6Addr, NULL, &AddressInfo)) {\r
+    ASSERT (AddressInfo != NULL);\r
+    //\r
+    // Update the lifetime.\r
+    //\r
+    AddressInfo->ValidLifetime     = ValidLifetime;\r
+    AddressInfo->PreferredLifetime = PreferredLifetime;\r
+\r
+    if (DadCallback != NULL) {\r
+      DadCallback (TRUE, Ip6Addr, Context);\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  AddressInfo = (IP6_ADDRESS_INFO *) AllocatePool (sizeof (IP6_ADDRESS_INFO));\r
+  if (AddressInfo == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  AddressInfo->Signature         = IP6_ADDR_INFO_SIGNATURE;\r
+  IP6_COPY_ADDRESS (&AddressInfo->Address, Ip6Addr);\r
+  AddressInfo->IsAnycast         = IsAnycast;\r
+  AddressInfo->PrefixLength      = PrefixLength;\r
+  AddressInfo->ValidLifetime     = ValidLifetime;\r
+  AddressInfo->PreferredLifetime = PreferredLifetime;\r
+\r
+  if (AddressInfo->PrefixLength == 0) {\r
+    //\r
+    // Find an appropriate prefix from on-link prefixes and update the prefixlength.\r
+    // Longest prefix match is used here.\r
+    //\r
+    NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {\r
+      PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+\r
+      if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {\r
+        AddressInfo->PrefixLength = PrefixEntry->PrefixLength;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (AddressInfo->PrefixLength == 0) {\r
+    //\r
+    // If the prefix length is still zero, try the autonomous prefixes.\r
+    // Longest prefix match is used here.\r
+    //\r
+    NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {\r
+      PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+\r
+      if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {\r
+        AddressInfo->PrefixLength = PrefixEntry->PrefixLength;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (AddressInfo->PrefixLength == 0) {\r
+    //\r
+    // BUGBUG: Stil fail, use 64 as the default prefix length.\r
+    //\r
+    AddressInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;\r
+  }\r
+\r
+\r
+  //\r
+  // Node should delay joining the solicited-node mulitcast address by a random delay\r
+  // between 0 and MAX_RTR_SOLICITATION_DELAY (1 second).\r
+  // Thus queue the address to be processed in Duplicate Address Detection module\r
+  // after the delay time (in milliseconds).\r
+  //\r
+  Delay = (UINT64) NET_RANDOM (NetRandomInitSeed ());\r
+  Delay = MultU64x32 (Delay, IP6_ONE_SECOND_IN_MS);\r
+  Delay = RShiftU64 (Delay, 32);\r
+\r
+  DelayNode = (IP6_DELAY_JOIN_LIST *) AllocatePool (sizeof (IP6_DELAY_JOIN_LIST));\r
+  if (DelayNode == NULL) {\r
+    FreePool (AddressInfo);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  DelayNode->DelayTime   = (UINT32) (DivU64x32 (Delay, IP6_TIMER_INTERVAL_IN_MS));\r
+  DelayNode->Interface   = Interface;\r
+  DelayNode->AddressInfo = AddressInfo;\r
+  DelayNode->DadCallback = DadCallback;\r
+  DelayNode->Context     = Context;\r
+\r
+  InsertTailList (&Interface->DelayJoinList, &DelayNode->Link);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Create an IP6_INTERFACE.\r
+\r
+  @param[in]  IpSb                  The IP6 service binding instance.\r
+  @param[in]  LinkLocal             If TRUE, the instance is created for link-local address.\r
+                                    Otherwise, it is not for a link-local address.\r
+\r
+  @return Point to the created IP6_INTERFACE, otherwise NULL.\r
+\r
+**/\r
+IP6_INTERFACE *\r
+Ip6CreateInterface (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN BOOLEAN                LinkLocal\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  IP6_INTERFACE             *Interface;\r
+  EFI_IPv6_ADDRESS          *Ip6Addr;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  Interface = AllocatePool (sizeof (IP6_INTERFACE));\r
+  if (Interface == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Interface->Signature        = IP6_INTERFACE_SIGNATURE;\r
+  Interface->RefCnt           = 1;\r
+\r
+  InitializeListHead (&Interface->AddressList);\r
+  Interface->AddressCount     = 0;\r
+  Interface->Configured       = FALSE;\r
+\r
+  Interface->Service          = IpSb;\r
+  Interface->Controller       = IpSb->Controller;\r
+  Interface->Image            = IpSb->Image;\r
+\r
+  InitializeListHead (&Interface->ArpQues);\r
+  InitializeListHead (&Interface->SentFrames);\r
+\r
+  Interface->DupAddrDetect    = IpSb->Ip6ConfigInstance.DadXmits.DupAddrDetectTransmits;\r
+  InitializeListHead (&Interface->DupAddrDetectList);\r
+\r
+  InitializeListHead (&Interface->DelayJoinList);\r
+\r
+  InitializeListHead (&Interface->IpInstances);\r
+  Interface->PromiscRecv      = FALSE;\r
+\r
+  if (!LinkLocal) {\r
+    return Interface;\r
+  }\r
+\r
+  //\r
+  // Get the link local addr\r
+  //\r
+  Ip6Addr = Ip6CreateLinkLocalAddr (IpSb);\r
+  if (Ip6Addr == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Perform DAD - Duplicate Address Detection.\r
+  //\r
+  Status = Ip6SetAddress (\r
+             Interface,\r
+             Ip6Addr,\r
+             FALSE,\r
+             IP6_LINK_LOCAL_PREFIX_LENGTH,\r
+             (UINT32) IP6_INFINIT_LIFETIME,\r
+             (UINT32) IP6_INFINIT_LIFETIME,\r
+             NULL,\r
+             NULL\r
+             );\r
+\r
+  FreePool (Ip6Addr);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return Interface;\r
+\r
+ON_ERROR:\r
+\r
+  FreePool (Interface);\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Free the interface used by IpInstance. All the IP instance with\r
+  the same Ip/prefix pair share the same interface. It is reference\r
+  counted. All the frames that haven't been sent will be cancelled.\r
+  Because the IpInstance is optional, the caller must remove\r
+  IpInstance from the interface's instance list.\r
+\r
+  @param[in]  Interface         The interface used by the IpInstance.\r
+  @param[in]  IpInstance        The IP instance that free the interface. NULL if\r
+                                the IP driver is releasing the default interface.\r
+\r
+**/\r
+VOID\r
+Ip6CleanInterface (\r
+  IN  IP6_INTERFACE         *Interface,\r
+  IN  IP6_PROTOCOL          *IpInstance           OPTIONAL\r
+  )\r
+{\r
+  IP6_DAD_ENTRY             *Duplicate;\r
+  IP6_DELAY_JOIN_LIST       *Delay;\r
+\r
+  NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);\r
+  ASSERT (Interface->RefCnt > 0);\r
+\r
+  //\r
+  // Remove all the pending transmit token related to this IP instance.\r
+  //\r
+  Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, IpInstance);\r
+\r
+  if (--Interface->RefCnt > 0) {\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Destory the interface if this is the last IP instance.\r
+  // Remove all the system transmitted packets\r
+  // from this interface, cancel the receive request if exists.\r
+  //\r
+  Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, NULL);\r
+\r
+  ASSERT (IsListEmpty (&Interface->IpInstances));\r
+  ASSERT (IsListEmpty (&Interface->ArpQues));\r
+  ASSERT (IsListEmpty (&Interface->SentFrames));\r
+\r
+  while (!IsListEmpty (&Interface->DupAddrDetectList)) {\r
+    Duplicate = NET_LIST_HEAD (&Interface->DupAddrDetectList, IP6_DAD_ENTRY, Link);\r
+    NetListRemoveHead (&Interface->DupAddrDetectList);\r
+    FreePool (Duplicate);\r
+  }\r
+\r
+  while (!IsListEmpty (&Interface->DelayJoinList)) {\r
+    Delay = NET_LIST_HEAD (&Interface->DelayJoinList, IP6_DELAY_JOIN_LIST, Link);\r
+    NetListRemoveHead (&Interface->DelayJoinList);\r
+    FreePool (Delay);\r
+  }\r
+\r
+  Ip6RemoveAddr (Interface->Service, &Interface->AddressList, &Interface->AddressCount, NULL, 0);\r
+\r
+  RemoveEntryList (&Interface->Link);\r
+  FreePool (Interface);\r
+}\r
+\r
+/**\r
+  Create and wrap a transmit request into a newly allocated IP6_LINK_TX_TOKEN.\r
+\r
+  @param[in]  Interface         The interface to send out from.\r
+  @param[in]  IpInstance        The IpInstance that transmit the packet.  NULL if\r
+                                the packet is sent by the IP6 driver itself.\r
+  @param[in]  Packet            The packet to transmit\r
+  @param[in]  CallBack          Call back function to execute if transmission\r
+                                finished.\r
+  @param[in]  Context           Opaque parameter to the callback.\r
+\r
+  @return The wrapped token if succeed or NULL.\r
+\r
+**/\r
+IP6_LINK_TX_TOKEN *\r
+Ip6CreateLinkTxToken (\r
+  IN IP6_INTERFACE          *Interface,\r
+  IN IP6_PROTOCOL           *IpInstance    OPTIONAL,\r
+  IN NET_BUF                *Packet,\r
+  IN IP6_FRAME_CALLBACK     CallBack,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;\r
+  EFI_MANAGED_NETWORK_TRANSMIT_DATA     *MnpTxData;\r
+  IP6_LINK_TX_TOKEN                     *Token;\r
+  EFI_STATUS                            Status;\r
+  UINT32                                Count;\r
+\r
+  Token = AllocatePool (sizeof (IP6_LINK_TX_TOKEN) + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));\r
+\r
+  if (Token == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Token->Signature = IP6_LINK_TX_SIGNATURE;\r
+  InitializeListHead (&Token->Link);\r
+\r
+  Token->IpInstance = IpInstance;\r
+  Token->CallBack   = CallBack;\r
+  Token->Packet     = Packet;\r
+  Token->Context    = Context;\r
+  ZeroMem (&Token->DstMac, sizeof (EFI_MAC_ADDRESS));\r
+  IP6_COPY_LINK_ADDRESS (&Token->SrcMac, &Interface->Service->SnpMode.CurrentAddress);\r
+\r
+  MnpToken          = &(Token->MnpToken);\r
+  MnpToken->Status  = EFI_NOT_READY;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  Ip6OnFrameSent,\r
+                  Token,\r
+                  &MnpToken->Event\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Token);\r
+    return NULL;\r
+  }\r
+\r
+  MnpTxData                     = &Token->MnpTxData;\r
+  MnpToken->Packet.TxData       = MnpTxData;\r
+\r
+  MnpTxData->DestinationAddress = &Token->DstMac;\r
+  MnpTxData->SourceAddress      = &Token->SrcMac;\r
+  MnpTxData->ProtocolType       = IP6_ETHER_PROTO;\r
+  MnpTxData->DataLength         = Packet->TotalSize;\r
+  MnpTxData->HeaderLength       = 0;\r
+\r
+  Count                         = Packet->BlockOpNum;\r
+\r
+  NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);\r
+  MnpTxData->FragmentCount      = (UINT16)Count;\r
+\r
+  return Token;\r
+}\r
+\r
+/**\r
+  Free the link layer transmit token. It will close the event,\r
+  then free the memory used.\r
+\r
+  @param[in]  Token                 Token to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeLinkTxToken (\r
+  IN IP6_LINK_TX_TOKEN      *Token\r
+  )\r
+{\r
+  NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);\r
+\r
+  gBS->CloseEvent (Token->MnpToken.Event);\r
+  FreePool (Token);\r
+}\r
+\r
+/**\r
+  Callback function when the received packet is freed.\r
+  Check Ip6OnFrameReceived for information.\r
+\r
+  @param[in]  Context       Points to EFI_MANAGED_NETWORK_RECEIVE_DATA.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6RecycleFrame (\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_MANAGED_NETWORK_RECEIVE_DATA  *RxData;\r
+\r
+  RxData = (EFI_MANAGED_NETWORK_RECEIVE_DATA *) Context;\r
+\r
+  gBS->SignalEvent (RxData->RecycleEvent);\r
+}\r
+\r
+/**\r
+  Received a frame from MNP. Wrap it in net buffer then deliver\r
+  it to IP's input function. The ownship of the packet also\r
+  is transferred to IP. When Ip is finished with this packet, it\r
+  will call NetbufFree to release the packet, NetbufFree will\r
+  again call the Ip6RecycleFrame to signal MNP's event and free\r
+  the token used.\r
+\r
+  @param[in]  Context         Context for the callback.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameReceivedDpc (\r
+  IN VOID                     *Context\r
+  )\r
+{\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;\r
+  EFI_MANAGED_NETWORK_RECEIVE_DATA      *MnpRxData;\r
+  IP6_LINK_RX_TOKEN                     *Token;\r
+  NET_FRAGMENT                          Netfrag;\r
+  NET_BUF                               *Packet;\r
+  UINT32                                Flag;\r
+  IP6_SERVICE                           *IpSb;\r
+\r
+  Token = (IP6_LINK_RX_TOKEN *) Context;\r
+  NET_CHECK_SIGNATURE (Token, IP6_LINK_RX_SIGNATURE);\r
+\r
+  //\r
+  // First clear the interface's receive request in case the\r
+  // caller wants to call Ip6ReceiveFrame in the callback.\r
+  //\r
+  IpSb = (IP6_SERVICE *) Token->Context;\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+\r
+  MnpToken  = &Token->MnpToken;\r
+  MnpRxData = MnpToken->Packet.RxData;\r
+\r
+  if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {\r
+    Token->CallBack (NULL, MnpToken->Status, 0, Token->Context);\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Wrap the frame in a net buffer then deliever it to IP input.\r
+  // IP will reassemble the packet, and deliver it to upper layer\r
+  //\r
+  Netfrag.Len  = MnpRxData->DataLength;\r
+  Netfrag.Bulk = MnpRxData->PacketData;\r
+\r
+  Packet = NetbufFromExt (&Netfrag, 1, IP6_MAX_HEADLEN, 0, Ip6RecycleFrame, Token->MnpToken.Packet.RxData);\r
+\r
+  if (Packet == NULL) {\r
+    gBS->SignalEvent (MnpRxData->RecycleEvent);\r
+\r
+    Token->CallBack (NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);\r
+\r
+    return ;\r
+  }\r
+\r
+  Flag  = (MnpRxData->BroadcastFlag ? IP6_LINK_BROADCAST : 0);\r
+  Flag |= (MnpRxData->MulticastFlag ? IP6_LINK_MULTICAST : 0);\r
+  Flag |= (MnpRxData->PromiscuousFlag ? IP6_LINK_PROMISC : 0);\r
+\r
+  Token->CallBack (Packet, EFI_SUCCESS, Flag, Token->Context);\r
+}\r
+\r
+/**\r
+  Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK.\r
+\r
+  @param  Event                 The receive event delivered to MNP for receive.\r
+  @param  Context               Context for the callback.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameReceived (\r
+  IN EFI_EVENT                Event,\r
+  IN VOID                     *Context\r
+  )\r
+{\r
+  //\r
+  // Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK\r
+  //\r
+  QueueDpc (TPL_CALLBACK, Ip6OnFrameReceivedDpc, Context);\r
+}\r
+\r
+/**\r
+  Request to receive the packet from the interface.\r
+\r
+  @param[in]  CallBack          Function to call when receive finished.\r
+  @param[in]  IpSb              Points to IP6 service binding instance.\r
+\r
+  @retval EFI_ALREADY_STARTED   There is already a pending receive request.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to receive.\r
+  @retval EFI_SUCCESS           The recieve request has been started.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ReceiveFrame (\r
+  IN  IP6_FRAME_CALLBACK    CallBack,\r
+  IN  IP6_SERVICE           *IpSb\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  IP6_LINK_RX_TOKEN         *Token;\r
+\r
+  if (IpSb->InDestroy) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  Token           = &IpSb->RecvRequest;\r
+  Token->CallBack = CallBack;\r
+  Token->Context  = (VOID *) IpSb;\r
+\r
+  Status = IpSb->Mnp->Receive (IpSb->Mnp, &Token->MnpToken);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Callback funtion when frame transmission is finished. It will\r
+  call the frame owner's callback function to tell it the result.\r
+\r
+  @param[in]  Context        Context which points to the token.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameSentDpc (\r
+  IN VOID                    *Context\r
+  )\r
+{\r
+  IP6_LINK_TX_TOKEN         *Token;\r
+\r
+  Token = (IP6_LINK_TX_TOKEN *) Context;\r
+  NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);\r
+\r
+  RemoveEntryList (&Token->Link);\r
+\r
+  Token->CallBack (\r
+          Token->Packet,\r
+          Token->MnpToken.Status,\r
+          0,\r
+          Token->Context\r
+          );\r
+\r
+  Ip6FreeLinkTxToken (Token);\r
+}\r
+\r
+/**\r
+  Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.\r
+\r
+  @param[in]  Event                 The transmit token's event.\r
+  @param[in]  Context               Context which points to the token.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameSent (\r
+  IN EFI_EVENT               Event,\r
+  IN VOID                    *Context\r
+  )\r
+{\r
+  //\r
+  // Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK\r
+  //\r
+  QueueDpc (TPL_CALLBACK, Ip6OnFrameSentDpc, Context);\r
+}\r
+\r
+/**\r
+  Send a frame from the interface. If the next hop is a multicast address,\r
+  it is transmitted immediately. If the next hop is a unicast,\r
+  and the NextHop's MAC is not known, it will perform address resolution.\r
+  If an error occurred, the CallBack won't be called. So, the caller\r
+  must test the return value, and take action when there is an error.\r
+\r
+  @param[in]  Interface         The interface to send the frame from\r
+  @param[in]  IpInstance        The IP child that request the transmission.\r
+                                NULL if it is the IP6 driver itself.\r
+  @param[in]  Packet            The packet to transmit.\r
+  @param[in]  NextHop           The immediate destination to transmit the packet to.\r
+  @param[in]  CallBack          Function to call back when transmit finished.\r
+  @param[in]  Context           Opaque parameter to the callback.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to send the frame.\r
+  @retval EFI_NO_MAPPING        Can't resolve the MAC for the nexthop.\r
+  @retval EFI_SUCCESS           The packet successfully transmitted.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendFrame (\r
+  IN  IP6_INTERFACE         *Interface,\r
+  IN  IP6_PROTOCOL          *IpInstance      OPTIONAL,\r
+  IN  NET_BUF               *Packet,\r
+  IN  EFI_IPv6_ADDRESS      *NextHop,\r
+  IN  IP6_FRAME_CALLBACK    CallBack,\r
+  IN  VOID                  *Context\r
+  )\r
+{\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_LINK_TX_TOKEN         *Token;\r
+  EFI_STATUS                Status;\r
+  IP6_NEIGHBOR_ENTRY        *NeighborCache;\r
+  LIST_ENTRY                *Entry;\r
+  IP6_NEIGHBOR_ENTRY        *ArpQue;\r
+\r
+  IpSb = Interface->Service;\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  //\r
+  // Only when link local address is performing DAD, the interface could be used in unconfigured.\r
+  //\r
+  if (IpSb->LinkLocalOk) {\r
+    ASSERT (Interface->Configured);\r
+  }\r
+\r
+  Token = Ip6CreateLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);\r
+\r
+  if (Token == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  if (IP6_IS_MULTICAST (NextHop)) {\r
+    Status = Ip6GetMulticastMac (IpSb->Mnp, NextHop, &Token->DstMac);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+\r
+    goto SendNow;\r
+  }\r
+\r
+  //\r
+  // If send to itself, directly send out\r
+  //\r
+  if (EFI_IP6_EQUAL (&Packet->Ip.Ip6->DestinationAddress, &Packet->Ip.Ip6->SourceAddress)) {\r
+    IP6_COPY_LINK_ADDRESS (&Token->DstMac, &IpSb->SnpMode.CurrentAddress);\r
+    goto SendNow;\r
+  }\r
+\r
+  //\r
+  // If unicast, check the neighbor state.\r
+  //\r
+\r
+  NeighborCache = Ip6FindNeighborEntry (IpSb, NextHop);\r
+  ASSERT (NeighborCache != NULL);\r
+\r
+  if (NeighborCache->Interface == NULL) {\r
+    NeighborCache->Interface = Interface;\r
+  }\r
+\r
+  switch (NeighborCache->State) {\r
+  case EfiNeighborStale:\r
+    NeighborCache->State = EfiNeighborDelay;\r
+    NeighborCache->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);\r
+    //\r
+    // Fall through\r
+    //\r
+  case EfiNeighborReachable:\r
+  case EfiNeighborDelay:\r
+  case EfiNeighborProbe:\r
+    IP6_COPY_LINK_ADDRESS (&Token->DstMac, &NeighborCache->LinkAddress);\r
+    goto SendNow;\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  //\r
+  // Have to do asynchronous ARP resolution. First check whether there is\r
+  // already a pending request.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {\r
+    ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);\r
+    if (ArpQue == NeighborCache) {\r
+      InsertTailList (&NeighborCache->Frames, &Token->Link);\r
+      NeighborCache->ArpFree = TRUE;\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  //\r
+  // First frame requires ARP.\r
+  //\r
+  InsertTailList (&NeighborCache->Frames, &Token->Link);\r
+  InsertTailList (&Interface->ArpQues, &NeighborCache->ArpList);\r
+\r
+  NeighborCache->ArpFree = TRUE;\r
+\r
+  return EFI_SUCCESS;\r
+\r
+SendNow:\r
+ //\r
+  // Insert the tx token into the SentFrames list before calling Mnp->Transmit.\r
+  // Remove it if the returned status is not EFI_SUCCESS.\r
+  //\r
+  InsertTailList (&Interface->SentFrames, &Token->Link);\r
+  Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);\r
+  if (EFI_ERROR (Status)) {\r
+    RemoveEntryList (&Token->Link);\r
+    goto Error;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+Error:\r
+  Ip6FreeLinkTxToken (Token);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The heartbeat timer of IP6 service instance. It times out\r
+  all of its IP6 children's received-but-not-delivered and\r
+  transmitted-but-not-recycle packets.\r
+\r
+  @param[in]  Event                 The IP6 service instance's heartbeat timer.\r
+  @param[in]  Context               The IP6 service instance.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6TimerTicking (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP6_SERVICE               *IpSb;\r
+\r
+  IpSb = (IP6_SERVICE *) Context;\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  Ip6PacketTimerTicking (IpSb);\r
+  Ip6NdTimerTicking (IpSb);\r
+  Ip6MldTimerTicking (IpSb);\r
+}\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6If.h b/NetworkPkg/Ip6Dxe/Ip6If.h
new file mode 100644 (file)
index 0000000..736035e
--- /dev/null
@@ -0,0 +1,267 @@
+/** @file\r
+  Definition for IP6 pesudo interface structure.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_IF_H__\r
+#define __EFI_IP6_IF_H__\r
+\r
+#define IP6_LINK_RX_SIGNATURE   SIGNATURE_32 ('I', 'P', '6', 'R')\r
+#define IP6_LINK_TX_SIGNATURE   SIGNATURE_32 ('I', 'P', '6', 'T')\r
+#define IP6_INTERFACE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'I')\r
+#define IP6_ADDR_INFO_SIGNATURE SIGNATURE_32 ('I', 'P', 'A', 'I')\r
+\r
+//\r
+// This prototype is used by both receive and transmission.\r
+// When receiving Netbuf is allocated by IP6_INTERFACE, and\r
+// released by IP6. Flag shows whether the frame is received\r
+// as unicast/multicast/anycast...\r
+//\r
+// When transmitting, the Netbuf is from IP6, and provided\r
+// to the callback as a reference. Flag isn't used.\r
+//\r
+// IpInstance can be NULL which means that it is the IP6 driver\r
+// itself sending the packets. IP6 driver may send packets that\r
+// don't belong to any instance, such as ICMP errors, ICMP\r
+// informational packets. IpInstance is used as a tag in\r
+// this module.\r
+//\r
+typedef\r
+VOID\r
+(*IP6_FRAME_CALLBACK) (\r
+  NET_BUF                   *Packet,\r
+  EFI_STATUS                IoStatus,\r
+  UINT32                    LinkFlag,\r
+  VOID                      *Context\r
+  );\r
+\r
+//\r
+// Each receive request is wrapped in an IP6_LINK_RX_TOKEN.\r
+// Upon completion, the Callback will be called. Only one\r
+// receive request is send to MNP. IpInstance is always NULL.\r
+// Reference MNP's spec for information.\r
+//\r
+typedef struct {\r
+  UINT32                                Signature;\r
+  IP6_FRAME_CALLBACK                    CallBack;\r
+  VOID                                  *Context;\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  MnpToken;\r
+} IP6_LINK_RX_TOKEN;\r
+\r
+//\r
+// Each transmit request is wrapped in an IP6_LINK_TX_TOKEN.\r
+// Upon completion, the Callback will be called.\r
+//\r
+typedef struct {\r
+  UINT32                                Signature;\r
+  LIST_ENTRY                            Link;\r
+\r
+  IP6_PROTOCOL                          *IpInstance;\r
+  IP6_FRAME_CALLBACK                    CallBack;\r
+  NET_BUF                               *Packet;\r
+  VOID                                  *Context;\r
+\r
+  EFI_MAC_ADDRESS                       DstMac;\r
+  EFI_MAC_ADDRESS                       SrcMac;\r
+\r
+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  MnpToken;\r
+  EFI_MANAGED_NETWORK_TRANSMIT_DATA     MnpTxData;\r
+} IP6_LINK_TX_TOKEN;\r
+\r
+struct _IP6_ADDRESS_INFO {\r
+  UINT32                  Signature;\r
+  LIST_ENTRY              Link;\r
+  EFI_IPv6_ADDRESS        Address;\r
+  BOOLEAN                 IsAnycast;\r
+  UINT8                   PrefixLength;\r
+  UINT32                  ValidLifetime;\r
+  UINT32                  PreferredLifetime;\r
+};\r
+\r
+//\r
+// Callback to select which frame to cancel. Caller can cancel a\r
+// single frame, or all the frame from an IP instance.\r
+//\r
+typedef\r
+BOOLEAN\r
+(*IP6_FRAME_TO_CANCEL) (\r
+  IP6_LINK_TX_TOKEN       *Frame,\r
+  VOID                    *Context\r
+  );\r
+\r
+struct _IP6_INTERFACE {\r
+  UINT32                        Signature;\r
+  LIST_ENTRY                    Link;\r
+  INTN                          RefCnt;\r
+\r
+  //\r
+  // IP address and prefix length of the interface.  The fileds\r
+  // are invalid if (Configured == FALSE)\r
+  //\r
+  LIST_ENTRY                    AddressList;\r
+  UINT32                        AddressCount;\r
+  BOOLEAN                       Configured;\r
+\r
+  IP6_SERVICE                   *Service;\r
+\r
+  EFI_HANDLE                    Controller;\r
+  EFI_HANDLE                    Image;\r
+\r
+\r
+  //\r
+  // Queues to keep the frames sent and waiting ARP request.\r
+  //\r
+  LIST_ENTRY                    ArpQues;\r
+  LIST_ENTRY                    SentFrames;\r
+\r
+\r
+  //\r
+  // The interface's configuration variables\r
+  //\r
+  UINT32                        DupAddrDetect;\r
+  LIST_ENTRY                    DupAddrDetectList;\r
+  LIST_ENTRY                    DelayJoinList;\r
+\r
+  //\r
+  // All the IP instances that have the same IP/SubnetMask are linked\r
+  // together through IpInstances. If any of the instance enables\r
+  // promiscuous receive, PromiscRecv is true.\r
+  //\r
+  LIST_ENTRY                    IpInstances;\r
+  BOOLEAN                       PromiscRecv;\r
+};\r
+\r
+/**\r
+  Create an IP6_INTERFACE.\r
+\r
+  @param[in]  IpSb                  The IP6 service binding instance.\r
+  @param[in]  LinkLocal             If TRUE, the instance is created for link-local address.\r
+                                    Otherwise, it is not for a link-local address.\r
+\r
+  @return Point to the created IP6_INTERFACE, otherwise NULL.\r
+\r
+**/\r
+IP6_INTERFACE *\r
+Ip6CreateInterface (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN BOOLEAN                LinkLocal\r
+  );\r
+\r
+/**\r
+  Free the interface used by IpInstance. All the IP instance with\r
+  the same Ip/prefix pair share the same interface. It is reference\r
+  counted. All the frames that haven't been sent will be cancelled.\r
+  Because the IpInstance is optional, the caller must remove\r
+  IpInstance from the interface's instance list.\r
+\r
+  @param[in]  Interface         The interface used by the IpInstance.\r
+  @param[in]  IpInstance        The IP instance that free the interface. NULL if\r
+                                the IP driver is releasing the default interface.\r
+\r
+**/\r
+VOID\r
+Ip6CleanInterface (\r
+  IN  IP6_INTERFACE         *Interface,\r
+  IN  IP6_PROTOCOL          *IpInstance           OPTIONAL\r
+  );\r
+\r
+/**\r
+  Free the link layer transmit token. It will close the event\r
+  then free the memory used.\r
+\r
+  @param[in]  Token                 Token to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeLinkTxToken (\r
+  IN IP6_LINK_TX_TOKEN      *Token\r
+  );\r
+\r
+/**\r
+  Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK\r
+\r
+  @param  Event                 The receive event delivered to MNP for receive.\r
+  @param  Context               Context for the callback.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameReceived (\r
+  IN EFI_EVENT                Event,\r
+  IN VOID                     *Context\r
+  );\r
+\r
+/**\r
+  Request to receive the packet from the interface.\r
+\r
+  @param[in]  CallBack          Function to call when the receive finished.\r
+  @param[in]  IpSb              Points to the IP6 service binding instance.\r
+\r
+  @retval EFI_ALREADY_STARTED   There is already a pending receive request.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to receive.\r
+  @retval EFI_SUCCESS           The recieve request has been started.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ReceiveFrame (\r
+  IN  IP6_FRAME_CALLBACK    CallBack,\r
+  IN  IP6_SERVICE           *IpSb\r
+  );\r
+\r
+/**\r
+  Send a frame from the interface. If the next hop is multicast address,\r
+  it is transmitted immediately. If the next hop is a unicast,\r
+  and the NextHop's MAC is not known, it will perform address resolution.\r
+  If some error happened, the CallBack won't be called. So, the caller\r
+  must test the return value, and take action when there is an error.\r
+\r
+  @param[in]  Interface         The interface to send the frame from\r
+  @param[in]  IpInstance        The IP child that request the transmission.\r
+                                NULL if it is the IP6 driver itself.\r
+  @param[in]  Packet            The packet to transmit.\r
+  @param[in]  NextHop           The immediate destination to transmit the packet to.\r
+  @param[in]  CallBack          Function to call back when transmit finished.\r
+  @param[in]  Context           Opaque parameter to the call back.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to send the frame.\r
+  @retval EFI_NO_MAPPING        Can't resolve the MAC for the nexthop.\r
+  @retval EFI_SUCCESS           The packet successfully transmitted.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendFrame (\r
+  IN  IP6_INTERFACE         *Interface,\r
+  IN  IP6_PROTOCOL          *IpInstance      OPTIONAL,\r
+  IN  NET_BUF               *Packet,\r
+  IN  EFI_IPv6_ADDRESS      *NextHop,\r
+  IN  IP6_FRAME_CALLBACK    CallBack,\r
+  IN  VOID                  *Context\r
+  );\r
+\r
+/**\r
+  The heartbeat timer of IP6 service instance. It times out\r
+  all of its IP6 children's received-but-not-delivered and\r
+  transmitted-but-not-recycle packets.\r
+\r
+  @param[in]  Event                 The IP6 service instance's heart beat timer.\r
+  @param[in]  Context               The IP6 service instance.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6TimerTicking (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Impl.c b/NetworkPkg/Ip6Dxe/Ip6Impl.c
new file mode 100644 (file)
index 0000000..9b34ece
--- /dev/null
@@ -0,0 +1,1857 @@
+/** @file\r
+  Implementation of EFI_IP6_PROTOCOL protocol interfaces.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_IPSEC_PROTOCOL    *mIpSec = NULL;\r
+\r
+EFI_IP6_PROTOCOL mEfiIp6ProtocolTemplete = {\r
+  EfiIp6GetModeData,\r
+  EfiIp6Configure,\r
+  EfiIp6Groups,\r
+  EfiIp6Routes,\r
+  EfiIp6Neighbors,\r
+  EfiIp6Transmit,\r
+  EfiIp6Receive,\r
+  EfiIp6Cancel,\r
+  EfiIp6Poll\r
+};\r
+\r
+/**\r
+  Gets the current operational settings for this instance of the EFI IPv6 Protocol driver.\r
+\r
+  The GetModeData() function returns the current operational mode data for this driver instance.\r
+  The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to\r
+  retrieve the operational mode data of underlying networks or drivers.\r
+\r
+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[out] Ip6ModeData        Pointer to the EFI IPv6 Protocol mode data structure.\r
+  @param[out] MnpConfigData      Pointer to the managed network configuration data structure.\r
+  @param[out] SnpModeData        Pointer to the simple network mode data structure.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   The required mode data could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6GetModeData (\r
+  IN EFI_IP6_PROTOCOL                 *This,\r
+  OUT EFI_IP6_MODE_DATA               *Ip6ModeData     OPTIONAL,\r
+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData   OPTIONAL,\r
+  OUT EFI_SIMPLE_NETWORK_MODE         *SnpModeData     OPTIONAL\r
+  )\r
+{\r
+  IP6_PROTOCOL              *IpInstance;\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_INTERFACE             *IpIf;\r
+  EFI_IP6_CONFIG_DATA       *Config;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl     = gBS->RaiseTPL (TPL_CALLBACK);\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+  IpIf       = IpInstance->Interface;\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Ip6ModeData != NULL) {\r
+    //\r
+    // IsStarted is "whether the EfiIp6Configure has been called".\r
+    // IsConfigured is "whether the station address has been configured"\r
+    //\r
+    Ip6ModeData->IsStarted     = (BOOLEAN) (IpInstance->State == IP6_STATE_CONFIGED);\r
+    Ip6ModeData->MaxPacketSize = IpSb->MaxPacketSize;\r
+    CopyMem (&Ip6ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP6_CONFIG_DATA));\r
+    Ip6ModeData->IsConfigured  = FALSE;\r
+\r
+    Ip6ModeData->AddressCount  = 0;\r
+    Ip6ModeData->AddressList   = NULL;\r
+\r
+    Ip6ModeData->GroupCount    = IpInstance->GroupCount;\r
+    Ip6ModeData->GroupTable    = NULL;\r
+\r
+    Ip6ModeData->RouteCount    = 0;\r
+    Ip6ModeData->RouteTable    = NULL;\r
+\r
+    Ip6ModeData->NeighborCount = 0;\r
+    Ip6ModeData->NeighborCache = NULL;\r
+\r
+    Ip6ModeData->PrefixCount   = 0;\r
+    Ip6ModeData->PrefixTable   = NULL;\r
+\r
+    Ip6ModeData->IcmpTypeCount = 23;\r
+    Ip6ModeData->IcmpTypeList  = AllocateCopyPool (\r
+                                   Ip6ModeData->IcmpTypeCount * sizeof (EFI_IP6_ICMP_TYPE),\r
+                                   mIp6SupportedIcmp\r
+                                   );\r
+    if (Ip6ModeData->IcmpTypeList == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto Error;\r
+    }\r
+\r
+    //\r
+    // Return the currently configured IPv6 addresses and corresponding prefix lengths.\r
+    //\r
+    Status = Ip6BuildEfiAddressList (\r
+               IpSb,\r
+               &Ip6ModeData->AddressCount,\r
+               &Ip6ModeData->AddressList\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+\r
+    //\r
+    // Return the current station address for this IP child.\r
+    // If UseAnyStationAddress is set to TRUE, IP6 driver will\r
+    // select a source address from its address list. Otherwise use the\r
+    // StationAddress in config data.\r
+    //\r
+    if (Ip6ModeData->IsStarted) {\r
+      Config = &Ip6ModeData->ConfigData;\r
+\r
+      if (IpIf->Configured || NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {\r
+        Ip6ModeData->IsConfigured = TRUE;\r
+      } else {\r
+        Ip6ModeData->IsConfigured = FALSE;\r
+      }\r
+\r
+      //\r
+      // Build a EFI route table for user from the internal route table.\r
+      //\r
+      Status = Ip6BuildEfiRouteTable (\r
+                 IpSb->RouteTable,\r
+                 &Ip6ModeData->RouteCount,\r
+                 &Ip6ModeData->RouteTable\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error;\r
+      }\r
+    }\r
+\r
+    if (Ip6ModeData->IsConfigured) {\r
+      //\r
+      // Return the joined multicast group addresses.\r
+      //\r
+      if (IpInstance->GroupCount != 0) {\r
+        Ip6ModeData->GroupTable = AllocateCopyPool (\r
+                                    IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS),\r
+                                    IpInstance->GroupList\r
+                                    );\r
+        if (Ip6ModeData->GroupTable == NULL) {\r
+          Status = EFI_OUT_OF_RESOURCES;\r
+          goto Error;\r
+        }\r
+      }\r
+      //\r
+      // Return the neighbor cache entries\r
+      //\r
+      Status = Ip6BuildEfiNeighborCache (\r
+                 IpInstance,\r
+                 &Ip6ModeData->NeighborCount,\r
+                 &Ip6ModeData->NeighborCache\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error;\r
+      }\r
+\r
+      //\r
+      // Return the prefix table entries\r
+      //\r
+      Status = Ip6BuildPrefixTable (\r
+                 IpInstance,\r
+                 &Ip6ModeData->PrefixCount,\r
+                 &Ip6ModeData->PrefixTable\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error;\r
+      }\r
+\r
+    }\r
+  }\r
+\r
+  //\r
+  // Get fresh mode data from MNP, since underlying media status may change\r
+  //\r
+  Status = IpSb->Mnp->GetModeData (IpSb->Mnp, MnpConfigData, SnpModeData);\r
+\r
+  goto Exit;\r
+\r
+Error:\r
+  if (Ip6ModeData != NULL) {\r
+    if (Ip6ModeData->AddressList != NULL) {\r
+      FreePool (Ip6ModeData->AddressList);\r
+    }\r
+\r
+    if (Ip6ModeData->GroupTable != NULL) {\r
+      FreePool (Ip6ModeData->GroupTable);\r
+    }\r
+\r
+    if (Ip6ModeData->RouteTable != NULL) {\r
+      FreePool (Ip6ModeData->RouteTable);\r
+    }\r
+\r
+    if (Ip6ModeData->NeighborCache != NULL) {\r
+      FreePool (Ip6ModeData->NeighborCache);\r
+    }\r
+\r
+    if (Ip6ModeData->PrefixTable != NULL) {\r
+      FreePool (Ip6ModeData->PrefixTable);\r
+    }\r
+\r
+    if (Ip6ModeData->IcmpTypeList != NULL) {\r
+      FreePool (Ip6ModeData->IcmpTypeList);\r
+    }\r
+  }\r
+\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Validate that Ipv6 address is OK to be used as station address or next hop address/ neighbor.\r
+\r
+  @param[in]  IpSb               The IP6 service instance.\r
+  @param[in]  Ip                 The IPv6 address to validate.\r
+  @param[in]  Flag               If TRUE, validate if the address is OK to be used\r
+                                 as station address. If FALSE, validate if the\r
+                                 address is OK to be used as the next hop address/\r
+                                 neighbor.\r
+\r
+  @retval TRUE                   The Ip address is valid and could be used.\r
+  @retval FALSE                  Invalid Ip address.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsValidAddress (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Ip,\r
+  IN BOOLEAN                Flag\r
+  )\r
+{\r
+  if (!NetIp6IsUnspecifiedAddr (Ip)) {\r
+    if (!NetIp6IsValidUnicast(Ip)) {\r
+      return FALSE;\r
+    }\r
+    if (Ip6IsOneOfSetAddress (IpSb, Ip, NULL, NULL)) {\r
+      return Flag;\r
+    }\r
+  } else {\r
+    return Flag;\r
+  }\r
+\r
+  return (BOOLEAN) !Flag;\r
+}\r
+\r
+/**\r
+  Validate whether the value of protocol is illegal or not. Protocol is the 'Next Header' field\r
+  in the last IPv6 extension header, or basic IPv6 header is there's no extension header.\r
+\r
+  @param[in]  Protocol           Default value of 'Next Header'\r
+\r
+  @retval TRUE                   The protocol is illegal.\r
+  @retval FALSE                  The protocol is legal.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsIllegalProtocol (\r
+  IN UINT8                  Protocol\r
+  )\r
+{\r
+  if (Protocol == IP6_HOP_BY_HOP || Protocol == EFI_IP_PROTO_ICMP || Protocol == IP4_PROTO_IGMP) {\r
+    return TRUE;\r
+  }\r
+\r
+  if (Protocol == 41 || Protocol == 43 || Protocol == 44 || Protocol == 59 || Protocol == 60 || Protocol == 124) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Intiialize the IP6_PROTOCOL structure to the unconfigured states.\r
+\r
+  @param[in]       IpSb                   The IP6 service instance.\r
+  @param[in, out]  IpInstance             The IP6 child instance.\r
+\r
+**/\r
+VOID\r
+Ip6InitProtocol (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN OUT IP6_PROTOCOL       *IpInstance\r
+  )\r
+{\r
+  ASSERT ((IpSb != NULL) && (IpInstance != NULL));\r
+\r
+  ZeroMem (IpInstance, sizeof (IP6_PROTOCOL));\r
+\r
+  IpInstance->Signature = IP6_PROTOCOL_SIGNATURE;\r
+  IpInstance->State     = IP6_STATE_UNCONFIGED;\r
+  IpInstance->Service   = IpSb;\r
+  IpInstance->GroupList = NULL;\r
+  CopyMem (&IpInstance->Ip6Proto, &mEfiIp6ProtocolTemplete, sizeof (EFI_IP6_PROTOCOL));\r
+\r
+  NetMapInit  (&IpInstance->RxTokens);\r
+  NetMapInit  (&IpInstance->TxTokens);\r
+  InitializeListHead (&IpInstance->Received);\r
+  InitializeListHead (&IpInstance->Delivered);\r
+\r
+  EfiInitializeLock (&IpInstance->RecycleLock, TPL_NOTIFY);\r
+}\r
+\r
+/**\r
+  Configure the IP6 child. If the child is already configured,\r
+  change the configuration parameter. Otherwise, configure it\r
+  for the first time. The caller should validate the configuration\r
+  before deliver them to it. It also don't do configure NULL.\r
+\r
+  @param[in, out]  IpInstance         The IP6 child to configure.\r
+  @param[in]       Config             The configure data.\r
+\r
+  @retval EFI_SUCCESS            The IP6 child is successfully configured.\r
+  @retval EFI_DEVICE_ERROR       Failed to free the pending transive or to\r
+                                 configure  underlying MNP, or other errors.\r
+  @retval EFI_NO_MAPPING         The IP6 child is configured to use the default\r
+                                 address, but the default address hasn't been\r
+                                 configured. The IP6 child doesn't need to be\r
+                                 reconfigured when the default address is configured.\r
+  @retval EFI_OUT_OF_RESOURCES   No more memory space is available.\r
+  @retval other                  Other error occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigProtocol (\r
+  IN OUT IP6_PROTOCOL        *IpInstance,\r
+  IN     EFI_IP6_CONFIG_DATA *Config\r
+  )\r
+{\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_INTERFACE             *IpIf;\r
+  EFI_STATUS                Status;\r
+  EFI_IP6_CONFIG_DATA       *Current;\r
+  IP6_ADDRESS_INFO          *AddressInfo;\r
+  BOOLEAN                   StationZero;\r
+  BOOLEAN                   DestZero;\r
+  EFI_IPv6_ADDRESS          Source;\r
+  BOOLEAN                   AddrOk;\r
+\r
+  IpSb    = IpInstance->Service;\r
+  Current = &IpInstance->ConfigData;\r
+\r
+  //\r
+  // User is changing packet filters. It must be stopped\r
+  // before the station address can be changed.\r
+  //\r
+  if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+    //\r
+    // Cancel all the pending transmit/receive from upper layer\r
+    //\r
+    Status = Ip6Cancel (IpInstance, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Set up the interface.\r
+  //\r
+  StationZero = NetIp6IsUnspecifiedAddr (&Config->StationAddress);\r
+  DestZero    = NetIp6IsUnspecifiedAddr (&Config->DestinationAddress);\r
+\r
+  if (StationZero && DestZero) {\r
+    //\r
+    // StationAddress is still zero.\r
+    //\r
+\r
+    NET_GET_REF (IpSb->DefaultInterface);\r
+    IpInstance->Interface = IpSb->DefaultInterface;\r
+    InsertTailList (&IpSb->DefaultInterface->IpInstances, &IpInstance->AddrLink);\r
+\r
+    CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));\r
+    IpInstance->State = IP6_STATE_CONFIGED;\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (StationZero && !DestZero) {\r
+    Status = Ip6SelectSourceAddress (IpSb, &Config->DestinationAddress, &Source);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  } else {\r
+    IP6_COPY_ADDRESS (&Source, &Config->StationAddress);\r
+  }\r
+\r
+  AddrOk = Ip6IsOneOfSetAddress (IpSb, &Source, &IpIf, &AddressInfo);\r
+  if (AddrOk) {\r
+    if (AddressInfo != NULL) {\r
+      IpInstance->PrefixLength = AddressInfo->PrefixLength;\r
+    } else {\r
+      IpInstance->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;\r
+    }\r
+  } else {\r
+    //\r
+    // The specified source address is not one of the addresses IPv6 maintains.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+\r
+  NET_GET_REF (IpIf);\r
+  IpInstance->Interface = IpIf;\r
+  InsertTailList (&IpIf->IpInstances, &IpInstance->AddrLink);\r
+\r
+  CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));\r
+  IP6_COPY_ADDRESS (&Current->StationAddress, &Source);\r
+  IpInstance->State = IP6_STATE_CONFIGED;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Clean up the IP6 child, and release all the resources used by it.\r
+\r
+  @param[in, out]  IpInstance    The IP6 child to clean up.\r
+\r
+  @retval EFI_SUCCESS            The IP6 child is cleaned up.\r
+  @retval EFI_DEVICE_ERROR       Some resources failed to be released.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CleanProtocol (\r
+  IN OUT IP6_PROTOCOL            *IpInstance\r
+  )\r
+{\r
+  if (EFI_ERROR (Ip6Cancel (IpInstance, NULL))) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (EFI_ERROR (Ip6Groups (IpInstance, FALSE, NULL))) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Some packets haven't been recycled. It is because either the\r
+  // user forgets to recycle the packets, or because the callback\r
+  // hasn't been called. Just leave it alone.\r
+  //\r
+  if (!IsListEmpty (&IpInstance->Delivered)) {\r
+    ;\r
+  }\r
+\r
+  if (IpInstance->Interface != NULL) {\r
+    RemoveEntryList (&IpInstance->AddrLink);\r
+    Ip6CleanInterface (IpInstance->Interface, IpInstance);\r
+    IpInstance->Interface = NULL;\r
+  }\r
+\r
+  if (IpInstance->GroupList != NULL) {\r
+    FreePool (IpInstance->GroupList);\r
+    IpInstance->GroupList   = NULL;\r
+    IpInstance->GroupCount  = 0;\r
+  }\r
+\r
+  NetMapClean (&IpInstance->TxTokens);\r
+\r
+  NetMapClean (&IpInstance->RxTokens);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Configure the MNP parameter used by IP. The IP driver uses one MNP\r
+  child to transmit/receive frames. By default, it configures MNP\r
+  to receive unicast/multicast/broadcast. Also, it will enable/disable\r
+  the promiscuous receive according to whether there is IP child\r
+  enable that or not. If Force is FALSE, it will iterate through\r
+  all the IP children to check whether the promiscuous receive\r
+  setting has been changed. If it hasn't been changed, it won't\r
+  reconfigure the MNP. If Force is TRUE, the MNP is configured\r
+  whether that is changed or not.\r
+\r
+  @param[in]  IpSb               The IP6 service instance that is to be changed.\r
+  @param[in]  Force              Force the configuration or not.\r
+\r
+  @retval EFI_SUCCESS            The MNP successfully configured/reconfigured.\r
+  @retval Others                 Configuration failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ServiceConfigMnp (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN BOOLEAN                Force\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *ProtoEntry;\r
+  IP6_INTERFACE             *IpIf;\r
+  IP6_PROTOCOL              *IpInstance;\r
+  BOOLEAN                   Reconfig;\r
+  BOOLEAN                   PromiscReceive;\r
+  EFI_STATUS                Status;\r
+\r
+  Reconfig       = FALSE;\r
+  PromiscReceive = FALSE;\r
+\r
+  if (!Force) {\r
+    //\r
+    // Iterate through the IP children to check whether promiscuous\r
+    // receive setting has been changed. Update the interface's receive\r
+    // filter also.\r
+    //\r
+    NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+\r
+      IpIf              = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+      IpIf->PromiscRecv = FALSE;\r
+\r
+      NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) {\r
+        IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP6_PROTOCOL, AddrLink);\r
+\r
+        if (IpInstance->ConfigData.AcceptPromiscuous) {\r
+          IpIf->PromiscRecv = TRUE;\r
+          PromiscReceive    = TRUE;\r
+        }\r
+      }\r
+    }\r
+\r
+    //\r
+    // If promiscuous receive isn't changed, it isn't necessary to reconfigure.\r
+    //\r
+    if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    Reconfig  = TRUE;\r
+    IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive;\r
+  }\r
+\r
+  Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData);\r
+\r
+  //\r
+  // recover the original configuration if failed to set the configure.\r
+  //\r
+  if (EFI_ERROR (Status) && Reconfig) {\r
+    IpSb->MnpConfigData.EnablePromiscuousReceive = (BOOLEAN) !PromiscReceive;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance.\r
+\r
+  The Configure() function is used to set, change, or reset the operational parameters and filter\r
+  settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic\r
+  can be sent or received by this instance. Once the parameters have been reset (by calling this\r
+  function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these\r
+  parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped\r
+  independently of each other by enabling or disabling their receive filter settings with the\r
+  Configure() function.\r
+\r
+  If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required\r
+  to be one of the currently configured IPv6 addresses listed in the EFI IPv6 drivers, or else\r
+  EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is\r
+  unspecified, the IPv6 driver will bind a source address according to the source address selection\r
+  algorithm. Clients could frequently call GetModeData() to check get currently configured IPv6\r
+  address list in the EFI IPv6 driver. If both Ip6ConfigData.StationAddress and\r
+  Ip6ConfigData.Destination are unspecified, when transmitting the packet afterwards, the\r
+  source address filled in each outgoing IPv6 packet is decided based on the destination of this packet.\r
+\r
+  If operational parameters are reset or changed, any pending transmit and receive requests will be\r
+  cancelled. Their completion token status will be set to EFI_ABORTED and their events will be\r
+  signaled.\r
+\r
+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  Ip6ConfigData      Pointer to the EFI IPv6 Protocol configuration data structure.\r
+                                 If NULL, reset the configuration data.\r
+\r
+  @retval EFI_SUCCESS            The driver instance was successfully opened.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Ip6ConfigData.StationAddress is neither zero nor\r
+                                   a unicast IPv6 address.\r
+                                 - Ip6ConfigData.StationAddress is neither zero nor\r
+                                   one of the configured IP addresses in the EFI IPv6 driver.\r
+                                 - Ip6ConfigData.DefaultProtocol is illegal.\r
+  @retval EFI_OUT_OF_RESOURCES   The EFI IPv6 Protocol driver instance data could not be allocated.\r
+  @retval EFI_NO_MAPPING         The IPv6 driver was responsible for choosing a source address for\r
+                                 this instance, but no source address was available for use.\r
+  @retval EFI_ALREADY_STARTED    The interface is already open and must be stopped before the IPv6\r
+                                 address or prefix length can be changed.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred. The EFI IPv6\r
+                                 Protocol driver instance was not opened.\r
+  @retval EFI_UNSUPPORTED        Default protocol specified through\r
+                                 Ip6ConfigData.DefaulProtocol isn't supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Configure (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN EFI_IP6_CONFIG_DATA       *Ip6ConfigData OPTIONAL\r
+  )\r
+{\r
+  IP6_PROTOCOL              *IpInstance;\r
+  EFI_IP6_CONFIG_DATA       *Current;\r
+  EFI_TPL                   OldTpl;\r
+  EFI_STATUS                Status;\r
+  IP6_SERVICE               *IpSb;\r
+\r
+  //\r
+  // First, validate the parameters\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  OldTpl     = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Status     = EFI_INVALID_PARAMETER;\r
+\r
+  //\r
+  // Validate the configuration first.\r
+  //\r
+  if (Ip6ConfigData != NULL) {\r
+    //\r
+    // Check whether the station address is valid.\r
+    //\r
+    if (!Ip6IsValidAddress (IpSb, &Ip6ConfigData->StationAddress, TRUE)) {\r
+       goto Exit;\r
+    }\r
+    //\r
+    // Check whether the default protocol is valid.\r
+    //\r
+    if (Ip6IsIllegalProtocol (Ip6ConfigData->DefaultProtocol)) {\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // User can only update packet filters when already configured.\r
+    // If it wants to change the station address, it must configure(NULL)\r
+    // the instance firstly.\r
+    //\r
+    if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+      Current = &IpInstance->ConfigData;\r
+\r
+      if (!EFI_IP6_EQUAL (&Current->StationAddress, &Ip6ConfigData->StationAddress)) {\r
+        Status = EFI_ALREADY_STARTED;\r
+        goto Exit;\r
+      }\r
+\r
+      if (NetIp6IsUnspecifiedAddr (&Current->StationAddress) && IP6_NO_MAPPING (IpInstance)) {\r
+        Status = EFI_NO_MAPPING;\r
+        goto Exit;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Configure the instance or clean it up.\r
+  //\r
+  if (Ip6ConfigData != NULL) {\r
+    Status = Ip6ConfigProtocol (IpInstance, Ip6ConfigData);\r
+  } else {\r
+    Status = Ip6CleanProtocol (IpInstance);\r
+\r
+    //\r
+    // Don't change the state if it is DESTORY, consider the following\r
+    // valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped,\r
+    // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED,\r
+    // the unload fails miserably.\r
+    //\r
+    if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+      IpInstance->State = IP6_STATE_UNCONFIGED;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Update the MNP's configure data. Ip6ServiceConfigMnp will check\r
+  // whether it is necessary to reconfigure the MNP.\r
+  //\r
+  Ip6ServiceConfigMnp (IpInstance->Service, FALSE);\r
+\r
+  //\r
+  // Update the variable data.\r
+  //\r
+  Ip6SetVariableData (IpInstance->Service);\r
+\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Joins and leaves multicast groups.\r
+\r
+  The Groups() function is used to join and leave multicast group sessions. Joining a group will\r
+  enable reception of matching multicast packets. Leaving a group will disable reception of matching\r
+  multicast packets. Source-Specific Multicast isn't required to be supported.\r
+\r
+  If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.\r
+\r
+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  JoinFlag           Set to TRUE to join the multicast group session, and FALSE to leave.\r
+  @param[in]  GroupAddress       Pointer to the IPv6 multicast address.\r
+                                 This is an optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following is TRUE:\r
+                                 - This is NULL.\r
+                                 - JoinFlag is TRUE and GroupAddress is NULL.\r
+                                 - GroupAddress is not NULL and *GroupAddress is\r
+                                   not a multicast IPv6 address.\r
+                                 - GroupAddress is not NULL and *GroupAddress is in the\r
+                                   range of SSM destination address.\r
+  @retval EFI_NOT_STARTED        This instance has not been started.\r
+  @retval EFI_OUT_OF_RESOURCES   System resources could not be allocated.\r
+  @retval EFI_UNSUPPORTED        This EFI IPv6 Protocol implementation does not support multicast groups.\r
+  @retval EFI_ALREADY_STARTED    The group address is already in the group table (when\r
+                                 JoinFlag is TRUE).\r
+  @retval EFI_NOT_FOUND          The group address is not in the group table (when JoinFlag is FALSE).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Groups (\r
+  IN EFI_IP6_PROTOCOL  *This,\r
+  IN BOOLEAN           JoinFlag,\r
+  IN EFI_IPv6_ADDRESS  *GroupAddress  OPTIONAL\r
+  )\r
+{\r
+  EFI_TPL                   OldTpl;\r
+  EFI_STATUS                Status;\r
+  IP6_PROTOCOL              *IpInstance;\r
+  IP6_SERVICE               *IpSb;\r
+\r
+  if ((This == NULL) || (JoinFlag && GroupAddress == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (GroupAddress != NULL && !IP6_IS_MULTICAST (GroupAddress)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  OldTpl     = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Ip6Groups (IpInstance, JoinFlag, GroupAddress);\r
+\r
+ON_EXIT:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Adds and deletes routing table entries.\r
+\r
+  The Routes() function adds a route to, or deletes a route from, the routing table.\r
+\r
+  Routes are determined by comparing the leftmost PrefixLength bits of Destination with\r
+  the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the\r
+  configured station address.\r
+\r
+  The default route is added with Destination and PrefixLegth both set to all zeros. The\r
+  default route matches all destination IPv6 addresses that do not match any other routes.\r
+\r
+  All EFI IPv6 Protocol instances share a routing table.\r
+\r
+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  DeleteRoute        Set to TRUE to delete this route from the routing table. Set to\r
+                                 FALSE to add this route to the routing table. Destination,\r
+                                 PrefixLength and Gateway are used as the key to each\r
+                                 route entry.\r
+  @param[in]  Destination        The address prefix of the subnet that needs to be routed.\r
+                                 This is an optional parameter that may be NULL.\r
+  @param[in]  PrefixLength       The prefix length of Destination. Ignored if Destination\r
+                                 is NULL.\r
+  @param[in]  GatewayAddress     The unicast gateway IPv6 address for this route.\r
+                                 This is an optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_NOT_STARTED        The driver instance has not been started.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - When DeleteRoute is TRUE, both Destination and\r
+                                   GatewayAddress are NULL.\r
+                                 - When DeleteRoute is FALSE, either Destination or\r
+                                   GatewayAddress is NULL.\r
+                                 - *GatewayAddress is not a valid unicast IPv6 address.\r
+                                 - *GatewayAddress is one of the local configured IPv6\r
+                                   addresses.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not add the entry to the routing table.\r
+  @retval EFI_NOT_FOUND          This route is not in the routing table (when DeleteRoute is TRUE).\r
+  @retval EFI_ACCESS_DENIED      The route is already defined in the routing table (when\r
+                                 DeleteRoute is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Routes (\r
+  IN EFI_IP6_PROTOCOL    *This,\r
+  IN BOOLEAN             DeleteRoute,\r
+  IN EFI_IPv6_ADDRESS    *Destination    OPTIONAL,\r
+  IN UINT8               PrefixLength,\r
+  IN EFI_IPv6_ADDRESS    *GatewayAddress OPTIONAL\r
+  )\r
+{\r
+  IP6_PROTOCOL              *IpInstance;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  IP6_SERVICE               *IpSb;\r
+\r
+  if ((This == NULL) || (PrefixLength >= IP6_PREFIX_NUM)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (DeleteRoute && (Destination == NULL) && (GatewayAddress == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!DeleteRoute && (Destination == NULL || GatewayAddress == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (GatewayAddress != NULL) {\r
+    if (!Ip6IsValidAddress (IpSb, GatewayAddress, FALSE)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (!NetIp6IsUnspecifiedAddr (GatewayAddress) &&\r
+        !NetIp6IsNetEqual (GatewayAddress, &IpInstance->ConfigData.StationAddress, PrefixLength)\r
+          ) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // Update the route table\r
+  //\r
+  if (DeleteRoute) {\r
+    Status = Ip6DelRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress);\r
+  } else {\r
+    Status = Ip6AddRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress);\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Add or delete Neighbor cache entries.\r
+\r
+  The Neighbors() function is used to add, update, or delete an entry from neighbor cache.\r
+  IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as\r
+  network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network\r
+  traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not\r
+  timeout) or dynamic (will timeout).\r
+\r
+  The implementation should follow the neighbor cache timeout mechanism which is defined in\r
+  RFC4861. The default neighbor cache timeout value should be tuned for the expected network\r
+  environment\r
+\r
+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  DeleteFlag         Set to TRUE to delete the specified cache entry, set to FALSE to\r
+                                 add (or update, if it already exists and Override is TRUE) the\r
+                                 specified cache entry. TargetIp6Address is used as the key\r
+                                 to find the requested cache entry.\r
+  @param[in]  TargetIp6Address   Pointer to the Target IPv6 address.\r
+  @param[in]  TargetLinkAddress  Pointer to the link-layer address of the target. Ignored if NULL.\r
+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor\r
+                                 cache, it will be deleted after Timeout. A value of zero means that\r
+                                 the entry is permanent. A non-zero value means that the entry is\r
+                                 dynamic.\r
+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will\r
+                                 be overridden and updated; if FALSE, EFI_ACCESS_DENIED\r
+                                 will be returned if a corresponding cache entry already existed.\r
+\r
+  @retval  EFI_SUCCESS           The data has been queued for transmission.\r
+  @retval  EFI_NOT_STARTED       This instance has not been started.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - TargetIpAddress is NULL.\r
+                                 - *TargetLinkAddress is invalid when not NULL.\r
+                                 - *TargetIpAddress is not a valid unicast IPv6 address.\r
+                                 - *TargetIpAddress is one of the local configured IPv6\r
+                                   addresses.\r
+  @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache.\r
+  @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache (when DeleteFlag  is\r
+                                 TRUE or when DeleteFlag  is FALSE while\r
+                                 TargetLinkAddress is NULL.).\r
+  @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,\r
+                                 and that entry is tagged as un-overridden (when Override\r
+                                 is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Neighbors (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN BOOLEAN                   DeleteFlag,\r
+  IN EFI_IPv6_ADDRESS          *TargetIp6Address,\r
+  IN EFI_MAC_ADDRESS           *TargetLinkAddress OPTIONAL,\r
+  IN UINT32                    Timeout,\r
+  IN BOOLEAN                   Override\r
+  )\r
+{\r
+  EFI_TPL                   OldTpl;\r
+  EFI_STATUS                Status;\r
+  IP6_PROTOCOL              *IpInstance;\r
+  IP6_SERVICE               *IpSb;\r
+\r
+  if (This == NULL || TargetIp6Address == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (NetIp6IsUnspecifiedAddr (TargetIp6Address)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (!Ip6IsValidAddress (IpSb, TargetIp6Address, FALSE)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (TargetLinkAddress != NULL) {\r
+    if (!Ip6IsValidLinkAddress (IpSb, TargetLinkAddress)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  if (Ip6IsOneOfSetAddress (IpSb, TargetIp6Address, NULL, NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  if (DeleteFlag) {\r
+    Status = Ip6DelNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override);\r
+  } else {\r
+    Status = Ip6AddNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override);\r
+  }\r
+\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Check whether the user's token or event has already\r
+  been enqueue on IP6's list.\r
+\r
+  @param[in]  Map                The container of either user's transmit or receive\r
+                                 token.\r
+  @param[in]  Item               Current item to check against.\r
+  @param[in]  Context            The Token to check againist.\r
+\r
+  @retval EFI_ACCESS_DENIED      The token or event has already been enqueued in IP\r
+  @retval EFI_SUCCESS            The current item isn't the same token/event as the\r
+                                 context.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6TokenExist (\r
+  IN NET_MAP                *Map,\r
+  IN NET_MAP_ITEM           *Item,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_IP6_COMPLETION_TOKEN  *Token;\r
+  EFI_IP6_COMPLETION_TOKEN  *TokenInItem;\r
+\r
+  Token       = (EFI_IP6_COMPLETION_TOKEN *) Context;\r
+  TokenInItem = (EFI_IP6_COMPLETION_TOKEN *) Item->Key;\r
+\r
+  if (Token == TokenInItem || Token->Event == TokenInItem->Event) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Validate the user's token against the current station address.\r
+\r
+  @param[in]  Token              User's token to validate.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Some parameters are invalid.\r
+  @retval EFI_BAD_BUFFER_SIZE    The user's option/data is too long.\r
+  @retval EFI_SUCCESS            The token is OK.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6TxTokenValid (\r
+  IN EFI_IP6_COMPLETION_TOKEN   *Token\r
+  )\r
+{\r
+  EFI_IP6_TRANSMIT_DATA     *TxData;\r
+  UINT32                    Index;\r
+  UINT32                    DataLength;\r
+\r
+  if (Token == NULL || Token->Event == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  TxData = Token->Packet.TxData;\r
+\r
+  if (TxData == NULL || (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (TxData->FragmentCount == 0 || TxData->DataLength == 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  for (DataLength = 0, Index = 0; Index < TxData->FragmentCount; Index++) {\r
+    if (TxData->FragmentTable[Index].FragmentLength == 0 || TxData->FragmentTable[Index].FragmentBuffer == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    DataLength += TxData->FragmentTable[Index].FragmentLength;\r
+  }\r
+\r
+  if (TxData->DataLength != DataLength) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // TODO: Token.Packet.TxData.DataLength is too short to transmit.\r
+  // return EFI_BUFFER_TOO_SMALL;\r
+  //\r
+\r
+  //\r
+  // If Token.Packet.TxData.DataLength is beyond the maximum that which can be\r
+  // described through the Fragment Offset field in Fragment header when performing\r
+  // fragmentation.\r
+  //\r
+  if (TxData->DataLength > 64 * 1024) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  The callback function for the net buffer which wraps the user's\r
+  transmit token. Although  this function seems simple, there\r
+  are some subtle aspects.\r
+  When user requests the IP to transmit a packet by passing it a\r
+  token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data\r
+  is wrapped in an net buffer. The net buffer's Free function is\r
+  set to Ip6FreeTxToken. The Token and token wrap are added to the\r
+  IP child's TxToken map. Then the buffer is passed to Ip6Output for\r
+  transmission. If an error happened before that, the buffer\r
+  is freed, which in turn frees the token wrap. The wrap may\r
+  have been added to the TxToken map or not, and the user's event\r
+  shouldn't be fired because we are still in the EfiIp6Transmit. If\r
+  the buffer has been sent by Ip6Output, it should be removed from\r
+  the TxToken map and user's event signaled. The token wrap and buffer\r
+  are bound together. Check the comments in Ip6Output for information\r
+  about IP fragmentation.\r
+\r
+  @param[in]  Context                The token's wrap.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6FreeTxToken (\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP6_TXTOKEN_WRAP          *Wrap;\r
+  NET_MAP_ITEM              *Item;\r
+\r
+  Wrap = (IP6_TXTOKEN_WRAP *) Context;\r
+\r
+  //\r
+  // Signal IpSecRecycleEvent to inform IPsec free the memory\r
+  //\r
+  if (Wrap->IpSecRecycleSignal != NULL) {\r
+    gBS->SignalEvent (Wrap->IpSecRecycleSignal);\r
+  }\r
+\r
+  //\r
+  // Find the token in the instance's map. EfiIp6Transmit put the\r
+  // token to the map. If that failed, NetMapFindKey will return NULL.\r
+  //\r
+  Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token);\r
+\r
+  if (Item != NULL) {\r
+    NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL);\r
+  }\r
+\r
+  if (Wrap->Sent) {\r
+    gBS->SignalEvent (Wrap->Token->Event);\r
+\r
+    //\r
+    // Dispatch the DPC queued by the NotifyFunction of Token->Event.\r
+    //\r
+    DispatchDpc ();\r
+  }\r
+\r
+  FreePool (Wrap);\r
+}\r
+\r
+\r
+/**\r
+  The callback function to Ip6Output to update the transmit status.\r
+\r
+  @param[in]  Packet           The user's transmit packet.\r
+  @param[in]  IoStatus         The result of the transmission.\r
+  @param[in]  Flag             Not used during transmission.\r
+  @param[in]  Context          The token's wrap.\r
+\r
+**/\r
+VOID\r
+Ip6OnPacketSent (\r
+  IN NET_BUF                   *Packet,\r
+  IN EFI_STATUS                IoStatus,\r
+  IN UINT32                    Flag,\r
+  IN VOID                      *Context\r
+  )\r
+{\r
+  IP6_TXTOKEN_WRAP             *Wrap;\r
+\r
+  //\r
+  // This is the transmission request from upper layer,\r
+  // not the IP6 driver itself.\r
+  //\r
+  Wrap                = (IP6_TXTOKEN_WRAP *) Context;\r
+  Wrap->Token->Status = IoStatus;\r
+\r
+  NetbufFree (Wrap->Packet);\r
+}\r
+\r
+/**\r
+  Places outgoing data packets into the transmit queue.\r
+\r
+  The Transmit() function places a sending request in the transmit queue of this\r
+  EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some\r
+  errors occur, the event in the token will be signaled, and the status is updated.\r
+\r
+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to the transmit token.\r
+\r
+  @retval  EFI_SUCCESS           The data has been queued for transmission.\r
+  @retval  EFI_NOT_STARTED       This instance has not been started.\r
+  @retval  EFI_NO_MAPPING        The IPv6 driver was responsible for choosing\r
+                                 a source address for this transmission,\r
+                                 but no source address was available for use.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token.Event is NULL.\r
+                                 - Token.Packet.TxData is NULL.\r
+                                 - Token.Packet.ExtHdrsLength is not zero and\r
+                                   Token.Packet.ExtHdrs is NULL.\r
+                                 - Token.Packet.FragmentCount is zero.\r
+                                 - One or more of the Token.Packet.TxData.\r
+                                   FragmentTable[].FragmentLength fields is zero.\r
+                                 - One or more of the Token.Packet.TxData.\r
+                                   FragmentTable[].FragmentBuffer fields is NULL.\r
+                                 - Token.Packet.TxData.DataLength is zero or not\r
+                                   equal to the sum of fragment lengths.\r
+                                 - Token.Packet.TxData.DestinationAddress is non\r
+                                   zero when DestinationAddress is configured as\r
+                                   non-zero when doing Configure() for this\r
+                                   EFI IPv6 protocol instance.\r
+                                 - Token.Packet.TxData.DestinationAddress is\r
+                                   unspecified when DestinationAddress is unspecified\r
+                                   when doing Configure() for this EFI IPv6 protocol\r
+                                   instance.\r
+  @retval  EFI_ACCESS_DENIED     The transmit completion token with the same Token.\r
+                                 Event was already in the transmit queue.\r
+  @retval  EFI_NOT_READY         The completion token could not be queued because\r
+                                 the transmit queue is full.\r
+  @retval  EFI_NOT_FOUND         Not route is found to destination address.\r
+  @retval  EFI_OUT_OF_RESOURCES  Could not queue the transmit data.\r
+  @retval  EFI_BUFFER_TOO_SMALL  Token.Packet.TxData.TotalDataLength is too\r
+                                 short to transmit.\r
+  @retval  EFI_BAD_BUFFER_SIZE   If Token.Packet.TxData.DataLength is beyond the\r
+                                 maximum that which can be described through the\r
+                                 Fragment Offset field in Fragment header when\r
+                                 performing fragmentation.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Transmit (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN EFI_IP6_COMPLETION_TOKEN  *Token\r
+  )\r
+{\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_PROTOCOL              *IpInstance;\r
+  EFI_IP6_CONFIG_DATA       *Config;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  EFI_IP6_HEADER            Head;\r
+  EFI_IP6_TRANSMIT_DATA     *TxData;\r
+  EFI_IP6_OVERRIDE_DATA     *Override;\r
+  IP6_TXTOKEN_WRAP          *Wrap;\r
+  UINT8                     *ExtHdrs;\r
+\r
+  //\r
+  // Check input parameters.\r
+  //\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ExtHdrs = NULL;\r
+\r
+  Status = Ip6TxTokenValid (Token);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  OldTpl     = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  Config = &IpInstance->ConfigData;\r
+\r
+  //\r
+  // Check whether the token or signal already existed.\r
+  //\r
+  if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip6TokenExist, Token))) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Build the IP header, fill in the information from ConfigData or OverrideData\r
+  //\r
+  ZeroMem (&Head, sizeof(EFI_IP6_HEADER));\r
+  TxData = Token->Packet.TxData;\r
+  IP6_COPY_ADDRESS (&Head.SourceAddress, &Config->StationAddress);\r
+  IP6_COPY_ADDRESS (&Head.DestinationAddress, &Config->DestinationAddress);\r
+\r
+  Status = EFI_INVALID_PARAMETER;\r
+\r
+  if (NetIp6IsUnspecifiedAddr (&TxData->DestinationAddress)) {\r
+    if (NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {\r
+      goto Exit;\r
+    }\r
+\r
+    ASSERT (!NetIp6IsUnspecifiedAddr (&Config->StationAddress));\r
+\r
+  } else {\r
+    //\r
+    // StationAddress is unspecified only when ConfigData.Dest is unspecified.\r
+    // Use TxData.Dest to override the DestinationAddress.\r
+    //\r
+    if (!NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {\r
+      goto Exit;\r
+    }\r
+\r
+    if (NetIp6IsUnspecifiedAddr (&Config->StationAddress)) {\r
+      Status = Ip6SelectSourceAddress (\r
+                 IpSb,\r
+                 &TxData->DestinationAddress,\r
+                 &Head.SourceAddress\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Exit;\r
+      }\r
+    }\r
+\r
+    IP6_COPY_ADDRESS (&Head.DestinationAddress, &TxData->DestinationAddress);\r
+  }\r
+\r
+  //\r
+  // Fill in Head infos.\r
+  //\r
+  Head.NextHeader = Config->DefaultProtocol;\r
+  if (TxData->ExtHdrsLength != 0) {\r
+    Head.NextHeader = TxData->NextHeader;\r
+  }\r
+\r
+  if (TxData->OverrideData != NULL) {\r
+    Override        = TxData->OverrideData;\r
+    Head.NextHeader = Override->Protocol;\r
+    Head.HopLimit   = Override->HopLimit;\r
+    Head.FlowLabelL = HTONS ((UINT16) Override->FlowLabel);\r
+    Head.FlowLabelH = (UINT8) ((Override->FlowLabel >> 16) & 0x0F);\r
+  } else {\r
+    Head.HopLimit   = Config->HopLimit;\r
+    Head.FlowLabelL = HTONS ((UINT16) Config->FlowLabel);\r
+    Head.FlowLabelH = (UINT8) ((Config->FlowLabel >> 16) & 0x0F);\r
+  }\r
+\r
+  Head.PayloadLength = HTONS ((UINT16) (TxData->ExtHdrsLength + TxData->DataLength));\r
+\r
+  //\r
+  // OK, it survives all the validation check. Wrap the token in\r
+  // a IP6_TXTOKEN_WRAP and the data in a netbuf\r
+  //\r
+  Status = EFI_OUT_OF_RESOURCES;\r
+  Wrap   = AllocateZeroPool (sizeof (IP6_TXTOKEN_WRAP));\r
+  if (Wrap == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  Wrap->IpInstance  = IpInstance;\r
+  Wrap->Token       = Token;\r
+  Wrap->Sent        = FALSE;\r
+  Wrap->Life        = IP6_US_TO_SEC (Config->TransmitTimeout);\r
+  Wrap->Packet      = NetbufFromExt (\r
+                        (NET_FRAGMENT *) TxData->FragmentTable,\r
+                        TxData->FragmentCount,\r
+                        IP6_MAX_HEADLEN,\r
+                        0,\r
+                        Ip6FreeTxToken,\r
+                        Wrap\r
+                        );\r
+\r
+  if (Wrap->Packet == NULL) {\r
+    FreePool (Wrap);\r
+    goto Exit;\r
+  }\r
+\r
+  Token->Status = EFI_NOT_READY;\r
+\r
+  Status = NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap);\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // NetbufFree will call Ip6FreeTxToken, which in turn will\r
+    // free the IP6_TXTOKEN_WRAP. Now, the token wrap hasn't been\r
+    // enqueued.\r
+    //\r
+    NetbufFree (Wrap->Packet);\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Allocate a new buffer to store IPv6 extension headers to avoid updating\r
+  // the original data in EFI_IP6_COMPLETION_TOKEN.\r
+  //\r
+  if (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs != NULL) {\r
+    ExtHdrs = (UINT8 *) AllocateCopyPool (TxData->ExtHdrsLength, TxData->ExtHdrs);\r
+    if (ExtHdrs == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Mark the packet sent before output it. Mark it not sent again if the\r
+  // returned status is not EFI_SUCCESS;\r
+  //\r
+  Wrap->Sent = TRUE;\r
+\r
+  Status = Ip6Output (\r
+             IpSb,\r
+             NULL,\r
+             IpInstance,\r
+             Wrap->Packet,\r
+             &Head,\r
+             ExtHdrs,\r
+             TxData->ExtHdrsLength,\r
+             Ip6OnPacketSent,\r
+             Wrap\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    Wrap->Sent = FALSE;\r
+    NetbufFree (Wrap->Packet);\r
+  }\r
+\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  if (ExtHdrs != NULL) {\r
+    FreePool (ExtHdrs);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Places a receiving request into the receiving queue.\r
+\r
+  The Receive() function places a completion token into the receive packet queue.\r
+  This function is always asynchronous.\r
+\r
+  The Token.Event field in the completion token must be filled in by the caller\r
+  and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol\r
+  driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event\r
+  is signaled.\r
+\r
+  Current Udp implementation creates an IP child for each Udp child.\r
+  It initates a asynchronous receive immediately no matter whether\r
+  there is no mapping or not. Therefore, disable the returning EFI_NO_MAPPING for now.\r
+  To enable it, the following check must be performed:\r
+\r
+  if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto Exit;\r
+  }\r
+\r
+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to a token that is associated with the receive data descriptor.\r
+\r
+  @retval EFI_SUCCESS            The receive completion token was cached.\r
+  @retval EFI_NOT_STARTED        This EFI IPv6 Protocol instance has not been started.\r
+  @retval EFI_NO_MAPPING         When IP6 driver responsible for binding source address to this instance,\r
+                                 while no source address is available for use.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued due to a lack of system\r
+                                 resources (usually memory).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+                                 The EFI IPv6 Protocol instance has been reset to startup defaults.\r
+  @retval EFI_ACCESS_DENIED      The receive completion token with the same Token.Event was already\r
+                                 in the receive queue.\r
+  @retval EFI_NOT_READY          The receive request could not be queued because the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Receive (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN EFI_IP6_COMPLETION_TOKEN  *Token\r
+  )\r
+{\r
+  IP6_PROTOCOL              *IpInstance;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  IP6_SERVICE               *IpSb;\r
+\r
+  if (This == NULL || Token == NULL || Token->Event == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Check whether the toke is already on the receive queue.\r
+  //\r
+  Status = NetMapIterate (&IpInstance->RxTokens, Ip6TokenExist, Token);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Queue the token then check whether there is pending received packet.\r
+  //\r
+  Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  Status = Ip6InstanceDeliverPacket (IpInstance);\r
+\r
+  //\r
+  // Dispatch the DPC queued by the NotifyFunction of this instane's receive\r
+  // event.\r
+  //\r
+  DispatchDpc ();\r
+\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Cancel the transmitted but not recycled packet. If a matching\r
+  token is found, it will call Ip6CancelPacket to cancel the\r
+  packet. Ip6CancelPacket cancels all the fragments of the\r
+  packet. When all the fragments are freed, the IP6_TXTOKEN_WRAP\r
+  is deleted from the Map, and user's event is signalled.\r
+  Because Ip6CancelPacket and other functions are all called in\r
+  line, after Ip6CancelPacket returns, the Item has been freed.\r
+\r
+  @param[in]  Map                The IP6 child's transmit queue.\r
+  @param[in]  Item               The current transmitted packet to test.\r
+  @param[in]  Context            The user's token to cancel.\r
+\r
+  @retval EFI_SUCCESS            Continue to check the next Item.\r
+  @retval EFI_ABORTED            The user's Token (Token != NULL) is cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6CancelTxTokens (\r
+  IN NET_MAP                *Map,\r
+  IN NET_MAP_ITEM           *Item,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_IP6_COMPLETION_TOKEN  *Token;\r
+  IP6_TXTOKEN_WRAP          *Wrap;\r
+\r
+  Token = (EFI_IP6_COMPLETION_TOKEN *) Context;\r
+\r
+  //\r
+  // Return EFI_SUCCESS to check the next item in the map if\r
+  // this one doesn't match.\r
+  //\r
+  if ((Token != NULL) && (Token != Item->Key)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;\r
+  ASSERT (Wrap != NULL);\r
+\r
+  //\r
+  // Don't access the Item, Wrap and Token's members after this point.\r
+  // Item and wrap has been freed. And we no longer own the Token.\r
+  //\r
+  Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);\r
+\r
+  //\r
+  // If only one item is to be cancel, return EFI_ABORTED to stop\r
+  // iterating the map any more.\r
+  //\r
+  if (Token != NULL) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Cancel the receive request. This is simple, because\r
+  it is only enqueued in our local receive map.\r
+\r
+  @param[in]  Map                The IP6 child's receive queue.\r
+  @param[in]  Item               Current receive request to cancel.\r
+  @param[in]  Context            The user's token to cancel.\r
+\r
+\r
+  @retval EFI_SUCCESS            Continue to check the next receive request on the\r
+                                 queue.\r
+  @retval EFI_ABORTED            The user's token (token != NULL) has been\r
+                                 cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6CancelRxTokens (\r
+  IN NET_MAP                *Map,\r
+  IN NET_MAP_ITEM           *Item,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_IP6_COMPLETION_TOKEN  *Token;\r
+  EFI_IP6_COMPLETION_TOKEN  *This;\r
+\r
+  Token = (EFI_IP6_COMPLETION_TOKEN *) Context;\r
+  This  = Item->Key;\r
+\r
+  if ((Token != NULL) && (Token != This)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  NetMapRemoveItem (Map, Item, NULL);\r
+\r
+  This->Status        = EFI_ABORTED;\r
+  This->Packet.RxData = NULL;\r
+  gBS->SignalEvent (This->Event);\r
+\r
+  if (Token != NULL) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Cancel the user's receive/transmit request. It is the worker function of\r
+  EfiIp6Cancel API.\r
+\r
+  @param[in]  IpInstance         The IP6 child.\r
+  @param[in]  Token              The token to cancel. If NULL, all token will be\r
+                                 cancelled.\r
+\r
+  @retval EFI_SUCCESS            The token is cancelled.\r
+  @retval EFI_NOT_FOUND          The token isn't found on either the\r
+                                 transmit/receive queue.\r
+  @retval EFI_DEVICE_ERROR       Not all tokens are cancelled when Token is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Cancel (\r
+  IN IP6_PROTOCOL             *IpInstance,\r
+  IN EFI_IP6_COMPLETION_TOKEN *Token          OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // First check the transmitted packet. Ip6CancelTxTokens returns\r
+  // EFI_ABORTED to mean that the token has been cancelled when\r
+  // token != NULL. So, return EFI_SUCCESS for this condition.\r
+  //\r
+  Status = NetMapIterate (&IpInstance->TxTokens, Ip6CancelTxTokens, Token);\r
+  if (EFI_ERROR (Status)) {\r
+    if ((Token != NULL) && (Status == EFI_ABORTED)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Check the receive queue. Ip6CancelRxTokens also returns EFI_ABORT\r
+  // for Token!=NULL and it is cancelled.\r
+  //\r
+  Status = NetMapIterate (&IpInstance->RxTokens, Ip6CancelRxTokens, Token);\r
+  //\r
+  // Dispatch the DPCs queued by the NotifyFunction of the canceled rx token's\r
+  // events.\r
+  //\r
+  DispatchDpc ();\r
+  if (EFI_ERROR (Status)) {\r
+    if ((Token != NULL) && (Status == EFI_ABORTED)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // OK, if the Token is found when Token != NULL, the NetMapIterate\r
+  // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS.\r
+  //\r
+  if (Token != NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // If Token == NULL, cancel all the tokens. return error if not\r
+  // all of them are cancelled.\r
+  //\r
+  if (!NetMapIsEmpty (&IpInstance->TxTokens) || !NetMapIsEmpty (&IpInstance->RxTokens)) {\r
+\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Abort an asynchronous transmit or receive request.\r
+\r
+  The Cancel() function is used to abort a pending transmit or receive request.\r
+  If the token is in the transmit or receive request queues, after calling this\r
+  function, Token->Status will be set to EFI_ABORTED, and then Token->Event will\r
+  be signaled. If the token is not in one of the queues, which usually means the\r
+  asynchronous operation has completed, this function will not signal the token,\r
+  and EFI_NOT_FOUND is returned.\r
+\r
+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to a token that has been issued by\r
+                                 EFI_IP6_PROTOCOL.Transmit() or\r
+                                 EFI_IP6_PROTOCOL.Receive(). If NULL, all pending\r
+                                 tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is\r
+                                 defined in EFI_IP6_PROTOCOL.Transmit().\r
+\r
+  @retval EFI_SUCCESS            The asynchronous I/O request was aborted and\r
+                                 Token->Event was signaled. When Token is NULL, all\r
+                                 pending requests were aborted, and their events were signaled.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_NOT_STARTED        This instance has not been started.\r
+  @retval EFI_NOT_FOUND          When Token is not NULL, the asynchronous I/O request was\r
+                                 not found in the transmit or receive queue. It has either completed\r
+                                 or was not issued by Transmit() and Receive().\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Cancel (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN EFI_IP6_COMPLETION_TOKEN  *Token    OPTIONAL\r
+  )\r
+{\r
+  IP6_PROTOCOL              *IpInstance;\r
+  IP6_SERVICE               *IpSb;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  Status = Ip6Cancel (IpInstance, Token);\r
+\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Polls for incoming data packets, and processes outgoing data packets.\r
+\r
+  The Poll() function polls for incoming data packets and processes outgoing data\r
+  packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll()\r
+  function to increase the rate that data packets are moved between the communications\r
+  device and the transmit and receive queues.\r
+\r
+  In some systems the periodic timer event may not poll the underlying communications\r
+  device fast enough to transmit and/or receive all data packets without missing\r
+  incoming packets or dropping outgoing packets. Drivers and applications that are\r
+  experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function\r
+  more often.\r
+\r
+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.\r
+\r
+  @retval  EFI_SUCCESS           Incoming or outgoing data was processed.\r
+  @retval  EFI_NOT_STARTED       This EFI IPv6 Protocol instance has not been started.\r
+  @retval  EFI_INVALID_PARAMETER This is NULL.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected system error or network error occurred.\r
+  @retval  EFI_NOT_READY         No incoming or outgoing data was processed.\r
+  @retval  EFI_TIMEOUT           Data was dropped out of the transmit and/or receive queue.\r
+                                 Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Poll (\r
+  IN EFI_IP6_PROTOCOL          *This\r
+  )\r
+{\r
+  IP6_PROTOCOL                  *IpInstance;\r
+  IP6_SERVICE                   *IpSb;\r
+  EFI_MANAGED_NETWORK_PROTOCOL  *Mnp;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+  IpSb       = IpInstance->Service;\r
+\r
+  if (IpSb->LinkLocalDadFail) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (IpInstance->State == IP6_STATE_UNCONFIGED) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  Mnp = IpInstance->Service->Mnp;\r
+\r
+  //\r
+  // Don't lock the Poll function to enable the deliver of\r
+  // the packet polled up.\r
+  //\r
+  return Mnp->Poll (Mnp);\r
+\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Impl.h b/NetworkPkg/Ip6Dxe/Ip6Impl.h
new file mode 100644 (file)
index 0000000..524de5e
--- /dev/null
@@ -0,0 +1,751 @@
+/** @file\r
+  Implementation of EFI_IP6_PROTOCOL protocol interfaces and type definitions.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_IMPL_H__\r
+#define __EFI_IP6_IMPL_H__\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/ManagedNetwork.h>\r
+#include <Protocol/IpSec.h>\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Ip6Config.h>\r
+#include <Protocol/Dhcp6.h>\r
+#include <Protocol/DevicePath.h>\r
+#include <Protocol/HiiConfigRouting.h>\r
+#include <Protocol/HiiConfigAccess.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/NetLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DpcLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/UefiHiiServicesLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/PrintLib.h>\r
+\r
+#include <Guid/MdeModuleHii.h>\r
+\r
+#include "Ip6Common.h"\r
+#include "Ip6Driver.h"\r
+#include "Ip6Icmp.h"\r
+#include "Ip6If.h"\r
+#include "Ip6Input.h"\r
+#include "Ip6Mld.h"\r
+#include "Ip6Nd.h"\r
+#include "Ip6Option.h"\r
+#include "Ip6Output.h"\r
+#include "Ip6Route.h"\r
+#include "Ip6ConfigNv.h"\r
+#include "Ip6ConfigImpl.h"\r
+\r
+#define IP6_PROTOCOL_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'P')\r
+#define IP6_SERVICE_SIGNATURE  SIGNATURE_32 ('I', 'P', '6', 'S')\r
+\r
+//\r
+// The state of IP6 protocol. It starts from UNCONFIGED. if it is\r
+// successfully configured, it goes to CONFIGED. if configure NULL\r
+// is called, it becomes UNCONFIGED again. If (partly) destroyed, it\r
+// becomes DESTROY.\r
+//\r
+#define IP6_STATE_UNCONFIGED   0\r
+#define IP6_STATE_CONFIGED     1\r
+#define IP6_STATE_DESTROY      2\r
+\r
+//\r
+// The state of IP6 service. It starts from UNSTARTED. It transits\r
+// to STARTED if autoconfigure is started. If default address is\r
+// configured, it becomes CONFIGED. and if partly destroyed, it goes\r
+// to DESTROY.\r
+//\r
+#define IP6_SERVICE_UNSTARTED  0\r
+#define IP6_SERVICE_STARTED    1\r
+#define IP6_SERVICE_CONFIGED   2\r
+#define IP6_SERVICE_DESTROY    3\r
+\r
+#define IP6_INSTANCE_FROM_PROTOCOL(Ip6) \\r
+          CR ((Ip6), IP6_PROTOCOL, Ip6Proto, IP6_PROTOCOL_SIGNATURE)\r
+\r
+#define IP6_SERVICE_FROM_PROTOCOL(Sb)   \\r
+          CR ((Sb), IP6_SERVICE, ServiceBinding, IP6_SERVICE_SIGNATURE)\r
+\r
+#define IP6_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured)\r
+\r
+extern EFI_IPSEC_PROTOCOL *mIpSec;\r
+\r
+//\r
+// IP6_TXTOKEN_WRAP wraps the upper layer's transmit token.\r
+// The user's data is kept in the Packet. When fragment is\r
+// needed, each fragment of the Packet has a reference to the\r
+// Packet, no data is actually copied. The Packet will be\r
+// released when all the fragments of it have been recycled by\r
+// MNP. Upon then, the IP6_TXTOKEN_WRAP will be released, and\r
+// user's event signalled.\r
+//\r
+typedef struct {\r
+  IP6_PROTOCOL              *IpInstance;\r
+  EFI_IP6_COMPLETION_TOKEN  *Token;\r
+  EFI_EVENT                 IpSecRecycleSignal;\r
+  NET_BUF                   *Packet;\r
+  BOOLEAN                   Sent;\r
+  INTN                      Life;\r
+} IP6_TXTOKEN_WRAP;\r
+\r
+typedef struct {\r
+  EFI_EVENT                 IpSecRecycleSignal;\r
+  NET_BUF                   *Packet;\r
+} IP6_IPSEC_WRAP;\r
+\r
+//\r
+// IP6_RXDATA_WRAP wraps the data IP6 child delivers to the\r
+// upper layers. The received packet is kept in the Packet.\r
+// The Packet itself may be constructured from some fragments.\r
+// All the fragments of the Packet is organized by a\r
+// IP6_ASSEMBLE_ENTRY structure. If the Packet is recycled by\r
+// the upper layer, the assemble entry and its associated\r
+// fragments will be freed at last.\r
+//\r
+typedef struct {\r
+  LIST_ENTRY                Link;\r
+  IP6_PROTOCOL              *IpInstance;\r
+  NET_BUF                   *Packet;\r
+  EFI_IP6_RECEIVE_DATA      RxData;\r
+} IP6_RXDATA_WRAP;\r
+\r
+struct _IP6_PROTOCOL {\r
+  UINT32                    Signature;\r
+\r
+  EFI_IP6_PROTOCOL          Ip6Proto;\r
+  EFI_HANDLE                Handle;\r
+  INTN                      State;\r
+\r
+  IP6_SERVICE               *Service;\r
+  LIST_ENTRY                Link; // Link to all the IP protocol from the service\r
+\r
+  UINT8                     PrefixLength; // PrefixLength of the configured station address.\r
+  //\r
+  // User's transmit/receive tokens, and received/deliverd packets\r
+  //\r
+  NET_MAP                   RxTokens;\r
+  NET_MAP                   TxTokens;   // map between (User's Token, IP6_TXTOKE_WRAP)\r
+  LIST_ENTRY                Received;   // Received but not delivered packet\r
+  LIST_ENTRY                Delivered;  // Delivered and to be recycled packets\r
+  EFI_LOCK                  RecycleLock;\r
+\r
+  IP6_INTERFACE             *Interface;\r
+  LIST_ENTRY                AddrLink;   // Ip instances with the same IP address.\r
+\r
+  EFI_IPv6_ADDRESS          *GroupList; // stored in network order.\r
+  UINT32                    GroupCount;\r
+\r
+  EFI_IP6_CONFIG_DATA       ConfigData;\r
+};\r
+\r
+struct _IP6_SERVICE {\r
+  UINT32                          Signature;\r
+  EFI_SERVICE_BINDING_PROTOCOL    ServiceBinding;\r
+  INTN                            State;\r
+  BOOLEAN                         InDestroy;\r
+\r
+  //\r
+  // List of all the IP instances and interfaces, and default\r
+  // interface and route table and caches.\r
+  //\r
+  UINTN                           NumChildren;\r
+  LIST_ENTRY                      Children;\r
+\r
+  LIST_ENTRY                      Interfaces;\r
+\r
+  IP6_INTERFACE                   *DefaultInterface;\r
+  IP6_ROUTE_TABLE                 *RouteTable;\r
+\r
+  IP6_LINK_RX_TOKEN               RecvRequest;\r
+\r
+  //\r
+  // Ip reassemble utilities and MLD data\r
+  //\r
+  IP6_ASSEMBLE_TABLE              Assemble;\r
+  IP6_MLD_SERVICE_DATA            MldCtrl;\r
+\r
+  EFI_IPv6_ADDRESS                LinkLocalAddr;\r
+  BOOLEAN                         LinkLocalOk;\r
+  BOOLEAN                         LinkLocalDadFail;\r
+  BOOLEAN                         Dhcp6NeedStart;\r
+  BOOLEAN                         Dhcp6NeedInfoRequest;\r
+\r
+  //\r
+  // ND data\r
+  //\r
+  UINT8                           CurHopLimit;\r
+  UINT32                          LinkMTU;\r
+  UINT32                          BaseReachableTime;\r
+  UINT32                          ReachableTime;\r
+  UINT32                          RetransTimer;\r
+  LIST_ENTRY                      NeighborTable;\r
+\r
+  LIST_ENTRY                      OnlinkPrefix;\r
+  LIST_ENTRY                      AutonomousPrefix;\r
+\r
+  LIST_ENTRY                      DefaultRouterList;\r
+  UINT32                          RoundRobin;\r
+\r
+  UINT8                           InterfaceIdLen;\r
+  UINT8                           *InterfaceId;\r
+\r
+  BOOLEAN                         RouterAdvertiseReceived;\r
+  UINT8                           SolicitTimer;\r
+  UINT32                          Ticks;\r
+\r
+  //\r
+  // Low level protocol used by this service instance\r
+  //\r
+  EFI_HANDLE                      Image;\r
+  EFI_HANDLE                      Controller;\r
+\r
+  EFI_HANDLE                      MnpChildHandle;\r
+  EFI_MANAGED_NETWORK_PROTOCOL    *Mnp;\r
+\r
+  EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;\r
+  EFI_SIMPLE_NETWORK_MODE         SnpMode;\r
+\r
+  EFI_EVENT                       Timer;\r
+  EFI_EVENT                       FasterTimer;\r
+\r
+  //\r
+  // IPv6 Configuration Protocol instance\r
+  //\r
+  IP6_CONFIG_INSTANCE             Ip6ConfigInstance;\r
+\r
+  //\r
+  // The string representation of the current mac address of the\r
+  // NIC this IP6_SERVICE works on.\r
+  //\r
+  CHAR16                          *MacString;\r
+  UINT32                          MaxPacketSize;\r
+  UINT32                          OldMaxPacketSize;\r
+};\r
+\r
+/**\r
+  The callback function for the net buffer which wraps the user's\r
+  transmit token. Although this function seems simple,\r
+  there are some subtle aspects.\r
+  When a user requests the IP to transmit a packet by passing it a\r
+  token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data\r
+  is wrapped in a net buffer. The net buffer's Free function is\r
+  set to Ip6FreeTxToken. The Token and token wrap are added to the\r
+  IP child's TxToken map. Then the buffer is passed to Ip6Output for\r
+  transmission. If an error occurs before that, the buffer\r
+  is freed, which in turn frees the token wrap. The wrap may\r
+  have been added to the TxToken map or not, and the user's event\r
+  shouldn't be signaled because we are still in the EfiIp6Transmit. If\r
+  the buffer has been sent by Ip6Output, it should be removed from\r
+  the TxToken map and the user's event signaled. The token wrap and buffer\r
+  are bound together. Refer to the comments in Ip6Output for information\r
+  about IP fragmentation.\r
+\r
+  @param[in]  Context                The token's wrap.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6FreeTxToken (\r
+  IN VOID                   *Context\r
+  );\r
+\r
+/**\r
+  Config the MNP parameter used by IP. The IP driver use one MNP\r
+  child to transmit/receive frames. By default, it configures MNP\r
+  to receive unicast/multicast/broadcast. And it will enable/disable\r
+  the promiscuous receive according to whether there is IP child\r
+  enable that or not. If Force is FALSE, it will iterate through\r
+  all the IP children to check whether the promiscuous receive\r
+  setting has been changed. If it hasn't been changed, it won't\r
+  reconfigure the MNP. If Force is TRUE, the MNP is configured\r
+  whether that is changed or not.\r
+\r
+  @param[in]  IpSb               The IP6 service instance that is to be changed.\r
+  @param[in]  Force              Force the configuration or not.\r
+\r
+  @retval EFI_SUCCESS            The MNP successfully configured/reconfigured.\r
+  @retval Others                 The configuration failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ServiceConfigMnp (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN BOOLEAN                Force\r
+  );\r
+\r
+/**\r
+  Cancel the user's receive/transmit request. It is the worker function of\r
+  EfiIp6Cancel API.\r
+\r
+  @param[in]  IpInstance         The IP6 child.\r
+  @param[in]  Token              The token to cancel. If NULL, all tokens will be\r
+                                 cancelled.\r
+\r
+  @retval EFI_SUCCESS            The token was cancelled.\r
+  @retval EFI_NOT_FOUND          The token isn't found on either the\r
+                                 transmit or receive queue.\r
+  @retval EFI_DEVICE_ERROR       Not all tokens are cancelled when Token is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Cancel (\r
+  IN IP6_PROTOCOL             *IpInstance,\r
+  IN EFI_IP6_COMPLETION_TOKEN *Token          OPTIONAL\r
+  );\r
+\r
+/**\r
+  Initialize the IP6_PROTOCOL structure to the unconfigured states.\r
+\r
+  @param[in]       IpSb                   The IP6 service instance.\r
+  @param[in, out]  IpInstance             The IP6 child instance.\r
+\r
+**/\r
+VOID\r
+Ip6InitProtocol (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN OUT IP6_PROTOCOL       *IpInstance\r
+  );\r
+\r
+/**\r
+  Clean up the IP6 child, release all the resources used by it.\r
+\r
+  @param[in, out]  IpInstance    The IP6 child to clean up.\r
+\r
+  @retval EFI_SUCCESS            The IP6 child was cleaned up\r
+  @retval EFI_DEVICE_ERROR       Some resources failed to be released.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CleanProtocol (\r
+  IN OUT IP6_PROTOCOL            *IpInstance\r
+  );\r
+\r
+//\r
+// EFI_IP6_PROTOCOL interface prototypes\r
+//\r
+\r
+/**\r
+  Gets the current operational settings for this instance of the EFI IPv6 Protocol driver.\r
+\r
+  The GetModeData() function returns the current operational mode data for this driver instance.\r
+  The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to\r
+  retrieve the operational mode data of underlying networks or drivers.\r
+\r
+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[out] Ip6ModeData        The pointer to the EFI IPv6 Protocol mode data structure.\r
+  @param[out] MnpConfigData      The pointer to the managed network configuration data structure.\r
+  @param[out] SnpModeData        The pointer to the simple network mode data structure.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   The required mode data could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6GetModeData (\r
+  IN EFI_IP6_PROTOCOL                 *This,\r
+  OUT EFI_IP6_MODE_DATA               *Ip6ModeData     OPTIONAL,\r
+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData   OPTIONAL,\r
+  OUT EFI_SIMPLE_NETWORK_MODE         *SnpModeData     OPTIONAL\r
+  );\r
+\r
+/**\r
+  Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance.\r
+\r
+  The Configure() function is used to set, change, or reset the operational parameters and filter\r
+  settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic\r
+  can be sent or received by this instance. Once the parameters have been reset (by calling this\r
+  function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these\r
+  parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped\r
+  independently of each other by enabling or disabling their receive filter settings with the\r
+  Configure() function.\r
+\r
+  If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required\r
+  to be one of the currently configured IPv6 addresses list in the EFI IPv6 drivers, or else\r
+  EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is\r
+  unspecified, the IPv6 driver will bind a source address according to the source address selection\r
+  algorithm. Clients could frequently call GetModeData() to check get a currently configured IPv6.\r
+  If both Ip6ConfigData.StationAddress and Ip6ConfigData.Destination are unspecified, when\r
+  transmitting the packet afterwards, the source address filled in each outgoing IPv6 packet\r
+  is decided based on the destination of this packet.\r
+\r
+  If operational parameters are reset or changed, any pending transmit and receive requests will be\r
+  cancelled. Their completion token status will be set to EFI_ABORTED, and their events will be\r
+  signaled.\r
+\r
+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  Ip6ConfigData      The pointer to the EFI IPv6 Protocol configuration data structure.\r
+                                 If NULL, reset the configuration data.\r
+\r
+  @retval EFI_SUCCESS            The driver instance was successfully opened.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Ip6ConfigData.StationAddress is neither zero nor\r
+                                   a unicast IPv6 address.\r
+                                 - Ip6ConfigData.StationAddress is neither zero nor\r
+                                   one of the configured IP addresses in the EFI IPv6 driver.\r
+                                 - Ip6ConfigData.DefaultProtocol is illegal.\r
+  @retval EFI_OUT_OF_RESOURCES   The EFI IPv6 Protocol driver instance data could not be allocated.\r
+  @retval EFI_NO_MAPPING         The IPv6 driver was responsible for choosing a source address for\r
+                                 this instance, but no source address was available for use.\r
+  @retval EFI_ALREADY_STARTED    The interface is already open and must be stopped before the IPv6\r
+                                 address or prefix length can be changed.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred. The EFI IPv6\r
+                                 Protocol driver instance was not opened.\r
+  @retval EFI_UNSUPPORTED        Default protocol specified through\r
+                                 Ip6ConfigData.DefaulProtocol isn't supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Configure (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN EFI_IP6_CONFIG_DATA       *Ip6ConfigData OPTIONAL\r
+  );\r
+\r
+/**\r
+  Joins and leaves multicast groups.\r
+\r
+  The Groups() function is used to join and leave multicast group sessions. Joining a group will\r
+  enable reception of matching multicast packets. Leaving a group will disable reception of matching\r
+  multicast packets. Source-Specific Multicast isn't required to be supported.\r
+\r
+  If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.\r
+\r
+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  JoinFlag           Set to TRUE to join the multicast group session and FALSE to leave.\r
+  @param[in]  GroupAddress       The pointer to the IPv6 multicast address.\r
+                                 This is an optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following is TRUE:\r
+                                 - This is NULL.\r
+                                 - JoinFlag is TRUE and GroupAddress is NULL.\r
+                                 - GroupAddress is not NULL and *GroupAddress is\r
+                                   not a multicast IPv6 address.\r
+                                 - GroupAddress is not NULL and *GroupAddress is in the\r
+                                   range of SSM destination address.\r
+  @retval EFI_NOT_STARTED        This instance has not been started.\r
+  @retval EFI_OUT_OF_RESOURCES   System resources could not be allocated.\r
+  @retval EFI_UNSUPPORTED        This EFI IPv6 Protocol implementation does not support multicast groups.\r
+  @retval EFI_ALREADY_STARTED    The group address is already in the group table (when\r
+                                 JoinFlag is TRUE).\r
+  @retval EFI_NOT_FOUND          The group address is not in the group table (when JoinFlag is FALSE).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Groups (\r
+  IN EFI_IP6_PROTOCOL  *This,\r
+  IN BOOLEAN           JoinFlag,\r
+  IN EFI_IPv6_ADDRESS  *GroupAddress  OPTIONAL\r
+  );\r
+\r
+/**\r
+  Adds and deletes routing table entries.\r
+\r
+  The Routes() function adds a route to or deletes a route from the routing table.\r
+\r
+  Routes are determined by comparing the leftmost PrefixLength bits of Destination with\r
+  the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the\r
+  configured station address.\r
+\r
+  The default route is added with Destination and PrefixLegth both set to all zeros. The\r
+  default route matches all destination IPv6 addresses that do not match any other routes.\r
+\r
+  All EFI IPv6 Protocol instances share a routing table.\r
+\r
+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  DeleteRoute        Set to TRUE to delete this route from the routing table. Set to\r
+                                 FALSE to add this route to the routing table. Destination,\r
+                                 PrefixLength and Gateway are used as the key to each\r
+                                 route entry.\r
+  @param[in]  Destination        The address prefix of the subnet that needs to be routed.\r
+                                 This is an optional parameter that may be NULL.\r
+  @param[in]  PrefixLength       The prefix length of Destination. Ignored if Destination\r
+                                 is NULL.\r
+  @param[in]  GatewayAddress     The unicast gateway IPv6 address for this route.\r
+                                 This is an optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_NOT_STARTED        The driver instance has not been started.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - When DeleteRoute is TRUE, both Destination and\r
+                                   GatewayAddress are NULL.\r
+                                 - When DeleteRoute is FALSE, either Destination or\r
+                                   GatewayAddress is NULL.\r
+                                 - *GatewayAddress is not a valid unicast IPv6 address.\r
+                                 - *GatewayAddress is one of the local configured IPv6\r
+                                   addresses.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not add the entry to the routing table.\r
+  @retval EFI_NOT_FOUND          This route is not in the routing table (when DeleteRoute is TRUE).\r
+  @retval EFI_ACCESS_DENIED      The route is already defined in the routing table (when\r
+                                 DeleteRoute is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Routes (\r
+  IN EFI_IP6_PROTOCOL    *This,\r
+  IN BOOLEAN             DeleteRoute,\r
+  IN EFI_IPv6_ADDRESS    *Destination    OPTIONAL,\r
+  IN UINT8               PrefixLength,\r
+  IN EFI_IPv6_ADDRESS    *GatewayAddress OPTIONAL\r
+  );\r
+\r
+/**\r
+  Add or delete Neighbor cache entries.\r
+\r
+  The Neighbors() function is used to add, update, or delete an entry from a neighbor cache.\r
+  IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as\r
+  network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network\r
+  traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not\r
+  timeout) or dynamic (will timeout).\r
+\r
+  The implementation should follow the neighbor cache timeout mechanism defined in\r
+  RFC4861. The default neighbor cache timeout value should be tuned for the expected network\r
+  environment.\r
+\r
+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  DeleteFlag         Set to TRUE to delete the specified cache entry. Set to FALSE to\r
+                                 add (or update, if it already exists and Override is TRUE) the\r
+                                 specified cache entry. TargetIp6Address is used as the key\r
+                                 to find the requested cache entry.\r
+  @param[in]  TargetIp6Address   The pointer to the Target IPv6 address.\r
+  @param[in]  TargetLinkAddress  The pointer to link-layer address of the target. Ignored if NULL.\r
+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor\r
+                                 cache, it will be deleted after Timeout. A value of zero means that\r
+                                 the entry is permanent. A non-zero value means that the entry is\r
+                                 dynamic.\r
+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will\r
+                                 be overridden and updated; if FALSE, EFI_ACCESS_DENIED\r
+                                 will be returned if a corresponding cache entry already exists.\r
+\r
+  @retval  EFI_SUCCESS           The data has been queued for transmission.\r
+  @retval  EFI_NOT_STARTED       This instance has not been started.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - TargetIpAddress is NULL.\r
+                                 - *TargetLinkAddress is invalid when not NULL.\r
+                                 - *TargetIpAddress is not a valid unicast IPv6 address.\r
+                                 - *TargetIpAddress is one of the local configured IPv6\r
+                                   addresses.\r
+  @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache.\r
+  @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache (when DeleteFlag  is\r
+                                 TRUE or when DeleteFlag  is FALSE while\r
+                                 TargetLinkAddress is NULL.).\r
+  @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,\r
+                                 and that entry is tagged as un-overridden (when Override\r
+                                 is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Neighbors (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN BOOLEAN                   DeleteFlag,\r
+  IN EFI_IPv6_ADDRESS          *TargetIp6Address,\r
+  IN EFI_MAC_ADDRESS           *TargetLinkAddress OPTIONAL,\r
+  IN UINT32                    Timeout,\r
+  IN BOOLEAN                   Override\r
+  );\r
+\r
+/**\r
+  Places outgoing data packets into the transmit queue.\r
+\r
+  The Transmit() function places a sending request in the transmit queue of this\r
+  EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some\r
+  errors occur, the event in the token will be signaled and the status is updated.\r
+\r
+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  Token              The pointer to the transmit token.\r
+\r
+  @retval  EFI_SUCCESS           The data has been queued for transmission.\r
+  @retval  EFI_NOT_STARTED       This instance has not been started.\r
+  @retval  EFI_NO_MAPPING        The IPv6 driver was responsible for choosing\r
+                                 a source address for this transmission,\r
+                                 but no source address was available for use.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token.Event is NULL.\r
+                                 - Token.Packet.TxData is NULL.\r
+                                 - Token.Packet.ExtHdrsLength is not zero and\r
+                                   Token.Packet.ExtHdrs is NULL.\r
+                                 - Token.Packet.FragmentCount is zero.\r
+                                 - One or more of the Token.Packet.TxData.\r
+                                   FragmentTable[].FragmentLength fields is zero.\r
+                                 - One or more of the Token.Packet.TxData.\r
+                                   FragmentTable[].FragmentBuffer fields is NULL.\r
+                                 - Token.Packet.TxData.DataLength is zero or not\r
+                                   equal to the sum of fragment lengths.\r
+                                 - Token.Packet.TxData.DestinationAddress is non-\r
+                                   zero when DestinationAddress is configured as\r
+                                   non-zero when doing Configure() for this\r
+                                   EFI IPv6 protocol instance.\r
+                                 - Token.Packet.TxData.DestinationAddress is\r
+                                   unspecified when DestinationAddress is unspecified\r
+                                   when doing Configure() for this EFI IPv6 protocol\r
+                                   instance.\r
+  @retval  EFI_ACCESS_DENIED     The transmit completion token with the same Token.\r
+                                 The event was already in the transmit queue.\r
+  @retval  EFI_NOT_READY         The completion token could not be queued because\r
+                                 the transmit queue is full.\r
+  @retval  EFI_NOT_FOUND         Not route is found to the destination address.\r
+  @retval  EFI_OUT_OF_RESOURCES  Could not queue the transmit data.\r
+  @retval  EFI_BUFFER_TOO_SMALL  Token.Packet.TxData.TotalDataLength is too\r
+                                 short to transmit.\r
+  @retval  EFI_BAD_BUFFER_SIZE   If Token.Packet.TxData.DataLength is beyond the\r
+                                 maximum that which can be described through the\r
+                                 Fragment Offset field in Fragment header when\r
+                                 performing fragmentation.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Transmit (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN EFI_IP6_COMPLETION_TOKEN  *Token\r
+  );\r
+\r
+/**\r
+  Places a receiving request into the receiving queue.\r
+\r
+  The Receive() function places a completion token into the receive packet queue.\r
+  This function is always asynchronous.\r
+\r
+  The Token.Event field in the completion token must be filled in by the caller\r
+  and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol\r
+  driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event\r
+  is signaled.\r
+\r
+  Current Udp implementation creates an IP child for each Udp child.\r
+  It initates a asynchronous receive immediately whether or not\r
+  there is no mapping. Therefore, disable the returning EFI_NO_MAPPING for now.\r
+  To enable it, the following check must be performed:\r
+\r
+  if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto Exit;\r
+  }\r
+\r
+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  Token              The pointer to a token that is associated with the\r
+                                 receive data descriptor.\r
+\r
+  @retval EFI_SUCCESS            The receive completion token was cached.\r
+  @retval EFI_NOT_STARTED        This EFI IPv6 Protocol instance has not been started.\r
+  @retval EFI_NO_MAPPING         When IP6 driver responsible for binding source address to this instance,\r
+                                 while no source address is available for use.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued due to a lack of system\r
+                                 resources (usually memory).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+                                 The EFI IPv6 Protocol instance has been reset to startup defaults.\r
+  @retval EFI_ACCESS_DENIED      The receive completion token with the same Token.Event was already\r
+                                 in the receive queue.\r
+  @retval EFI_NOT_READY          The receive request could not be queued because the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Receive (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN EFI_IP6_COMPLETION_TOKEN  *Token\r
+  );\r
+\r
+/**\r
+  Abort an asynchronous transmit or receive request.\r
+\r
+  The Cancel() function is used to abort a pending transmit or receive request.\r
+  If the token is in the transmit or receive request queues, after calling this\r
+  function, Token->Status will be set to EFI_ABORTED, and then Token->Event will\r
+  be signaled. If the token is not in one of the queues, which usually means the\r
+  asynchronous operation has completed, this function will not signal the token,\r
+  and EFI_NOT_FOUND is returned.\r
+\r
+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.\r
+  @param[in]  Token              The pointer to a token that has been issued by\r
+                                 EFI_IP6_PROTOCOL.Transmit() or\r
+                                 EFI_IP6_PROTOCOL.Receive(). If NULL, all pending\r
+                                 tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is\r
+                                 defined in EFI_IP6_PROTOCOL.Transmit().\r
+\r
+  @retval EFI_SUCCESS            The asynchronous I/O request was aborted and\r
+                                 Token->Event was signaled. When Token is NULL, all\r
+                                 pending requests were aborted, and their events were signaled.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_NOT_STARTED        This instance has not been started.\r
+  @retval EFI_NOT_FOUND          When Token is not NULL, the asynchronous I/O request was\r
+                                 not found in the transmit or receive queue. It has either completed\r
+                                 or was not issued by Transmit() and Receive().\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Cancel (\r
+  IN EFI_IP6_PROTOCOL          *This,\r
+  IN EFI_IP6_COMPLETION_TOKEN  *Token    OPTIONAL\r
+  );\r
+\r
+/**\r
+  Polls for incoming data packets and processes outgoing data packets.\r
+\r
+  The Poll() function polls for incoming data packets and processes outgoing data\r
+  packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll()\r
+  function to increase the rate that data packets are moved between the communications\r
+  device and the transmit and receive queues.\r
+\r
+  In some systems the periodic timer event may not poll the underlying communications\r
+  device fast enough to transmit and/or receive all data packets without missing\r
+  incoming packets or dropping outgoing packets. Drivers and applications that are\r
+  experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function\r
+  more often.\r
+\r
+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.\r
+\r
+  @retval  EFI_SUCCESS           Incoming or outgoing data was processed.\r
+  @retval  EFI_NOT_STARTED       This EFI IPv6 Protocol instance has not been started.\r
+  @retval  EFI_INVALID_PARAMETER This is NULL.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+  @retval  EFI_NOT_READY         No incoming or outgoing data was processed.\r
+  @retval  EFI_TIMEOUT           Data was dropped out of the transmit and/or receive queue.\r
+                                 Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Poll (\r
+  IN EFI_IP6_PROTOCOL          *This\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Input.c b/NetworkPkg/Ip6Dxe/Ip6Input.c
new file mode 100644 (file)
index 0000000..c18811b
--- /dev/null
@@ -0,0 +1,1710 @@
+/** @file\r
+  IP6 internal functions to process the incoming packets.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+  Create an empty assemble entry for the packet identified by\r
+  (Dst, Src, Id). The default life for the packet is 60 seconds.\r
+\r
+  @param[in]  Dst                    The destination address.\r
+  @param[in]  Src                    The source address.\r
+  @param[in]  Id                     The ID field in the IP header.\r
+\r
+  @return NULL if failed to allocate memory for the entry. Otherwise,\r
+          the pointer to the just created reassemble entry.\r
+\r
+**/\r
+IP6_ASSEMBLE_ENTRY *\r
+Ip6CreateAssembleEntry (\r
+  IN EFI_IPv6_ADDRESS       *Dst,\r
+  IN EFI_IPv6_ADDRESS       *Src,\r
+  IN UINT32                 Id\r
+  )\r
+{\r
+  IP6_ASSEMBLE_ENTRY        *Assemble;\r
+\r
+  Assemble = AllocatePool (sizeof (IP6_ASSEMBLE_ENTRY));\r
+  if (Assemble == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  IP6_COPY_ADDRESS (&Assemble->Dst, Dst);\r
+  IP6_COPY_ADDRESS (&Assemble->Src, Src);\r
+  InitializeListHead (&Assemble->Fragments);\r
+\r
+  Assemble->Id       = Id;\r
+  Assemble->Life     = IP6_FRAGMENT_LIFE + 1;\r
+\r
+  Assemble->TotalLen = 0;\r
+  Assemble->CurLen   = 0;\r
+  Assemble->Head     = NULL;\r
+  Assemble->Info     = NULL;\r
+  Assemble->Packet   = NULL;\r
+\r
+  return Assemble;\r
+}\r
+\r
+/**\r
+  Release all the fragments of a packet, then free the assemble entry.\r
+\r
+  @param[in]  Assemble               The assemble entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeAssembleEntry (\r
+  IN IP6_ASSEMBLE_ENTRY     *Assemble\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  NET_BUF                   *Fragment;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {\r
+    Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+\r
+    RemoveEntryList (Entry);\r
+    NetbufFree (Fragment);\r
+  }\r
+\r
+  if (Assemble->Packet != NULL) {\r
+    NetbufFree (Assemble->Packet);\r
+  }\r
+\r
+  FreePool (Assemble);\r
+}\r
+\r
+/**\r
+  Release all the fragments of the packet. This is the callback for\r
+  the assembled packet's OnFree. It will free the assemble entry,\r
+  which in turn frees all the fragments of the packet.\r
+\r
+  @param[in]  Arg                    The assemble entry to free.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFreeFragments (\r
+  IN VOID                   *Arg\r
+  )\r
+{\r
+  Ip6FreeAssembleEntry ((IP6_ASSEMBLE_ENTRY *) Arg);\r
+}\r
+\r
+/**\r
+  Trim the packet to fit in [Start, End), and update per the\r
+  packet information.\r
+\r
+  @param[in, out]  Packet   Packet to trim.\r
+  @param[in]       Start    The sequence of the first byte to fit in.\r
+  @param[in]       End      One beyond the sequence of last byte to fit in.\r
+\r
+**/\r
+VOID\r
+Ip6TrimPacket (\r
+  IN OUT NET_BUF            *Packet,\r
+  IN INTN                   Start,\r
+  IN INTN                   End\r
+  )\r
+{\r
+  IP6_CLIP_INFO             *Info;\r
+  INTN                      Len;\r
+\r
+  Info = IP6_GET_CLIP_INFO (Packet);\r
+\r
+  ASSERT (Info->Start + Info->Length == Info->End);\r
+  ASSERT ((Info->Start < End) && (Start < Info->End));\r
+\r
+   if (Info->Start < Start) {\r
+    Len = Start - Info->Start;\r
+\r
+    NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);\r
+    Info->Start   = (UINT32) Start;\r
+    Info->Length -= (UINT32) Len;\r
+  }\r
+\r
+  if (End < Info->End) {\r
+    Len = End - Info->End;\r
+\r
+    NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);\r
+    Info->End     = (UINT32) End;\r
+    Info->Length -= (UINT32) Len;\r
+  }\r
+}\r
+\r
+/**\r
+  Reassemble the IP fragments. If all the fragments of the packet\r
+  have been received, it will wrap the packet in a net buffer then\r
+  return it to caller. If the packet can't be assembled, NULL is\r
+  returned.\r
+\r
+  @param[in, out] Table  The assemble table used. A new assemble entry will be created\r
+                         if the Packet is from a new chain of fragments.\r
+  @param[in]      Packet The fragment to assemble. It might be freed if the fragment\r
+                         can't be re-assembled.\r
+\r
+  @return NULL if the packet can't be reassembled. The pointer to the just assembled\r
+          packet if all the fragments of the packet have arrived.\r
+\r
+**/\r
+NET_BUF *\r
+Ip6Reassemble (\r
+  IN OUT IP6_ASSEMBLE_TABLE *Table,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  EFI_IP6_HEADER            *Head;\r
+  IP6_CLIP_INFO             *This;\r
+  IP6_CLIP_INFO             *Node;\r
+  IP6_ASSEMBLE_ENTRY        *Assemble;\r
+  IP6_ASSEMBLE_ENTRY        *Entry;\r
+  LIST_ENTRY                *ListHead;\r
+  LIST_ENTRY                *Prev;\r
+  LIST_ENTRY                *Cur;\r
+  NET_BUF                   *Fragment;\r
+  NET_BUF                   *TmpPacket;\r
+  NET_BUF                   *NewPacket;\r
+  NET_BUF                   *Duplicate;\r
+  UINT8                     *DupHead;\r
+  INTN                      Index;\r
+  UINT16                    UnFragmentLen;\r
+  UINT8                     *NextHeader;\r
+\r
+  Head = Packet->Ip.Ip6;\r
+  This = IP6_GET_CLIP_INFO (Packet);\r
+\r
+  ASSERT (Head != NULL);\r
+\r
+  //\r
+  // Find the corresponding assemble entry by (Dst, Src, Id)\r
+  //\r
+  Assemble  = NULL;\r
+  Index     = IP6_ASSEMBLE_HASH (&Head->DestinationAddress, &Head->SourceAddress, This->Id);\r
+\r
+  NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {\r
+    Entry = NET_LIST_USER_STRUCT (Cur, IP6_ASSEMBLE_ENTRY, Link);\r
+\r
+    if (Entry->Id == This->Id &&\r
+        EFI_IP6_EQUAL (&Entry->Src, &Head->SourceAddress) &&\r
+        EFI_IP6_EQUAL (&Entry->Dst, &Head->DestinationAddress)\r
+          ) {\r
+      Assemble = Entry;\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Create a new entry if can not find an existing one, insert it to assemble table\r
+  //\r
+  if (Assemble == NULL) {\r
+    Assemble = Ip6CreateAssembleEntry (\r
+                 &Head->DestinationAddress,\r
+                 &Head->SourceAddress,\r
+                 This->Id\r
+                 );\r
+\r
+    if (Assemble == NULL) {\r
+      goto Error;\r
+    }\r
+\r
+    InsertHeadList (&Table->Bucket[Index], &Assemble->Link);\r
+  }\r
+\r
+  //\r
+  // Find the point to insert the packet: before the first\r
+  // fragment with THIS.Start < CUR.Start. the previous one\r
+  // has PREV.Start <= THIS.Start < CUR.Start.\r
+  //\r
+  ListHead = &Assemble->Fragments;\r
+\r
+  NET_LIST_FOR_EACH (Cur, ListHead) {\r
+    Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+\r
+    if (This->Start < IP6_GET_CLIP_INFO (Fragment)->Start) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check whether the current fragment overlaps with the previous one.\r
+  // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to\r
+  // check whether THIS.Start < PREV.End for overlap. If two fragments\r
+  // overlaps, trim the overlapped part off THIS fragment.\r
+  //\r
+  if ((Cur != ListHead) && ((Prev = Cur->BackLink) != ListHead)) {\r
+    Fragment  = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);\r
+    Node      = IP6_GET_CLIP_INFO (Fragment);\r
+\r
+    if (This->Start < Node->End) {\r
+      if (This->End <= Node->End) {\r
+        goto Error;\r
+      }\r
+\r
+      //\r
+      // Trim the previous fragment from tail.\r
+      //\r
+      Ip6TrimPacket (Fragment, Node->Start, This->Start);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Insert the fragment into the packet. The fragment may be removed\r
+  // from the list by the following checks.\r
+  //\r
+  NetListInsertBefore (Cur, &Packet->List);\r
+\r
+  //\r
+  // Check the packets after the insert point. It holds that:\r
+  // THIS.Start <= NODE.Start < NODE.End. The equality holds\r
+  // if PREV and NEXT are continuous. THIS fragment may fill\r
+  // several holes. Remove the completely overlapped fragments\r
+  //\r
+  while (Cur != ListHead) {\r
+    Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+    Node     = IP6_GET_CLIP_INFO (Fragment);\r
+\r
+    //\r
+    // Remove fragments completely overlapped by this fragment\r
+    //\r
+    if (Node->End <= This->End) {\r
+      Cur = Cur->ForwardLink;\r
+\r
+      RemoveEntryList (&Fragment->List);\r
+      Assemble->CurLen -= Node->Length;\r
+\r
+      NetbufFree (Fragment);\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // The conditions are: THIS.Start <= NODE.Start, and THIS.End <\r
+    // NODE.End. Two fragments overlaps if NODE.Start < THIS.End.\r
+    // If two fragments start at the same offset, remove THIS fragment\r
+    // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).\r
+    //\r
+    if (Node->Start < This->End) {\r
+      if (This->Start == Node->Start) {\r
+        RemoveEntryList (&Packet->List);\r
+        goto Error;\r
+      }\r
+\r
+      Ip6TrimPacket (Packet, This->Start, Node->Start);\r
+    }\r
+\r
+    break;\r
+  }\r
+\r
+  //\r
+  // Update the assemble info: increase the current length. If it is\r
+  // the frist fragment, update the packet's IP head and per packet\r
+  // info. If it is the last fragment, update the total length.\r
+  //\r
+  Assemble->CurLen += This->Length;\r
+\r
+  if (This->Start == 0) {\r
+    //\r
+    // Once the first fragment is enqueued, it can't be removed\r
+    // from the fragment list. So, Assemble->Head always point\r
+    // to valid memory area.\r
+    //\r
+    if ((Assemble->Head != NULL) || (Assemble->Packet != NULL)) {\r
+      goto Error;\r
+    }\r
+\r
+    //\r
+    // Backup the first fragment in case the reasembly of that packet fail.\r
+    //\r
+    Duplicate = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));\r
+    if (Duplicate == NULL) {\r
+      goto Error;\r
+    }\r
+\r
+    //\r
+    // Revert IP head to network order.\r
+    //\r
+    DupHead = NetbufGetByte (Duplicate, 0, NULL);\r
+    ASSERT (DupHead != NULL);\r
+    Duplicate->Ip.Ip6 = Ip6NtohHead ((EFI_IP6_HEADER *) DupHead);\r
+    Assemble->Packet  = Duplicate;\r
+\r
+    //\r
+    // Adjust the unfragmentable part in first fragment\r
+    //\r
+    UnFragmentLen = (UINT16) (This->HeadLen - sizeof (EFI_IP6_HEADER));\r
+    if (UnFragmentLen == 0) {\r
+      //\r
+      // There is not any unfragmentable extension header.\r
+      //\r
+      ASSERT (Head->NextHeader == IP6_FRAGMENT);\r
+      Head->NextHeader = This->NextHeader;\r
+    } else {\r
+      NextHeader = NetbufGetByte (\r
+                     Packet,\r
+                     This->FormerNextHeader + sizeof (EFI_IP6_HEADER),\r
+                     0\r
+                     );\r
+      if (NextHeader == NULL) {\r
+        goto Error;\r
+      }\r
+\r
+      *NextHeader = This->NextHeader;\r
+    }\r
+\r
+    Assemble->Head = Head;\r
+    Assemble->Info = IP6_GET_CLIP_INFO (Packet);\r
+  }\r
+\r
+  //\r
+  // Don't update the length more than once.\r
+  //\r
+  if ((This->LastFrag != 0) && (Assemble->TotalLen == 0)) {\r
+    Assemble->TotalLen = This->End;\r
+  }\r
+\r
+  //\r
+  // Deliver the whole packet if all the fragments received.\r
+  // All fragments received if:\r
+  //  1. received the last one, so, the totoal length is know\r
+  //  2. received all the data. If the last fragment on the\r
+  //     queue ends at the total length, all data is received.\r
+  //\r
+  if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {\r
+\r
+    RemoveEntryList (&Assemble->Link);\r
+\r
+    //\r
+    // If the packet is properly formated, the last fragment's End\r
+    // equals to the packet's total length. Otherwise, the packet\r
+    // is a fake, drop it now.\r
+    //\r
+    Fragment = NET_LIST_USER_STRUCT (ListHead->BackLink, NET_BUF, List);\r
+    if (IP6_GET_CLIP_INFO (Fragment)->End != (INTN) Assemble->TotalLen) {\r
+      Ip6FreeAssembleEntry (Assemble);\r
+      goto Error;\r
+    }\r
+\r
+    Fragment = NET_LIST_HEAD (ListHead, NET_BUF, List);\r
+    This     = Assemble->Info;\r
+\r
+    //\r
+    // This TmpPacket is used to hold the unfragmentable part, i.e.,\r
+    // the IPv6 header and the unfragmentable extension headers. Be noted that\r
+    // the Fragment Header is exluded.\r
+    //\r
+    TmpPacket = NetbufGetFragment (Fragment, 0, This->HeadLen, 0);\r
+    ASSERT (TmpPacket != NULL);\r
+\r
+    NET_LIST_FOR_EACH (Cur, ListHead) {\r
+      //\r
+      // Trim off the unfragment part plus the fragment header from all fragments.\r
+      //\r
+      Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+      NetbufTrim (Fragment, This->HeadLen + sizeof (IP6_FRAGMENT_HEADER), TRUE);\r
+    }\r
+\r
+    InsertHeadList (ListHead, &TmpPacket->List);\r
+\r
+    //\r
+    // Wrap the packet in a net buffer then deliver it up\r
+    //\r
+    NewPacket = NetbufFromBufList (\r
+                  &Assemble->Fragments,\r
+                  0,\r
+                  0,\r
+                  Ip6OnFreeFragments,\r
+                  Assemble\r
+                  );\r
+\r
+    if (NewPacket == NULL) {\r
+      Ip6FreeAssembleEntry (Assemble);\r
+      goto Error;\r
+    }\r
+\r
+    NewPacket->Ip.Ip6 = Assemble->Head;\r
+\r
+    CopyMem (IP6_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP6_CLIP_INFO));\r
+\r
+    return NewPacket;\r
+  }\r
+\r
+  return NULL;\r
+\r
+Error:\r
+  NetbufFree (Packet);\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  The callback function for the net buffer that 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
+**/\r
+VOID\r
+EFIAPI\r
+Ip6IpSecFree (\r
+  IN VOID                   *Arg\r
+  )\r
+{\r
+  IP6_IPSEC_WRAP            *Wrap;\r
+\r
+  Wrap = (IP6_IPSEC_WRAP *) Arg;\r
+\r
+  if (Wrap->IpSecRecycleSignal != NULL) {\r
+    gBS->SignalEvent (Wrap->IpSecRecycleSignal);\r
+  }\r
+\r
+  NetbufFree (Wrap->Packet);\r
+\r
+  FreePool (Wrap);\r
+\r
+  return;\r
+}\r
+\r
+/**\r
+  The work function to locate the IPsec protocol to process the inbound or\r
+  outbound IP packets. The process routine handles the packet with the following\r
+  actions: bypass the packet, discard the packet, or protect the packet.\r
+\r
+  @param[in]       IpSb          The IP6 service instance.\r
+  @param[in]       Head          The caller-supplied IP6 header.\r
+  @param[in, out]  LastHead      The next header field of last IP header.\r
+  @param[in, out]  Netbuf        The IP6 packet to be processed by IPsec.\r
+  @param[in]       ExtHdrs       The caller-supplied options.\r
+  @param[in]       ExtHdrsLen    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_OUT_OF_RESOURCES   There are not suffcient resources to complete the operation.\r
+  @retval EFI_BUFFER_TOO_SMALL   The number of non-empty blocks is bigger than the\r
+                                 number of input data blocks when building a fragment table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IpSecProcessPacket (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN OUT UINT8              *LastHead,\r
+  IN OUT NET_BUF            **Netbuf,\r
+  IN VOID                   *ExtHdrs,\r
+  IN UINT32                 ExtHdrsLen,\r
+  IN EFI_IPSEC_TRAFFIC_DIR  Direction,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  NET_FRAGMENT              *FragmentTable;\r
+  UINT32                    FragmentCount;\r
+  EFI_EVENT                 RecycleEvent;\r
+  NET_BUF                   *Packet;\r
+  IP6_TXTOKEN_WRAP          *TxWrap;\r
+  IP6_IPSEC_WRAP            *IpSecWrap;\r
+  EFI_STATUS                Status;\r
+  EFI_IP6_HEADER            *PacketHead;\r
+  UINT8                     *Buf;\r
+\r
+  Status        = EFI_SUCCESS;\r
+  Packet        = *Netbuf;\r
+  RecycleEvent  = NULL;\r
+  IpSecWrap     = NULL;\r
+  FragmentTable = NULL;\r
+  PacketHead    = NULL;\r
+  Buf           = NULL;\r
+  TxWrap        = (IP6_TXTOKEN_WRAP *) Context;\r
+  FragmentCount = Packet->BlockOpNum;\r
+\r
+  if (mIpSec == NULL) {\r
+    gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &mIpSec);\r
+\r
+    //\r
+    // Check whether the ipsec protocol is available.\r
+    //\r
+    if (mIpSec == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\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
+    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
+    //\r
+    IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP6_MAX_IPSEC_HEADLEN;\r
+  }\r
+\r
+\r
+  //\r
+  // Bypass all multicast inbound or outbound traffic.\r
+  //\r
+  if (IP6_IS_MULTICAST (&Head->DestinationAddress) || IP6_IS_MULTICAST (&Head->SourceAddress)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Rebuild fragment table from netbuf to ease ipsec process.\r
+  //\r
+  FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT));\r
+\r
+  if (FragmentTable == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount);\r
+\r
+  if (EFI_ERROR(Status)) {\r
+    FreePool (FragmentTable);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Convert host byte order to network byte order\r
+  //\r
+  Ip6NtohHead (Head);\r
+\r
+  Status = mIpSec->Process (\r
+                     mIpSec,\r
+                     IpSb->Controller,\r
+                     IP_VERSION_6,\r
+                     (VOID *) Head,\r
+                     LastHead,\r
+                     NULL,\r
+                     0,\r
+                     (EFI_IPSEC_FRAGMENT_DATA  **) (&FragmentTable),\r
+                     &FragmentCount,\r
+                     Direction,\r
+                     &RecycleEvent\r
+                     );\r
+  //\r
+  // Convert back to host byte order\r
+  //\r
+  Ip6NtohHead (Head);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (Direction == EfiIPsecOutBound && TxWrap != NULL) {\r
+\r
+    TxWrap->IpSecRecycleSignal = RecycleEvent;\r
+    TxWrap->Packet             = NetbufFromExt (\r
+                                   FragmentTable,\r
+                                   FragmentCount,\r
+                                   IP6_MAX_HEADLEN,\r
+                                   0,\r
+                                   Ip6FreeTxToken,\r
+                                   TxWrap\r
+                                   );\r
+    if (TxWrap->Packet == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    *Netbuf = TxWrap->Packet;\r
+\r
+  } else {\r
+\r
+    IpSecWrap = AllocateZeroPool (sizeof (IP6_IPSEC_WRAP));\r
+\r
+    if (IpSecWrap == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    IpSecWrap->IpSecRecycleSignal = RecycleEvent;\r
+    IpSecWrap->Packet             = Packet;\r
+    Packet                        = NetbufFromExt (\r
+                                      FragmentTable,\r
+                                      FragmentCount,\r
+                                      IP6_MAX_HEADLEN,\r
+                                      0,\r
+                                      Ip6IpSecFree,\r
+                                      IpSecWrap\r
+                                      );\r
+\r
+    if (Packet == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    if (Direction == EfiIPsecInBound) {\r
+\r
+      PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (\r
+                                        Packet,\r
+                                        sizeof (EFI_IP6_HEADER) + ExtHdrsLen,\r
+                                        NET_BUF_HEAD\r
+                                        );\r
+      if (PacketHead == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));\r
+      Packet->Ip.Ip6 = PacketHead;\r
+\r
+      if (ExtHdrs != NULL) {\r
+        Buf = (UINT8 *) (PacketHead + 1);\r
+        CopyMem (Buf, ExtHdrs, ExtHdrsLen);\r
+      }\r
+\r
+      NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE);\r
+      CopyMem (\r
+        IP6_GET_CLIP_INFO (Packet),\r
+        IP6_GET_CLIP_INFO (IpSecWrap->Packet),\r
+        sizeof (IP6_CLIP_INFO)\r
+        );\r
+    }\r
+\r
+    *Netbuf = Packet;\r
+  }\r
+\r
+ON_EXIT:\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The IP6 input routine. It is called by the IP6_INTERFACE when an\r
+  IP6 fragment is received from MNP.\r
+\r
+  @param[in]  Packet             The IP6 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 IP6 service instance that owns the MNP.\r
+\r
+**/\r
+VOID\r
+Ip6AcceptFrame (\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN UINT32                 Flag,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_CLIP_INFO             *Info;\r
+  EFI_IP6_HEADER            *Head;\r
+  UINT16                    PayloadLen;\r
+  UINT8                     *Payload;\r
+  UINT16                    TotalLen;\r
+  UINT8                     *LastHead;\r
+  UINT32                    FormerHeadOffset;\r
+  UINT32                    UnFragmentLen;\r
+  UINT32                    ExtHdrsLen;\r
+  UINT32                    HeadLen;\r
+  BOOLEAN                   Fragmented;\r
+  IP6_FRAGMENT_HEADER       *FragmentHead;\r
+  UINT16                    FragmentOffset;\r
+  EFI_STATUS                Status;\r
+  EFI_IPv6_ADDRESS          Loopback;\r
+\r
+  IpSb = (IP6_SERVICE *) Context;\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  Payload = NULL;\r
+\r
+  //\r
+  // Check input parameters\r
+  //\r
+  if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) {\r
+    goto Drop;\r
+  }\r
+\r
+  //\r
+  // Check whether the input packet is a valid packet\r
+  //\r
+  if (Packet->TotalSize < IP6_MIN_HEADLEN) {\r
+    goto Restart;\r
+  }\r
+\r
+  //\r
+  // Get header information of the packet.\r
+  //\r
+  Head = (EFI_IP6_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+  if (Head == NULL) {\r
+    goto Restart;\r
+  }\r
+\r
+  //\r
+  // Multicast addresses must not be used as source addresses in IPv6 packets.\r
+  //\r
+  if ((Head->Version != 6) || (IP6_IS_MULTICAST (&Head->SourceAddress))) {\r
+    goto Restart;\r
+  }\r
+\r
+  //\r
+  // A packet with a destination address of loopback ::1/128 or unspecified must be dropped.\r
+  //\r
+  ZeroMem (&Loopback, sizeof (EFI_IPv6_ADDRESS));\r
+  Loopback.Addr[15] = 0x1;\r
+  if ((CompareMem (&Loopback, &Head->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) ||\r
+      (NetIp6IsUnspecifiedAddr (&Head->DestinationAddress))) {\r
+    goto Restart;\r
+  }\r
+\r
+  //\r
+  // Convert the IP header to host byte order.\r
+  //\r
+  Packet->Ip.Ip6 = Ip6NtohHead (Head);\r
+\r
+  //\r
+  // Get the per packet info.\r
+  //\r
+  Info           = IP6_GET_CLIP_INFO (Packet);\r
+  Info->LinkFlag = Flag;\r
+  Info->CastType = 0;\r
+\r
+  if (IpSb->MnpConfigData.EnablePromiscuousReceive) {\r
+    Info->CastType = Ip6Promiscuous;\r
+  }\r
+\r
+  if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {\r
+    Info->CastType = Ip6Unicast;\r
+  } else if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
+    if (Ip6FindMldEntry (IpSb, &Head->DestinationAddress) != NULL) {\r
+      Info->CastType = Ip6Multicast;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Drop the packet that is not delivered to us.\r
+  //\r
+  if (Info->CastType == 0) {\r
+    goto Restart;\r
+  }\r
+\r
+\r
+  PayloadLen = Head->PayloadLength;\r
+\r
+  Info->Start    = 0;\r
+  Info->Length   = PayloadLen;\r
+  Info->End      = Info->Start + Info->Length;\r
+  Info->HeadLen  = (UINT16) sizeof (EFI_IP6_HEADER);\r
+  Info->Status   = EFI_SUCCESS;\r
+  Info->LastFrag = FALSE;\r
+\r
+  TotalLen   = (UINT16) (PayloadLen + sizeof (EFI_IP6_HEADER));\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
+  }\r
+\r
+  if (TotalLen != Packet->TotalSize) {\r
+    goto Restart;\r
+  }\r
+\r
+  //\r
+  // Check the extension headers, if exist validate them\r
+  //\r
+  if (PayloadLen != 0) {\r
+    Payload = AllocatePool ((UINTN) PayloadLen);\r
+    if (Payload == NULL) {\r
+      goto Restart;\r
+    }\r
+\r
+    NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);\r
+  }\r
+\r
+  LastHead   = NULL;\r
+  if (!Ip6IsExtsValid (\r
+         IpSb,\r
+         Packet,\r
+         &Head->NextHeader,\r
+         Payload,\r
+         (UINT32) PayloadLen,\r
+         TRUE,\r
+         &FormerHeadOffset,\r
+         &LastHead,\r
+         &ExtHdrsLen,\r
+         &UnFragmentLen,\r
+         &Fragmented\r
+         )) {\r
+    goto Restart;\r
+  }\r
+\r
+  HeadLen        = sizeof (EFI_IP6_HEADER) + UnFragmentLen;\r
+\r
+  if (Fragmented) {\r
+    //\r
+    // Get the fragment offset from the Fragment header\r
+    //\r
+    FragmentHead = (IP6_FRAGMENT_HEADER *) NetbufGetByte (Packet, HeadLen, NULL);\r
+    if (FragmentHead == NULL) {\r
+      goto Restart;\r
+    }\r
+\r
+    FragmentOffset = NTOHS (FragmentHead->FragmentOffset);\r
+\r
+    if ((FragmentOffset & 0x1) == 0) {\r
+      Info->LastFrag = TRUE;\r
+    }\r
+\r
+    FragmentOffset &= (~0x1);\r
+\r
+    //\r
+    // This is the first fragment of the packet\r
+    //\r
+    if (FragmentOffset == 0) {\r
+      Info->NextHeader = FragmentHead->NextHeader;\r
+    }\r
+\r
+    Info->HeadLen          = (UINT16) HeadLen;\r
+    HeadLen                += sizeof (IP6_FRAGMENT_HEADER);\r
+    Info->Start            = FragmentOffset;\r
+    Info->Length           = TotalLen - (UINT16) HeadLen;\r
+    Info->End              = Info->Start + Info->Length;\r
+    Info->Id               = FragmentHead->Identification;\r
+    Info->FormerNextHeader = FormerHeadOffset;\r
+\r
+    //\r
+    // Fragments should in the unit of 8 octets long except the last one.\r
+    //\r
+    if ((Info->LastFrag == 0) && (Info->Length % 8 != 0)) {\r
+      goto Restart;\r
+    }\r
+\r
+    //\r
+    // Reassemble the packet.\r
+    //\r
+    Packet = Ip6Reassemble (&IpSb->Assemble, Packet);\r
+    if (Packet == NULL) {\r
+      goto Restart;\r
+    }\r
+\r
+    //\r
+    // Re-check the assembled packet to get the right values.\r
+    //\r
+    Head       = Packet->Ip.Ip6;\r
+    PayloadLen = Head->PayloadLength;\r
+    if (PayloadLen != 0) {\r
+      if (Payload != NULL) {\r
+        FreePool (Payload);\r
+      }\r
+\r
+      Payload = AllocatePool ((UINTN) PayloadLen);\r
+      if (Payload == NULL) {\r
+        goto Restart;\r
+      }\r
+\r
+      NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);\r
+    }\r
+\r
+    if (!Ip6IsExtsValid (\r
+           IpSb,\r
+           Packet,\r
+           &Head->NextHeader,\r
+           Payload,\r
+           (UINT32) PayloadLen,\r
+           TRUE,\r
+           NULL,\r
+           &LastHead,\r
+           &ExtHdrsLen,\r
+           &UnFragmentLen,\r
+           &Fragmented\r
+           )) {\r
+      goto Restart;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Trim the head off, after this point, the packet is headless.\r
+  // and Packet->TotalLen == Info->Length.\r
+  //\r
+  NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE);\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 = Ip6IpSecProcessPacket (\r
+             IpSb,\r
+             Head,\r
+             LastHead, // need get the lasthead value for input\r
+             &Packet,\r
+             NULL,\r
+             0,\r
+             EfiIPsecInBound,\r
+             NULL\r
+             );\r
+\r
+  if (EFI_ERROR(Status)) {\r
+    goto Restart;\r
+  }\r
+\r
+  //\r
+  // TODO: may check the last head again, the same as the output routine\r
+  //\r
+\r
+  //\r
+  // Packet may have been changed. The ownership of the packet\r
+  // is transfered to the packet process logic.\r
+  //\r
+  Head  = Packet->Ip.Ip6;\r
+  IP6_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;\r
+\r
+  switch (*LastHead) {\r
+  case IP6_ICMP:\r
+    Ip6IcmpHandle (IpSb, Head, Packet);\r
+    break;\r
+  default:\r
+    Ip6Demultiplex (IpSb, Head, Packet);\r
+  }\r
+\r
+  Packet = NULL;\r
+\r
+  //\r
+  // Dispatch the DPCs queued by the NotifyFunction of the rx token's events\r
+  // which are signaled with received data.\r
+  //\r
+  DispatchDpc ();\r
+\r
+Restart:\r
+  if (Payload != NULL) {\r
+    FreePool (Payload);\r
+  }\r
+\r
+  Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);\r
+\r
+Drop:\r
+  if (Packet != NULL) {\r
+    NetbufFree (Packet);\r
+  }\r
+\r
+  return ;\r
+}\r
+\r
+/**\r
+  Initialize an already allocated assemble table. This is generally\r
+  the assemble table embedded in the IP6 service instance.\r
+\r
+  @param[in, out]  Table    The assemble table to initialize.\r
+\r
+**/\r
+VOID\r
+Ip6CreateAssembleTable (\r
+  IN OUT IP6_ASSEMBLE_TABLE *Table\r
+  )\r
+{\r
+  UINT32                    Index;\r
+\r
+  for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {\r
+    InitializeListHead (&Table->Bucket[Index]);\r
+  }\r
+}\r
+\r
+/**\r
+  Clean up the assemble table by removing all of the fragments\r
+  and assemble entries.\r
+\r
+  @param[in, out]  Table    The assemble table to clean up.\r
+\r
+**/\r
+VOID\r
+Ip6CleanAssembleTable (\r
+  IN OUT IP6_ASSEMBLE_TABLE *Table\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_ASSEMBLE_ENTRY        *Assemble;\r
+  UINT32                    Index;\r
+\r
+  for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {\r
+      Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);\r
+\r
+      RemoveEntryList (Entry);\r
+      Ip6FreeAssembleEntry (Assemble);\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  The signal handle of IP6's recycle event. It is called back\r
+  when the upper layer releases the packet.\r
+\r
+  @param[in]  Event         The IP6's recycle event.\r
+  @param[in]  Context       The context of the handle, which is a IP6_RXDATA_WRAP.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnRecyclePacket (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP6_RXDATA_WRAP           *Wrap;\r
+\r
+  Wrap = (IP6_RXDATA_WRAP *) Context;\r
+\r
+  EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock);\r
+  RemoveEntryList (&Wrap->Link);\r
+  EfiReleaseLock (&Wrap->IpInstance->RecycleLock);\r
+\r
+  ASSERT (!NET_BUF_SHARED (Wrap->Packet));\r
+  NetbufFree (Wrap->Packet);\r
+\r
+  gBS->CloseEvent (Wrap->RxData.RecycleSignal);\r
+  FreePool (Wrap);\r
+}\r
+\r
+/**\r
+  Wrap the received packet to a IP6_RXDATA_WRAP, which will be\r
+  delivered to the upper layer. Each IP6 child that accepts the\r
+  packet will get a not-shared copy of the packet which is wrapped\r
+  in the IP6_RXDATA_WRAP. The IP6_RXDATA_WRAP->RxData is passed\r
+  to the upper layer. The upper layer will signal the recycle event in\r
+  it when it is done with the packet.\r
+\r
+  @param[in]  IpInstance    The IP6 child to receive the packet.\r
+  @param[in]  Packet        The packet to deliver up.\r
+\r
+  @return NULL if it failed to wrap the packet; otherwise, the wrapper.\r
+\r
+**/\r
+IP6_RXDATA_WRAP *\r
+Ip6WrapRxData (\r
+  IN IP6_PROTOCOL           *IpInstance,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_RXDATA_WRAP           *Wrap;\r
+  EFI_IP6_RECEIVE_DATA      *RxData;\r
+  EFI_STATUS                Status;\r
+\r
+  Wrap = AllocatePool (IP6_RXDATA_WRAP_SIZE (Packet->BlockOpNum));\r
+\r
+  if (Wrap == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  InitializeListHead (&Wrap->Link);\r
+\r
+  Wrap->IpInstance  = IpInstance;\r
+  Wrap->Packet      = Packet;\r
+  RxData            = &Wrap->RxData;\r
+\r
+  ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  Ip6OnRecyclePacket,\r
+                  Wrap,\r
+                  &RxData->RecycleSignal\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Wrap);\r
+    return NULL;\r
+  }\r
+\r
+  ASSERT (Packet->Ip.Ip6 != NULL);\r
+\r
+  //\r
+  // The application expects a network byte order header.\r
+  //\r
+  RxData->HeaderLength  = sizeof (EFI_IP6_HEADER);\r
+  RxData->Header        = (EFI_IP6_HEADER *) Ip6NtohHead (Packet->Ip.Ip6);\r
+  RxData->DataLength    = Packet->TotalSize;\r
+\r
+  //\r
+  // Build the fragment table to be delivered up.\r
+  //\r
+  RxData->FragmentCount = Packet->BlockOpNum;\r
+  NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);\r
+\r
+  return Wrap;\r
+}\r
+\r
+/**\r
+  Check whether this IP child accepts the packet.\r
+\r
+  @param[in]  IpInstance    The IP child to check.\r
+  @param[in]  Head          The IP header of the packet.\r
+  @param[in]  Packet        The data of the packet.\r
+\r
+  @retval     TRUE          The child wants to receive the packet.\r
+  @retval     FALSE         The child does not want to receive the packet.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6InstanceFrameAcceptable (\r
+  IN IP6_PROTOCOL           *IpInstance,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_ERROR_HEAD       Icmp;\r
+  EFI_IP6_CONFIG_DATA       *Config;\r
+  IP6_CLIP_INFO             *Info;\r
+  UINT8                     *Proto;\r
+  UINT32                    Index;\r
+  UINT8                     *ExtHdrs;\r
+  UINT16                    ErrMsgPayloadLen;\r
+  UINT8                     *ErrMsgPayload;\r
+\r
+  Config = &IpInstance->ConfigData;\r
+  Proto  = NULL;\r
+\r
+  //\r
+  // Dirty trick for the Tiano UEFI network stack implmentation. If\r
+  // ReceiveTimeout == -1, the receive of the packet for this instance\r
+  // is disabled. The UEFI spec don't have such captibility. We add\r
+  // this to improve the performance because IP will make a copy of\r
+  // the received packet for each accepting instance. Some IP instances\r
+  // used by UDP/TCP only send packets, they don't wants to receive.\r
+  //\r
+  if (Config->ReceiveTimeout == (UINT32)(-1)) {\r
+    return FALSE;\r
+  }\r
+\r
+  if (Config->AcceptPromiscuous) {\r
+    return TRUE;\r
+  }\r
+\r
+  //\r
+  // Check whether the protocol is acceptable.\r
+  //\r
+  ExtHdrs = NetbufGetByte (Packet, 0, NULL);\r
+\r
+  if (!Ip6IsExtsValid (\r
+         IpInstance->Service,\r
+         Packet,\r
+         &Head->NextHeader,\r
+         ExtHdrs,\r
+         (UINT32) Head->PayloadLength,\r
+         TRUE,\r
+         NULL,\r
+         &Proto,\r
+         NULL,\r
+         NULL,\r
+         NULL\r
+         )) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // The upper layer driver may want to receive the ICMPv6 error packet\r
+  // invoked by its packet, like UDP.\r
+  //\r
+  if ((*Proto == IP6_ICMP) && (!Config->AcceptAnyProtocol) && (*Proto != Config->DefaultProtocol)) {\r
+    NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+\r
+    if (Icmp.Head.Type <= ICMP_V6_ERROR_MAX) {\r
+      if (!Config->AcceptIcmpErrors) {\r
+        return FALSE;\r
+      }\r
+\r
+      //\r
+      // Get the protocol of the invoking packet of ICMPv6 error packet.\r
+      //\r
+      ErrMsgPayloadLen = NTOHS (Icmp.IpHead.PayloadLength);\r
+      ErrMsgPayload    = NetbufGetByte (Packet, sizeof (Icmp), NULL);\r
+\r
+      if (!Ip6IsExtsValid (\r
+             NULL,\r
+             NULL,\r
+             &Icmp.IpHead.NextHeader,\r
+             ErrMsgPayload,\r
+             ErrMsgPayloadLen,\r
+             TRUE,\r
+             NULL,\r
+             &Proto,\r
+             NULL,\r
+             NULL,\r
+             NULL\r
+             )) {\r
+        return FALSE;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Match the protocol\r
+  //\r
+  if (!Config->AcceptAnyProtocol && (*Proto != Config->DefaultProtocol)) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check for broadcast, the caller has computed the packet's\r
+  // cast type for this child's interface.\r
+  //\r
+  Info = IP6_GET_CLIP_INFO (Packet);\r
+\r
+  //\r
+  // If it is a multicast packet, check whether we are in the group.\r
+  //\r
+  if (Info->CastType == Ip6Multicast) {\r
+    //\r
+    // Receive the multicast if the instance wants to receive all packets.\r
+    //\r
+    if (NetIp6IsUnspecifiedAddr (&IpInstance->ConfigData.StationAddress)) {\r
+      return TRUE;\r
+    }\r
+\r
+    for (Index = 0; Index < IpInstance->GroupCount; Index++) {\r
+      if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, &Head->DestinationAddress)) {\r
+        break;\r
+      }\r
+    }\r
+\r
+    return (BOOLEAN)(Index < IpInstance->GroupCount);\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Enqueue a shared copy of the packet to the IP6 child if the\r
+  packet is acceptable to it. Here the data of the packet is\r
+  shared, but the net buffer isn't.\r
+\r
+  @param  IpInstance             The IP6 child to enqueue the packet to.\r
+  @param  Head                   The IP header of the received packet.\r
+  @param  Packet                 The data of the received packet.\r
+\r
+  @retval EFI_NOT_STARTED        The IP child hasn't been configured.\r
+  @retval EFI_INVALID_PARAMETER  The child doesn't want to receive the packet.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources\r
+  @retval EFI_SUCCESS            A shared copy the packet is enqueued to the child.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InstanceEnquePacket (\r
+  IN IP6_PROTOCOL           *IpInstance,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_CLIP_INFO             *Info;\r
+  NET_BUF                   *Clone;\r
+\r
+  //\r
+  // Check whether the packet is acceptable to this instance.\r
+  //\r
+  if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (!Ip6InstanceFrameAcceptable (IpInstance, Head, Packet)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Enque a shared copy of the packet.\r
+  //\r
+  Clone = NetbufClone (Packet);\r
+\r
+  if (Clone == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Set the receive time out for the assembled packet. If it expires,\r
+  // packet will be removed from the queue.\r
+  //\r
+  Info        = IP6_GET_CLIP_INFO (Clone);\r
+  Info->Life  = IP6_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);\r
+\r
+  InsertTailList (&IpInstance->Received, &Clone->List);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Deliver the received packets to the upper layer if there are both received\r
+  requests and enqueued packets. If the enqueued packet is shared, it will\r
+  duplicate it to a non-shared packet, release the shared packet, then\r
+  deliver the non-shared packet up.\r
+\r
+  @param[in]  IpInstance         The IP child to deliver the packet up.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to deliver the\r
+                                 packets.\r
+  @retval EFI_SUCCESS            All the enqueued packets that can be delivered\r
+                                 are delivered up.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InstanceDeliverPacket (\r
+  IN IP6_PROTOCOL           *IpInstance\r
+  )\r
+{\r
+  EFI_IP6_COMPLETION_TOKEN  *Token;\r
+  IP6_RXDATA_WRAP           *Wrap;\r
+  NET_BUF                   *Packet;\r
+  NET_BUF                   *Dup;\r
+  UINT8                     *Head;\r
+\r
+  //\r
+  // Deliver a packet if there are both a packet and a receive token.\r
+  //\r
+  while (!IsListEmpty (&IpInstance->Received) && !NetMapIsEmpty (&IpInstance->RxTokens)) {\r
+\r
+    Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);\r
+\r
+    if (!NET_BUF_SHARED (Packet)) {\r
+      //\r
+      // If this is the only instance that wants the packet, wrap it up.\r
+      //\r
+      Wrap = Ip6WrapRxData (IpInstance, Packet);\r
+\r
+      if (Wrap == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      RemoveEntryList (&Packet->List);\r
+\r
+    } else {\r
+      //\r
+      // Create a duplicated packet if this packet is shared\r
+      //\r
+      Dup = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));\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, sizeof (EFI_IP6_HEADER), NET_BUF_HEAD);\r
+      ASSERT (Head != NULL);\r
+      Dup->Ip.Ip6 = (EFI_IP6_HEADER *) Head;\r
+\r
+      CopyMem (Head, Packet->Ip.Ip6, sizeof (EFI_IP6_HEADER));\r
+      NetbufTrim (Dup, sizeof (EFI_IP6_HEADER), TRUE);\r
+\r
+      Wrap = Ip6WrapRxData (IpInstance, Dup);\r
+\r
+      if (Wrap == NULL) {\r
+        NetbufFree (Dup);\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      RemoveEntryList (&Packet->List);\r
+      NetbufFree (Packet);\r
+\r
+      Packet = Dup;\r
+    }\r
+\r
+    //\r
+    // Insert it into the delivered packet, then get a user's\r
+    // receive token, pass the wrapped packet up.\r
+    //\r
+    EfiAcquireLockOrFail (&IpInstance->RecycleLock);\r
+    InsertHeadList (&IpInstance->Delivered, &Wrap->Link);\r
+    EfiReleaseLock (&IpInstance->RecycleLock);\r
+\r
+    Token                = NetMapRemoveHead (&IpInstance->RxTokens, NULL);\r
+    Token->Status        = IP6_GET_CLIP_INFO (Packet)->Status;\r
+    Token->Packet.RxData = &Wrap->RxData;\r
+\r
+    gBS->SignalEvent (Token->Event);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Enqueue a received packet to all the IP children that share\r
+  the same interface.\r
+\r
+  @param[in]  IpSb          The IP6 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
+\r
+  @return The number of the IP6 children that accepts the packet.\r
+\r
+**/\r
+INTN\r
+Ip6InterfaceEnquePacket (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet,\r
+  IN IP6_INTERFACE          *IpIf\r
+  )\r
+{\r
+  IP6_PROTOCOL              *IpInstance;\r
+  IP6_CLIP_INFO             *Info;\r
+  LIST_ENTRY                *Entry;\r
+  INTN                      Enqueued;\r
+  INTN                      LocalType;\r
+  INTN                      SavedType;\r
+\r
+  //\r
+  // First, check that the packet is acceptable to this interface\r
+  // and find the local cast type for the interface.\r
+  //\r
+  LocalType = 0;\r
+  Info      = IP6_GET_CLIP_INFO (Packet);\r
+\r
+  if (IpIf->PromiscRecv) {\r
+    LocalType = Ip6Promiscuous;\r
+  } else {\r
+    LocalType = Info->CastType;\r
+  }\r
+\r
+  //\r
+  // Iterate through the ip instances on the interface, enqueue\r
+  // the packet if filter passed. Save the original cast type,\r
+  // and pass the local cast type to the IP children on the\r
+  // interface. The global cast type will be restored later.\r
+  //\r
+  SavedType       = Info->CastType;\r
+  Info->CastType  = (UINT32) LocalType;\r
+\r
+  Enqueued        = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {\r
+    IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);\r
+    NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);\r
+\r
+    if (Ip6InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {\r
+      Enqueued++;\r
+    }\r
+  }\r
+\r
+  Info->CastType = (UINT32) SavedType;\r
+  return Enqueued;\r
+}\r
+\r
+/**\r
+  Deliver the packet for each IP6 child on the interface.\r
+\r
+  @param[in]  IpSb          The IP6 service instance that received the packet.\r
+  @param[in]  IpIf          The IP6 interface to deliver the packet.\r
+\r
+**/\r
+VOID\r
+Ip6InterfaceDeliverPacket (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_INTERFACE          *IpIf\r
+  )\r
+{\r
+  IP6_PROTOCOL              *IpInstance;\r
+  LIST_ENTRY                *Entry;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {\r
+    IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);\r
+    Ip6InstanceDeliverPacket (IpInstance);\r
+  }\r
+}\r
+\r
+/**\r
+  De-multiplex the packet. the packet delivery is processed in two\r
+  passes. The first pass will enqueue a shared copy of the packet\r
+  to each IP6 child that accepts the packet. The second pass will\r
+  deliver a non-shared copy of the packet to each IP6 child that\r
+  has pending receive requests. Data is copied if more than one\r
+  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 IP6 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
+\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
+**/\r
+EFI_STATUS\r
+Ip6Demultiplex (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+\r
+  LIST_ENTRY                *Entry;\r
+  IP6_INTERFACE             *IpIf;\r
+  INTN                      Enqueued;\r
+\r
+  //\r
+  // Two pass delivery: first, enque a shared copy of the packet\r
+  // to each instance that accept the packet.\r
+  //\r
+  Enqueued = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+    if (IpIf->Configured) {\r
+      Enqueued += Ip6InterfaceEnquePacket (IpSb, Head, Packet, IpIf);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Second: deliver a duplicate of the packet to each instance.\r
+  // Release the local reference first, so that the last instance\r
+  // getting the packet will not copy the data.\r
+  //\r
+  NetbufFree (Packet);\r
+  Packet = NULL;\r
+\r
+  if (Enqueued == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+    if (IpIf->Configured) {\r
+      Ip6InterfaceDeliverPacket (IpSb, IpIf);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Decrease the life of the transmitted packets. If it is\r
+  decreased to zero, cancel the packet. This function is\r
+  called by Ip6packetTimerTicking that provides timeout for both the\r
+  received-but-not-delivered and transmitted-but-not-recycle\r
+  packets.\r
+\r
+  @param[in]  Map           The IP6 child's transmit map.\r
+  @param[in]  Item          Current transmitted packet.\r
+  @param[in]  Context       Not used.\r
+\r
+  @retval EFI_SUCCESS       Always returns EFI_SUCCESS.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6SentPacketTicking (\r
+  IN NET_MAP                *Map,\r
+  IN NET_MAP_ITEM           *Item,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP6_TXTOKEN_WRAP          *Wrap;\r
+\r
+  Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;\r
+  ASSERT (Wrap != NULL);\r
+\r
+  if ((Wrap->Life > 0) && (--Wrap->Life == 0)) {\r
+    Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Timeout the fragments, and the enqueued, and transmitted packets.\r
+\r
+  @param[in]  IpSb          The IP6 service instance to timeout.\r
+\r
+**/\r
+VOID\r
+Ip6PacketTimerTicking (\r
+  IN IP6_SERVICE            *IpSb\r
+  )\r
+{\r
+  LIST_ENTRY                *InstanceEntry;\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_PROTOCOL              *IpInstance;\r
+  IP6_ASSEMBLE_ENTRY        *Assemble;\r
+  NET_BUF                   *Packet;\r
+  IP6_CLIP_INFO             *Info;\r
+  UINT32                    Index;\r
+\r
+  //\r
+  // First, time out the fragments. The packet's life is counting down\r
+  // once the first-arriving fragment of that packet was received.\r
+  //\r
+  for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &(IpSb->Assemble.Bucket[Index])) {\r
+      Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);\r
+\r
+      if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {\r
+        //\r
+        // If the first fragment (the one with a Fragment Offset of zero)\r
+        // has been received, an ICMP Time Exceeded - Fragment Reassembly\r
+        // Time Exceeded message should be sent to the source of that fragment.\r
+        //\r
+        if ((Assemble->Packet != NULL) &&\r
+            !IP6_IS_MULTICAST (&Assemble->Head->DestinationAddress)) {\r
+          Ip6SendIcmpError (\r
+            IpSb,\r
+            Assemble->Packet,\r
+            NULL,\r
+            &Assemble->Head->SourceAddress,\r
+            ICMP_V6_TIME_EXCEEDED,\r
+            ICMP_V6_TIMEOUT_REASSEMBLE,\r
+            NULL\r
+            );\r
+        }\r
+\r
+        //\r
+        // If reassembly of a packet is not completed within 60 seconds of\r
+        // the reception of the first-arriving fragment of that packet, the\r
+        // reassembly must be abandoned and all the fragments that have been\r
+        // received for that packet must be discarded.\r
+        //\r
+        RemoveEntryList (Entry);\r
+        Ip6FreeAssembleEntry (Assemble);\r
+      }\r
+    }\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {\r
+    IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP6_PROTOCOL, Link);\r
+\r
+    //\r
+    // Second, time out the assembled packets enqueued on each IP child.\r
+    //\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {\r
+      Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+      Info   = IP6_GET_CLIP_INFO (Packet);\r
+\r
+      if ((Info->Life > 0) && (--Info->Life == 0)) {\r
+        RemoveEntryList (Entry);\r
+        NetbufFree (Packet);\r
+      }\r
+    }\r
+\r
+    //\r
+    // Third: time out the transmitted packets.\r
+    //\r
+    NetMapIterate (&IpInstance->TxTokens, Ip6SentPacketTicking, NULL);\r
+  }\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Input.h b/NetworkPkg/Ip6Dxe/Ip6Input.h
new file mode 100644 (file)
index 0000000..8594896
--- /dev/null
@@ -0,0 +1,235 @@
+/** @file\r
+  IP6 internal functions and definitions to process the incoming packets.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_INPUT_H__\r
+#define __EFI_IP6_INPUT_H__\r
+\r
+#define IP6_MIN_HEADLEN       40\r
+#define IP6_MAX_HEADLEN       120\r
+///\r
+/// 8(ESP header) + 16(max IV) + 16(max padding) + 2(ESP tail) + 12(max ICV) = 54\r
+///\r
+#define IP6_MAX_IPSEC_HEADLEN 54\r
+\r
+\r
+#define IP6_ASSEMLE_HASH_SIZE 127\r
+///\r
+/// Lift time in seconds.\r
+///\r
+#define IP6_FRAGMENT_LIFE     60\r
+#define IP6_MAX_PACKET_SIZE   65535\r
+\r
+\r
+#define IP6_GET_CLIP_INFO(Packet) ((IP6_CLIP_INFO *) ((Packet)->ProtoData))\r
+\r
+#define IP6_ASSEMBLE_HASH(Dst, Src, Id)  \\r
+          ((*((UINT32 *) (Dst)) + *((UINT32 *) (Src)) + (Id)) % IP6_ASSEMLE_HASH_SIZE)\r
+\r
+#define IP6_RXDATA_WRAP_SIZE(NumFrag) \\r
+          (sizeof (IP6_RXDATA_WRAP) + sizeof (EFI_IP6_FRAGMENT_DATA) * ((NumFrag) - 1))\r
+\r
+//\r
+// Per packet information for input process. LinkFlag specifies whether\r
+// the packet is received as Link layer unicast, multicast or broadcast.\r
+// The CastType is the IP layer cast type, such as IP multicast or unicast.\r
+// Start, End and Length are staffs used to assemble the packets. Start\r
+// is the sequence number of the first byte of data in the packet. Length\r
+// is the number of bytes of data. End = Start + Length, that is, the\r
+// sequence number of last byte + 1. Each assembled packet has a count down\r
+// life. If it isn't consumed before Life reaches zero, the packet is released.\r
+//\r
+typedef struct {\r
+  UINT32                    LinkFlag;\r
+  INT32                     CastType;\r
+  INT32                     Start;\r
+  INT32                     End;\r
+  INT32                     Length;\r
+  UINT32                    Life;\r
+  EFI_STATUS                Status;\r
+  UINT32                    Id;\r
+  UINT16                    HeadLen;\r
+  UINT8                     NextHeader;\r
+  UINT8                     LastFrag;\r
+  UINT32                    FormerNextHeader;\r
+} IP6_CLIP_INFO;\r
+\r
+//\r
+// Structure used to assemble IP packets.\r
+//\r
+typedef struct {\r
+  LIST_ENTRY                Link;\r
+  LIST_ENTRY                Fragments;  // List of all the fragments of this packet\r
+\r
+  //\r
+  // Identity of one IP6 packet. Each fragment of a packet has\r
+  // the same (Dst, Src, Id).\r
+  //\r
+  EFI_IPv6_ADDRESS          Dst;\r
+  EFI_IPv6_ADDRESS          Src;\r
+  UINT32                    Id;\r
+\r
+  UINT32                    TotalLen;\r
+  UINT32                    CurLen;\r
+  UINT32                    Life;       // Count down life for the packet.\r
+\r
+  EFI_IP6_HEADER            *Head;      // IP head of the first fragment\r
+  IP6_CLIP_INFO             *Info;      // Per packet information of the first fragment\r
+  NET_BUF                   *Packet;    // The first fragment of the packet\r
+} IP6_ASSEMBLE_ENTRY;\r
+\r
+//\r
+// Each Ip service instance has an assemble table to reassemble\r
+// the packets before delivery to its children. It is organized\r
+// as hash table.\r
+//\r
+typedef struct {\r
+  LIST_ENTRY  Bucket[IP6_ASSEMLE_HASH_SIZE];\r
+} IP6_ASSEMBLE_TABLE;\r
+\r
+/**\r
+  The IP6 input routine. It is called by the IP6_INTERFACE when an\r
+  IP6 fragment is received from MNP.\r
+\r
+  @param[in]  Packet             The IP6 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 IP6 service instance that own the MNP.\r
+\r
+**/\r
+VOID\r
+Ip6AcceptFrame (\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN UINT32                 Flag,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+/**\r
+  Deliver the received packets to upper layer if there are both received\r
+  requests and enqueued packets. If the enqueued packet is shared, it will\r
+  duplicate it to a non-shared packet, release the shared packet, then\r
+  deliver the non-shared packet up.\r
+\r
+  @param[in]  IpInstance         The IP child to deliver the packet up.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to deliver the\r
+                                 packets.\r
+  @retval EFI_SUCCESS            All the enqueued packets that can be delivered\r
+                                 are delivered up.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InstanceDeliverPacket (\r
+  IN IP6_PROTOCOL           *IpInstance\r
+  );\r
+\r
+/**\r
+  The work function to locate IPsec protocol to process the inbound or\r
+  outbound IP packets. The process routine handls the packet with the following\r
+  actions: bypass the packet, discard the packet, or protect the packet.\r
+\r
+  @param[in]       IpSb          The IP6 service instance.\r
+  @param[in]       Head          The caller supplied IP6 header.\r
+  @param[in, out]  LastHead      The next header field of last IP header.\r
+  @param[in, out]  Netbuf        The IP6 packet to be processed by IPsec.\r
+  @param[in]       ExtHdrs       The caller supplied options.\r
+  @param[in]       ExtHdrsLen    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_OUT_OF_RESOURCES   There are not suffcient resources to complete the operation.\r
+  @retval EFI_BUFFER_TOO_SMALL   The number of non-empty block is bigger than the\r
+                                 number of input data blocks when building a fragment table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IpSecProcessPacket (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN OUT UINT8              *LastHead,\r
+  IN OUT NET_BUF            **Netbuf,\r
+  IN VOID                   *ExtHdrs,\r
+  IN UINT32                 ExtHdrsLen,\r
+  IN EFI_IPSEC_TRAFFIC_DIR  Direction,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+/**\r
+  Initialize an already allocated assemble table. This is generally\r
+  the assemble table embedded in the IP6 service instance.\r
+\r
+  @param[in, out]  Table    The assemble table to initialize.\r
+\r
+**/\r
+VOID\r
+Ip6CreateAssembleTable (\r
+  IN OUT IP6_ASSEMBLE_TABLE *Table\r
+  );\r
+\r
+/**\r
+  Clean up the assemble table: remove all the fragments\r
+  and assemble entries.\r
+\r
+  @param[in, out]  Table    The assemble table to clean up.\r
+\r
+**/\r
+VOID\r
+Ip6CleanAssembleTable (\r
+  IN OUT IP6_ASSEMBLE_TABLE *Table\r
+  );\r
+\r
+/**\r
+  Demultiple the packet. the packet delivery is processed in two\r
+  passes. The first pass will enque a shared copy of the packet\r
+  to each IP6 child that accepts the packet. The second pass will\r
+  deliver a non-shared copy of the packet to each IP6 child that\r
+  has pending receive requests. Data is copied if more than one\r
+  child wants to consume the packet bacause each IP child need\r
+  its own copy of the packet to make changes.\r
+\r
+  @param[in]  IpSb          The IP6 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
+\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
+**/\r
+EFI_STATUS\r
+Ip6Demultiplex (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  );\r
+\r
+/**\r
+  Timeout the fragmented, enqueued, and transmitted packets.\r
+\r
+  @param[in]  IpSb          The IP6 service instance to timeout.\r
+\r
+**/\r
+VOID\r
+Ip6PacketTimerTicking (\r
+  IN IP6_SERVICE            *IpSb\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Mld.c b/NetworkPkg/Ip6Dxe/Ip6Mld.c
new file mode 100644 (file)
index 0000000..4a418fa
--- /dev/null
@@ -0,0 +1,908 @@
+/** @file\r
+  Multicast Listener Discovery support routines.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+  Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data.\r
+\r
+  @param[in, out]  IpSb          Points to IP6 service binding instance.\r
+  @param[in]       MulticastAddr The IPv6 multicast address to be recorded.\r
+  @param[in]       DelayTimer    The maximum allowed delay before sending a responding\r
+                                 report, in units of milliseconds.\r
+  @return The created IP6_ML_GROUP list entry or NULL.\r
+\r
+**/\r
+IP6_MLD_GROUP *\r
+Ip6CreateMldEntry (\r
+  IN OUT IP6_SERVICE        *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *MulticastAddr,\r
+  IN UINT32                 DelayTimer\r
+  )\r
+{\r
+  IP6_MLD_GROUP             *Entry;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
+\r
+  Entry = AllocatePool (sizeof (IP6_MLD_GROUP));\r
+  if (Entry != NULL) {\r
+    Entry->RefCnt     = 1;\r
+    Entry->DelayTimer = DelayTimer;\r
+    Entry->SendByUs   = FALSE;\r
+    IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr);\r
+    InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link);\r
+  }\r
+\r
+  return Entry;\r
+}\r
+\r
+/**\r
+  Search a IP6_MLD_GROUP list entry node from a list array.\r
+\r
+  @param[in]       IpSb          Points to IP6 service binding instance.\r
+  @param[in]       MulticastAddr The IPv6 multicast address to be searched.\r
+\r
+  @return The found IP6_ML_GROUP list entry or NULL.\r
+\r
+**/\r
+IP6_MLD_GROUP *\r
+Ip6FindMldEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *MulticastAddr\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  IP6_MLD_GROUP             *Group;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {\r
+    Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
+    if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) {\r
+      return Group;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Count the number of IP6 multicast groups that are mapped to the\r
+  same MAC address. Several IP6 multicast address may be mapped to\r
+  the same MAC address.\r
+\r
+  @param[in]  MldCtrl              The MLD control block to search in.\r
+  @param[in]  Mac                  The MAC address to search.\r
+\r
+  @return The number of the IP6 multicast group that mapped to the same\r
+          multicast group Mac.\r
+\r
+**/\r
+INTN\r
+Ip6FindMac (\r
+  IN IP6_MLD_SERVICE_DATA   *MldCtrl,\r
+  IN EFI_MAC_ADDRESS        *Mac\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  IP6_MLD_GROUP             *Group;\r
+  INTN                      Count;\r
+\r
+  Count = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) {\r
+    Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
+\r
+    if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {\r
+      Count++;\r
+    }\r
+  }\r
+\r
+  return Count;\r
+}\r
+\r
+/**\r
+  Generate MLD report message and send it out to MulticastAddr.\r
+\r
+  @param[in]  IpSb               The IP service to send the packet.\r
+  @param[in]  Interface          The IP interface to send the packet.\r
+                                 If NULL, a system interface will be selected.\r
+  @param[in]  MulticastAddr      The specific IPv6 multicast address to which\r
+                                 the message sender is listening.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   There are not sufficient resources to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The MLD report message was successfully sent out.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendMldReport (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_INTERFACE          *Interface OPTIONAL,\r
+  IN EFI_IPv6_ADDRESS       *MulticastAddr\r
+  )\r
+{\r
+  IP6_MLD_HEAD              *MldHead;\r
+  NET_BUF                   *Packet;\r
+  EFI_IP6_HEADER            Head;\r
+  UINT16                    PayloadLen;\r
+  UINTN                     OptionLen;\r
+  UINT8                     *Options;\r
+  EFI_STATUS                Status;\r
+  UINT16                    HeadChecksum;\r
+  UINT16                    PseudoChecksum;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
+\r
+  //\r
+  // Generate the packet to be sent\r
+  // IPv6 basic header + Hop by Hop option + MLD message\r
+  //\r
+\r
+  OptionLen = 0;\r
+  Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);\r
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+  PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));\r
+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Create the basic IPv6 header.\r
+  // RFC3590: Use link-local address as source address if it is available,\r
+  // otherwise use the unspecified address.\r
+  //\r
+  Head.FlowLabelL     = 0;\r
+  Head.FlowLabelH     = 0;\r
+  Head.PayloadLength  = HTONS (PayloadLen);\r
+  Head.NextHeader     = IP6_HOP_BY_HOP;\r
+  Head.HopLimit       = 1;\r
+  IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr);\r
+\r
+  //\r
+  // If Link-Local address is not ready, we use unspecified address.\r
+  //\r
+  IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);\r
+\r
+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+  //\r
+  // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header\r
+  //\r
+  Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);\r
+  ASSERT (Options != NULL);\r
+  Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);\r
+  if (EFI_ERROR (Status)) {\r
+    NetbufFree (Packet);\r
+    Packet = NULL;\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Fill in MLD message - Report\r
+  //\r
+  MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);\r
+  ASSERT (MldHead != NULL);\r
+  ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));\r
+  MldHead->Head.Type = ICMP_V6_LISTENER_REPORT;\r
+  MldHead->Head.Code = 0;\r
+  IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);\r
+\r
+  HeadChecksum   = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));\r
+  PseudoChecksum = NetIp6PseudoHeadChecksum (\r
+                     &Head.SourceAddress,\r
+                     &Head.DestinationAddress,\r
+                     IP6_ICMP,\r
+                     sizeof (IP6_MLD_HEAD)\r
+                     );\r
+\r
+  MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);\r
+\r
+  //\r
+  // Transmit the packet\r
+  //\r
+  return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+  Generate MLD Done message and send it out to MulticastAddr.\r
+\r
+  @param[in]  IpSb               The IP service to send the packet.\r
+  @param[in]  MulticastAddr      The specific IPv6 multicast address to which\r
+                                 the message sender is ceasing to listen.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   There are not sufficient resources to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The MLD report message was successfully sent out.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendMldDone (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *MulticastAddr\r
+  )\r
+{\r
+  IP6_MLD_HEAD              *MldHead;\r
+  NET_BUF                   *Packet;\r
+  EFI_IP6_HEADER            Head;\r
+  UINT16                    PayloadLen;\r
+  UINTN                     OptionLen;\r
+  UINT8                     *Options;\r
+  EFI_STATUS                Status;\r
+  EFI_IPv6_ADDRESS          Destination;\r
+  UINT16                    HeadChecksum;\r
+  UINT16                    PseudoChecksum;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
+\r
+  //\r
+  // Generate the packet to be sent\r
+  // IPv6 basic header + Hop by Hop option + MLD message\r
+  //\r
+\r
+  OptionLen = 0;\r
+  Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);\r
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+  PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));\r
+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Create the basic IPv6 header.\r
+  //\r
+  Head.FlowLabelL     = 0;\r
+  Head.FlowLabelH     = 0;\r
+  Head.PayloadLength  = HTONS (PayloadLen);\r
+  Head.NextHeader     = IP6_HOP_BY_HOP;\r
+  Head.HopLimit       = 1;\r
+\r
+  //\r
+  // If Link-Local address is not ready, we use unspecified address.\r
+  //\r
+  IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);\r
+\r
+  Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination);\r
+  IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination);\r
+\r
+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+  //\r
+  // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header\r
+  //\r
+  Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);\r
+  ASSERT (Options != NULL);\r
+  Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);\r
+  if (EFI_ERROR (Status)) {\r
+    NetbufFree (Packet);\r
+    Packet = NULL;\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Fill in MLD message - Done\r
+  //\r
+  MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);\r
+  ASSERT (MldHead != NULL);\r
+  ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));\r
+  MldHead->Head.Type = ICMP_V6_LISTENER_DONE;\r
+  MldHead->Head.Code = 0;\r
+  IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);\r
+\r
+  HeadChecksum   = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));\r
+  PseudoChecksum = NetIp6PseudoHeadChecksum (\r
+                     &Head.SourceAddress,\r
+                     &Head.DestinationAddress,\r
+                     IP6_ICMP,\r
+                     sizeof (IP6_MLD_HEAD)\r
+                     );\r
+\r
+  MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);\r
+\r
+  //\r
+  // Transmit the packet\r
+  //\r
+  return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+  Init the MLD data of the IP6 service instance. Configure\r
+  MNP to receive ALL SYSTEM multicast.\r
+\r
+  @param[in]  IpSb              The IP6 service whose MLD is to be initialized.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  There are not sufficient resourcet to complete the\r
+                                operation.\r
+  @retval EFI_SUCCESS           The MLD module successfully initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InitMld (\r
+  IN IP6_SERVICE            *IpSb\r
+  )\r
+{\r
+  EFI_IPv6_ADDRESS          AllNodes;\r
+  IP6_MLD_GROUP             *Group;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // Join the link-scope all-nodes multicast address (FF02::1).\r
+  // This address is started in Idle Listener state and never transitions to\r
+  // another state, and never sends a Report or Done for that address.\r
+  //\r
+\r
+  Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);\r
+\r
+  Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32) IP6_INFINIT_LIFETIME);\r
+  if (Group == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR;\r
+  }\r
+\r
+  //\r
+  // Configure MNP to receive all-nodes multicast\r
+  //\r
+  Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ERROR:\r
+  RemoveEntryList (&Group->Link);\r
+  FreePool (Group);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Add a group address to the array of group addresses.\r
+  The caller should make sure that no duplicated address\r
+  existed in the array.\r
+\r
+  @param[in, out]  IpInstance       Points to an IP6_PROTOCOL instance.\r
+  @param[in]       Group            The IP6 multicast address to add.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES      There are not sufficient resources to complete\r
+                                    the operation.\r
+  @retval EFI_SUCESS                The address is added to the group address array.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CombineGroups (\r
+  IN OUT IP6_PROTOCOL *IpInstance,\r
+  IN EFI_IPv6_ADDRESS *Group\r
+  )\r
+{\r
+  EFI_IPv6_ADDRESS     *GroupList;\r
+\r
+  NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);\r
+  ASSERT (Group != NULL && IP6_IS_MULTICAST (Group));\r
+\r
+  IpInstance->GroupCount++;\r
+\r
+  GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS));\r
+  if (GroupList == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  if (IpInstance->GroupCount > 1) {\r
+    ASSERT (IpInstance->GroupList != NULL);\r
+\r
+    CopyMem (\r
+      GroupList,\r
+      IpInstance->GroupList,\r
+      (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS)\r
+      );\r
+\r
+    FreePool (IpInstance->GroupList);\r
+  }\r
+\r
+  IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group);\r
+\r
+  IpInstance->GroupList = GroupList;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Remove a group address from the array of group addresses.\r
+  Although the function doesn't assume the byte order of Group,\r
+  the network byte order is used by the caller.\r
+\r
+  @param[in, out]  IpInstance       Points to an IP6_PROTOCOL instance.\r
+  @param[in]       Group            The IP6 multicast address to remove.\r
+\r
+  @retval EFI_NOT_FOUND             Cannot find the to be removed group address.\r
+  @retval EFI_SUCCESS               The group address was successfully removed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6RemoveGroup (\r
+  IN OUT IP6_PROTOCOL *IpInstance,\r
+  IN EFI_IPv6_ADDRESS *Group\r
+  )\r
+{\r
+  UINT32                    Index;\r
+  UINT32                    Count;\r
+\r
+  Count = IpInstance->GroupCount;\r
+\r
+  for (Index = 0; Index < Count; Index++) {\r
+    if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (Index == Count) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  while (Index < Count - 1) {\r
+    IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1);\r
+    Index++;\r
+  }\r
+\r
+  ASSERT (IpInstance->GroupCount > 0);\r
+  IpInstance->GroupCount--;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Join the multicast group on behalf of this IP6 service binding instance.\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance.\r
+  @param[in]  Interface          Points to an IP6_INTERFACE structure.\r
+  @param[in]  Address            The group address to join.\r
+\r
+  @retval EFI_SUCCESS            Successfully join the multicast group.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.\r
+  @retval Others                 Failed to join the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6JoinGroup (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_INTERFACE          *Interface,\r
+  IN EFI_IPv6_ADDRESS       *Address\r
+  )\r
+{\r
+  IP6_MLD_GROUP            *Group;\r
+  EFI_STATUS               Status;\r
+\r
+  Group = Ip6FindMldEntry (IpSb, Address);\r
+  if (Group != NULL) {\r
+    Group->RefCnt++;\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Repeat the report once or twcie after short delays [Unsolicited Report Interval] (default:10s)\r
+  // Simulate this operation as a Multicast-Address-Specific Query was received for that addresss.\r
+  //\r
+  Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL);\r
+  if (Group == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Group->SendByUs = TRUE;\r
+\r
+  Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);\r
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+    goto ERROR;\r
+  }\r
+\r
+  //\r
+  // Send unsolicited report when a node starts listening to a multicast address\r
+  //\r
+  Status = Ip6SendMldReport (IpSb, Interface, Address);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ERROR:\r
+  RemoveEntryList (&Group->Link);\r
+  FreePool (Group);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Leave the IP6 multicast group.\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance.\r
+  @param[in]  Address            The group address to leave.\r
+\r
+  @retval EFI_NOT_FOUND          The IP6 service instance isn't in the group.\r
+  @retval EFI_SUCCESS            Successfully leave the multicast group..\r
+  @retval Others                 Failed to leave the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6LeaveGroup (\r
+ IN IP6_SERVICE            *IpSb,\r
+ IN EFI_IPv6_ADDRESS       *Address\r
+  )\r
+{\r
+  IP6_MLD_GROUP            *Group;\r
+  EFI_STATUS               Status;\r
+\r
+  Group = Ip6FindMldEntry (IpSb, Address);\r
+  if (Group == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // If more than one instance is in the group, decrease\r
+  // the RefCnt then return.\r
+  //\r
+  if ((Group->RefCnt > 0) && (--Group->RefCnt > 0)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // If multiple IP6 group addresses are mapped to the same\r
+  // multicast MAC address, don't configure the MNP to leave\r
+  // the MAC.\r
+  //\r
+  if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) {\r
+    Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac);\r
+    if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Send a leave report if we are the last node to report\r
+  //\r
+  if (Group->SendByUs) {\r
+    Status = Ip6SendMldDone (IpSb, Address);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  RemoveEntryList (&Group->Link);\r
+  FreePool (Group);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Worker function for EfiIp6Groups(). The caller\r
+  should make sure that the parameters are valid.\r
+\r
+  @param[in]  IpInstance        The IP6 child to change the setting.\r
+  @param[in]  JoinFlag          TRUE to join the group, otherwise leave it.\r
+  @param[in]  GroupAddress      The target group address. If NULL, leave all\r
+                                the group addresses.\r
+\r
+  @retval EFI_ALREADY_STARTED   Wants to join the group, but is already a member of it\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate sufficient resources.\r
+  @retval EFI_DEVICE_ERROR      Failed to set the group configuraton.\r
+  @retval EFI_SUCCESS           Successfully updated the group setting.\r
+  @retval EFI_NOT_FOUND         Try to leave the group which it isn't a member.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Groups (\r
+  IN IP6_PROTOCOL           *IpInstance,\r
+  IN BOOLEAN                JoinFlag,\r
+  IN EFI_IPv6_ADDRESS       *GroupAddress       OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  IP6_SERVICE               *IpSb;\r
+  UINT32                    Index;\r
+  EFI_IPv6_ADDRESS          *Group;\r
+\r
+  IpSb = IpInstance->Service;\r
+\r
+  if (JoinFlag) {\r
+    ASSERT (GroupAddress != NULL);\r
+\r
+    for (Index = 0; Index < IpInstance->GroupCount; Index++) {\r
+      if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) {\r
+        return EFI_ALREADY_STARTED;\r
+      }\r
+    }\r
+\r
+    Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress);\r
+    if (!EFI_ERROR (Status)) {\r
+      return Ip6CombineGroups (IpInstance, GroupAddress);\r
+    }\r
+\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Leave the group. Leave all the groups if GroupAddress is NULL.\r
+  //\r
+  for (Index = IpInstance->GroupCount; Index > 0; Index--) {\r
+    Group = IpInstance->GroupList + (Index - 1);\r
+\r
+    if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) {\r
+      Status = Ip6LeaveGroup (IpInstance->Service, Group);\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+\r
+      Ip6RemoveGroup (IpInstance, Group);\r
+\r
+      if (IpInstance->GroupCount == 0) {\r
+        ASSERT (Index == 1);\r
+        FreePool (IpInstance->GroupList);\r
+        IpInstance->GroupList = NULL;\r
+      }\r
+\r
+      if (GroupAddress != NULL) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+  }\r
+\r
+  return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Set a random value of the delay timer for the multicast address from the range\r
+  [0, Maximum Response Delay]. If a timer for any address is already\r
+  running, it is reset to the new random value only if the requested\r
+  Maximum Response Delay is less than the remaining value of the\r
+  running timer.  If the Query packet specifies a Maximum Response\r
+  Delay of zero, each timer is effectively set to zero, and the action\r
+  specified below for timer expiration is performed immediately.\r
+\r
+  @param[in]      IpSb               The IP6 service binding instance.\r
+  @param[in]      MaxRespDelay       The Maximum Response Delay, in milliseconds.\r
+  @param[in]      MulticastAddr      The multicast address.\r
+  @param[in, out] Group              Points to a IP6_MLD_GROUP list entry node.\r
+\r
+  @retval EFI_SUCCESS                The delay timer is successfully updated or\r
+                                     timer expiration is performed immediately.\r
+  @retval Others                     Failed to send out MLD report message.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6UpdateDelayTimer (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN UINT16                 MaxRespDelay,\r
+  IN EFI_IPv6_ADDRESS       *MulticastAddr,\r
+  IN OUT IP6_MLD_GROUP      *Group\r
+  )\r
+{\r
+  UINT32                    Delay;\r
+\r
+  //\r
+  // If the Query packet specifies a Maximum Response Delay of zero, perform timer\r
+  // expiration immediately.\r
+  //\r
+  if (MaxRespDelay == 0) {\r
+    Group->DelayTimer = 0;\r
+    return Ip6SendMldReport (IpSb, NULL, MulticastAddr);\r
+  }\r
+\r
+  Delay = (UINT32) (MaxRespDelay / 1000);\r
+\r
+  //\r
+  // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay]\r
+  // If a timer is already running, resets it if the request Maximum Response Delay\r
+  // is less than the remaining value of the running timer.\r
+  //\r
+  if (Group->DelayTimer == 0 || Delay < Group->DelayTimer) {\r
+    Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ());\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Process the Multicast Listener Query message.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the MLD query packet.\r
+  @param[in]  Packet             The content of the MLD query packet with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_SUCCESS            The MLD query packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval Others                 Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessMldQuery (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  EFI_IPv6_ADDRESS          AllNodes;\r
+  IP6_MLD_GROUP             *Group;\r
+  IP6_MLD_HEAD              MldPacket;\r
+  LIST_ENTRY                *Entry;\r
+  EFI_STATUS                Status;\r
+\r
+  Status = EFI_INVALID_PARAMETER;\r
+\r
+  //\r
+  // Check the validity of the packet, generic query or specific query\r
+  //\r
+  if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
+    goto Exit;\r
+  }\r
+\r
+  if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // The Packet points to MLD report raw data without Hop-By-Hop option.\r
+  //\r
+  NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);\r
+  MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay);\r
+\r
+  Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);\r
+  if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) {\r
+    //\r
+    // Receives a Multicast-Address-Specific Query, check it firstly\r
+    //\r
+    if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {\r
+      goto Exit;\r
+    }\r
+    //\r
+    // The node is not listening but it receives the specific query. Just return.\r
+    //\r
+    Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);\r
+    if (Group == NULL) {\r
+      Status = EFI_SUCCESS;\r
+      goto Exit;\r
+    }\r
+\r
+    Status = Ip6UpdateDelayTimer (\r
+               IpSb,\r
+               MldPacket.MaxRespDelay,\r
+               &MldPacket.Group,\r
+               Group\r
+               );\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Receives a General Query, sets a delay timer for each multicast address it is listening\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {\r
+    Group  = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
+    Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+  NetbufFree (Packet);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Process the Multicast Listener Report message.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the MLD report packet.\r
+  @param[in]  Packet             The content of the MLD report packet with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_SUCCESS            The MLD report packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessMldReport (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_MLD_HEAD              MldPacket;\r
+  IP6_MLD_GROUP             *Group;\r
+  EFI_STATUS                Status;\r
+\r
+  Status = EFI_INVALID_PARAMETER;\r
+\r
+  //\r
+  // Validate the incoming message, if invalid, drop it.\r
+  //\r
+  if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
+    goto Exit;\r
+  }\r
+\r
+  if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // The Packet points to MLD report raw data without Hop-By-Hop option.\r
+  //\r
+  NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);\r
+  if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {\r
+    goto Exit;\r
+  }\r
+\r
+  Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);\r
+  if (Group == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // The report is sent by another node, stop its own timer relates to the multicast address and clear\r
+  //\r
+\r
+  if (!Group->SendByUs) {\r
+    Group->DelayTimer = 0;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+  NetbufFree (Packet);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The heartbeat timer of MLD module. It sends out a solicited MLD report when\r
+  DelayTimer expires.\r
+\r
+  @param[in]  IpSb              The IP6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6MldTimerTicking (\r
+  IN IP6_SERVICE            *IpSb\r
+  )\r
+{\r
+  IP6_MLD_GROUP             *Group;\r
+  LIST_ENTRY                *Entry;\r
+\r
+  //\r
+  // Send solicited report when timer expires\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {\r
+    Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
+    if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) {\r
+      Ip6SendMldReport (IpSb, NULL, &Group->Address);\r
+    }\r
+  }\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Mld.h b/NetworkPkg/Ip6Dxe/Ip6Mld.h
new file mode 100644 (file)
index 0000000..e69f5d5
--- /dev/null
@@ -0,0 +1,198 @@
+/** @file\r
+  Multicast Listener Discovery support routines.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_MLD_H__\r
+#define __EFI_IP6_MLD_H__\r
+\r
+#define IP6_UNSOLICITED_REPORT_INTERVAL 10\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+  IP6_ICMP_HEAD           Head;\r
+  UINT16                  MaxRespDelay;\r
+  UINT16                  Reserved;\r
+  EFI_IPv6_ADDRESS        Group;\r
+} IP6_MLD_HEAD;\r
+#pragma pack()\r
+\r
+//\r
+// The status of multicast group. It isn't necessary to maintain\r
+// explicit state of host state diagram. A group with finity\r
+// DelayTime (less than 0xffffffff) is in "delaying listener" state. otherwise, it is in\r
+// "idle listener" state.\r
+//\r
+typedef struct {\r
+  LIST_ENTRY              Link;\r
+  INTN                    RefCnt;\r
+  EFI_IPv6_ADDRESS        Address;\r
+  UINT32                  DelayTimer;\r
+  BOOLEAN                 SendByUs;\r
+  EFI_MAC_ADDRESS         Mac;\r
+} IP6_MLD_GROUP;\r
+\r
+//\r
+// The MLD status. Each IP6 service instance has a MLD_SERVICE_DATA\r
+// attached. The Mldv1QuerySeen remember whether the server on this\r
+// connected network is v1 or v2.\r
+//\r
+typedef struct {\r
+  INTN                    Mldv1QuerySeen;\r
+  LIST_ENTRY              Groups;\r
+} IP6_MLD_SERVICE_DATA;\r
+\r
+/**\r
+  Search a IP6_MLD_GROUP list entry node from a list array.\r
+\r
+  @param[in]       IpSb          Points to an IP6 service binding instance.\r
+  @param[in]       MulticastAddr The IPv6 multicast address to be searched.\r
+\r
+  @return The found IP6_ML_GROUP list entry or NULL.\r
+\r
+**/\r
+IP6_MLD_GROUP *\r
+Ip6FindMldEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *MulticastAddr\r
+  );\r
+\r
+/**\r
+  Init the MLD data of the IP6 service instance, configure\r
+  MNP to receive ALL SYSTEM multicasts.\r
+\r
+  @param[in]  IpSb              The IP6 service whose MLD is to be initialized.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  There are not sufficient resources to complete the\r
+                                operation.\r
+  @retval EFI_SUCCESS           The MLD module successfully initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InitMld (\r
+  IN IP6_SERVICE            *IpSb\r
+  );\r
+\r
+/**\r
+  Join the multicast group on behalf of this IP6 service binding instance.\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance.\r
+  @param[in]  Interface          Points to an IP6_INTERFACE structure.\r
+  @param[in]  Address            The group address to join.\r
+\r
+  @retval EFI_SUCCESS            Successfully joined the multicast group.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.\r
+  @retval Others                 Failed to join the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6JoinGroup (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_INTERFACE          *Interface,\r
+  IN EFI_IPv6_ADDRESS       *Address\r
+  );\r
+\r
+/**\r
+  Leave the IP6 multicast group.\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance.\r
+  @param[in]  Address            The group address to leave.\r
+\r
+  @retval EFI_NOT_FOUND          The IP6 service instance isn't in the group.\r
+  @retval EFI_SUCCESS            Successfully left the multicast group.\r
+  @retval Others                 Failed to leave the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6LeaveGroup (\r
+ IN IP6_SERVICE            *IpSb,\r
+ IN EFI_IPv6_ADDRESS       *Address\r
+  );\r
+\r
+/**\r
+  Worker function for EfiIp6Groups(). The caller\r
+  should verify that the parameters are valid.\r
+\r
+  @param[in]  IpInstance        The IP6 child to change the setting.\r
+  @param[in]  JoinFlag          TRUE to join the group, otherwise leave it.\r
+  @param[in]  GroupAddress      The target group address. If NULL, leave all\r
+                                the group addresses.\r
+\r
+  @retval EFI_ALREADY_STARTED   Wants to join the group, but is already a member of it.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate some resources.\r
+  @retval EFI_DEVICE_ERROR      Failed to set the group configuraton.\r
+  @retval EFI_SUCCESS           Successfully updated the group setting.\r
+  @retval EFI_NOT_FOUND         Tried to leave a group of whom it isn't a member.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Groups (\r
+  IN IP6_PROTOCOL           *IpInstance,\r
+  IN BOOLEAN                JoinFlag,\r
+  IN EFI_IPv6_ADDRESS       *GroupAddress       OPTIONAL\r
+  );\r
+\r
+/**\r
+  Process the Multicast Listener Query message.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the MLD query packet.\r
+  @param[in]  Packet             The content of the MLD query packet with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_SUCCESS            The MLD query packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval Others                 Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessMldQuery (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  );\r
+\r
+/**\r
+  Process the Multicast Listener Report message.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the MLD report packet.\r
+  @param[in]  Packet             The content of the MLD report packet with IP head\r
+                                 removed.\r
+\r
+  @retval EFI_SUCCESS            The MLD report packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessMldReport (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  );\r
+\r
+\r
+/**\r
+  The heartbeat timer of the MLD module. It sends out solicited MLD report when\r
+  DelayTimer expires.\r
+\r
+  @param[in]  IpSb              The IP6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6MldTimerTicking (\r
+  IN IP6_SERVICE            *IpSb\r
+  );\r
+\r
+#endif\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Nd.c b/NetworkPkg/Ip6Dxe/Ip6Nd.c
new file mode 100644 (file)
index 0000000..f2a47a8
--- /dev/null
@@ -0,0 +1,3141 @@
+/** @file\r
+  Implementation of Neighbor Discovery support routines.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_MAC_ADDRESS mZeroMacAddress;\r
+\r
+/**\r
+  Update the ReachableTime in IP6 service binding instance data, in milliseconds.\r
+\r
+  @param[in, out] IpSb     Points to the IP6_SERVICE.\r
+\r
+**/\r
+VOID\r
+Ip6UpdateReachableTime (\r
+  IN OUT IP6_SERVICE  *IpSb\r
+  )\r
+{\r
+  UINT32              Random;\r
+\r
+  Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE;\r
+  Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED;\r
+  IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE;\r
+}\r
+\r
+/**\r
+  Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number\r
+  of EFI_IP6_NEIGHBOR_CACHE is also returned.\r
+\r
+  @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.\r
+  @param[out] NeighborCount     The number of returned neighbor cache entries.\r
+  @param[out] NeighborCache     The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.\r
+\r
+  @retval EFI_SUCCESS           The EFI_IP6_NEIGHBOR_CACHE successfully built.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiNeighborCache (\r
+  IN IP6_PROTOCOL            *IpInstance,\r
+  OUT UINT32                 *NeighborCount,\r
+  OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache\r
+  )\r
+{\r
+  IP6_NEIGHBOR_ENTRY        *Neighbor;\r
+  LIST_ENTRY                *Entry;\r
+  IP6_SERVICE               *IpSb;\r
+  UINT32                    Count;\r
+  EFI_IP6_NEIGHBOR_CACHE    *EfiNeighborCache;\r
+  EFI_IP6_NEIGHBOR_CACHE    *NeighborCacheTmp;\r
+\r
+  NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);\r
+  ASSERT (NeighborCount != NULL && NeighborCache != NULL);\r
+\r
+  IpSb  = IpInstance->Service;\r
+  Count = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {\r
+    Count++;\r
+  }\r
+\r
+  if (Count == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE));\r
+  if (NeighborCacheTmp == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  *NeighborCount = Count;\r
+  Count          = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {\r
+    Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);\r
+\r
+    EfiNeighborCache = NeighborCacheTmp + Count;\r
+\r
+   EfiNeighborCache->State = Neighbor->State;\r
+    IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor);\r
+    IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress);\r
+\r
+    Count++;\r
+  }\r
+\r
+  ASSERT (*NeighborCount == Count);\r
+  *NeighborCache = NeighborCacheTmp;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number\r
+  of prefix entries is also returned.\r
+\r
+  @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.\r
+  @param[out] PrefixCount       The number of returned prefix entries.\r
+  @param[out] PrefixTable       The pointer to the array of PrefixTable.\r
+\r
+  @retval EFI_SUCCESS           The prefix table successfully built.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the prefix table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildPrefixTable (\r
+  IN IP6_PROTOCOL           *IpInstance,\r
+  OUT UINT32                *PrefixCount,\r
+  OUT EFI_IP6_ADDRESS_INFO  **PrefixTable\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  IP6_SERVICE               *IpSb;\r
+  UINT32                    Count;\r
+  IP6_PREFIX_LIST_ENTRY     *PrefixList;\r
+  EFI_IP6_ADDRESS_INFO      *EfiPrefix;\r
+  EFI_IP6_ADDRESS_INFO      *PrefixTableTmp;\r
+\r
+  NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);\r
+  ASSERT (PrefixCount != NULL && PrefixTable != NULL);\r
+\r
+  IpSb  = IpInstance->Service;\r
+  Count = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {\r
+    Count++;\r
+  }\r
+\r
+  if (Count == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO));\r
+  if (PrefixTableTmp == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  *PrefixCount = Count;\r
+  Count        = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {\r
+    PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+    EfiPrefix  = PrefixTableTmp + Count;\r
+    IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix);\r
+    EfiPrefix->PrefixLength = PrefixList->PrefixLength;\r
+\r
+    Count++;\r
+  }\r
+\r
+  ASSERT (*PrefixCount == Count);\r
+  *PrefixTable = PrefixTableTmp;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Allocate and initialize a IP6 prefix list entry.\r
+\r
+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.\r
+  @param[in]  OnLinkOrAuto      If TRUE, the entry is created for the on link prefix list.\r
+                                Otherwise, it is created for the autoconfiguration prefix list.\r
+  @param[in]  ValidLifetime     The length of time in seconds that the prefix\r
+                                is valid for the purpose of on-link determination.\r
+  @param[in]  PreferredLifetime The length of time in seconds that addresses\r
+                                generated from the prefix via stateless address\r
+                                autoconfiguration remain preferred.\r
+  @param[in]  PrefixLength      The prefix length of the Prefix.\r
+  @param[in]  Prefix            The prefix address.\r
+\r
+  @return NULL if it failed to allocate memory for the prefix node. Otherwise, point\r
+          to the created or existing prefix list entry.\r
+\r
+**/\r
+IP6_PREFIX_LIST_ENTRY *\r
+Ip6CreatePrefixListEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN BOOLEAN                OnLinkOrAuto,\r
+  IN UINT32                 ValidLifetime,\r
+  IN UINT32                 PreferredLifetime,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *Prefix\r
+  )\r
+{\r
+  IP6_PREFIX_LIST_ENTRY     *PrefixEntry;\r
+  IP6_ROUTE_ENTRY           *RtEntry;\r
+  LIST_ENTRY                *ListHead;\r
+  LIST_ENTRY                *Entry;\r
+  IP6_PREFIX_LIST_ENTRY     *TmpPrefixEntry;\r
+\r
+  if (Prefix == NULL || PreferredLifetime > ValidLifetime || PrefixLength >= IP6_PREFIX_NUM) {\r
+    return NULL;\r
+  }\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  PrefixEntry = Ip6FindPrefixListEntry (\r
+                  IpSb,\r
+                  OnLinkOrAuto,\r
+                  PrefixLength,\r
+                  Prefix\r
+                  );\r
+  if (PrefixEntry != NULL) {\r
+    PrefixEntry->RefCnt ++;\r
+    return PrefixEntry;\r
+  }\r
+\r
+  PrefixEntry = AllocatePool (sizeof (IP6_PREFIX_LIST_ENTRY));\r
+  if (PrefixEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  PrefixEntry->RefCnt            = 1;\r
+  PrefixEntry->ValidLifetime     = ValidLifetime;\r
+  PrefixEntry->PreferredLifetime = PreferredLifetime;\r
+  PrefixEntry->PrefixLength      = PrefixLength;\r
+  IP6_COPY_ADDRESS (&PrefixEntry->Prefix, Prefix);\r
+\r
+  ListHead = OnLinkOrAuto ? &IpSb->OnlinkPrefix : &IpSb->AutonomousPrefix;\r
+\r
+  //\r
+  // Create a direct route entry for on-link prefix and insert to route area.\r
+  //\r
+  if (OnLinkOrAuto) {\r
+    RtEntry = Ip6CreateRouteEntry (Prefix, PrefixLength, NULL);\r
+    if (RtEntry == NULL) {\r
+      FreePool (PrefixEntry);\r
+      return NULL;\r
+    }\r
+\r
+    RtEntry->Flag = IP6_DIRECT_ROUTE;\r
+    InsertHeadList (&IpSb->RouteTable->RouteArea[PrefixLength], &RtEntry->Link);\r
+    IpSb->RouteTable->TotalNum++;\r
+  }\r
+\r
+  //\r
+  // Insert the prefix entry in the order that a prefix with longer prefix length\r
+  // is put ahead in the list.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, ListHead) {\r
+    TmpPrefixEntry = NET_LIST_USER_STRUCT(Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+\r
+    if (TmpPrefixEntry->PrefixLength < PrefixEntry->PrefixLength) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  NetListInsertBefore (Entry, &PrefixEntry->Link);\r
+\r
+  return PrefixEntry;\r
+}\r
+\r
+/**\r
+  Destory a IP6 prefix list entry.\r
+\r
+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.\r
+  @param[in]  PrefixEntry       The to be destroyed prefix list entry.\r
+  @param[in]  OnLinkOrAuto      If TRUE, the entry is removed from on link prefix list.\r
+                                Otherwise remove from autoconfiguration prefix list.\r
+  @param[in]  ImmediateDelete   If TRUE, remove the entry directly.\r
+                                Otherwise, check the reference count to see whether\r
+                                it should be removed.\r
+\r
+**/\r
+VOID\r
+Ip6DestroyPrefixListEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_PREFIX_LIST_ENTRY  *PrefixEntry,\r
+  IN BOOLEAN                OnLinkOrAuto,\r
+  IN BOOLEAN                ImmediateDelete\r
+  )\r
+{\r
+  LIST_ENTRY      *Entry;\r
+  IP6_INTERFACE   *IpIf;\r
+  EFI_STATUS      Status;\r
+\r
+  if ((!ImmediateDelete) && (PrefixEntry->RefCnt > 0) && ((--PrefixEntry->RefCnt) > 0)) {\r
+    return ;\r
+  }\r
+\r
+  if (OnLinkOrAuto) {\r
+      //\r
+      // Remove the direct route for onlink prefix from route table.\r
+      //\r
+      do {\r
+        Status = Ip6DelRoute (\r
+                   IpSb->RouteTable,\r
+                   &PrefixEntry->Prefix,\r
+                   PrefixEntry->PrefixLength,\r
+                   NULL\r
+                   );\r
+      } while (Status != EFI_NOT_FOUND);\r
+  } else {\r
+    //\r
+    // Remove the corresponding addresses generated from this autonomous prefix.\r
+    //\r
+    NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+      IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);\r
+\r
+      Ip6RemoveAddr (IpSb, &IpIf->AddressList, &IpIf->AddressCount, &PrefixEntry->Prefix, PrefixEntry->PrefixLength);\r
+    }\r
+  }\r
+\r
+  RemoveEntryList (&PrefixEntry->Link);\r
+  FreePool (PrefixEntry);\r
+}\r
+\r
+/**\r
+  Search the list array to find an IP6 prefix list entry.\r
+\r
+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.\r
+  @param[in]  OnLinkOrAuto      If TRUE, the search the link prefix list,\r
+                                Otherwise search the autoconfiguration prefix list.\r
+  @param[in]  PrefixLength      The prefix length of the Prefix\r
+  @param[in]  Prefix            The prefix address.\r
+\r
+  @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the\r
+          pointer to the IP6 prefix list entry.\r
+\r
+**/\r
+IP6_PREFIX_LIST_ENTRY *\r
+Ip6FindPrefixListEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN BOOLEAN                OnLinkOrAuto,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *Prefix\r
+  )\r
+{\r
+  IP6_PREFIX_LIST_ENTRY     *PrefixList;\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *ListHead;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (Prefix != NULL);\r
+\r
+  if (OnLinkOrAuto) {\r
+    ListHead = &IpSb->OnlinkPrefix;\r
+  } else {\r
+    ListHead = &IpSb->AutonomousPrefix;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Entry, ListHead) {\r
+    PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+    if (PrefixLength != 255) {\r
+      //\r
+      // Perform exactly prefix match.\r
+      //\r
+      if (PrefixList->PrefixLength == PrefixLength &&\r
+        NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixLength)) {\r
+        return PrefixList;\r
+      }\r
+    } else {\r
+      //\r
+      // Perform the longest prefix match. The list is already sorted with\r
+      // the longest length prefix put at the head of the list.\r
+      //\r
+      if (NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixList->PrefixLength)) {\r
+        return PrefixList;\r
+      }\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Release the resource in the prefix list table, and destroy the list entry and\r
+  corresponding addresses or route entries.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  ListHead          The list entry head of the prefix list table.\r
+\r
+**/\r
+VOID\r
+Ip6CleanPrefixListTable (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN LIST_ENTRY             *ListHead\r
+  )\r
+{\r
+  IP6_PREFIX_LIST_ENTRY     *PrefixList;\r
+  BOOLEAN                   OnLink;\r
+\r
+  OnLink = (BOOLEAN) (ListHead == &IpSb->OnlinkPrefix);\r
+\r
+  while (!IsListEmpty (ListHead)) {\r
+    PrefixList = NET_LIST_HEAD (ListHead, IP6_PREFIX_LIST_ENTRY, Link);\r
+    Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);\r
+  }\r
+}\r
+\r
+/**\r
+  Callback function when address resolution is finished. It will cancel\r
+  all the queued frames if the address resolution failed, or transmit them\r
+  if the request succeeded.\r
+\r
+  @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.\r
+\r
+**/\r
+VOID\r
+Ip6OnArpResolved (\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_NEIGHBOR_ENTRY        *ArpQue;\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_LINK_TX_TOKEN         *Token;\r
+  EFI_STATUS                Status;\r
+  BOOLEAN                   Sent;\r
+\r
+  ArpQue = (IP6_NEIGHBOR_ENTRY *) Context;\r
+  if ((ArpQue == NULL) || (ArpQue->Interface == NULL)) {\r
+    return ;\r
+  }\r
+\r
+  IpSb   = ArpQue->Interface->Service;\r
+  if ((IpSb == NULL) || (IpSb->Signature != IP6_SERVICE_SIGNATURE)) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // ARP resolve failed for some reason. Release all the frame\r
+  // and ARP queue itself. Ip6FreeArpQue will call the frame's\r
+  // owner back.\r
+  //\r
+  if (NET_MAC_EQUAL (&ArpQue->LinkAddress, &mZeroMacAddress, IpSb->SnpMode.HwAddressSize)) {\r
+    Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, TRUE, EFI_NO_MAPPING, NULL, NULL);\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // ARP resolve succeeded, Transmit all the frame.\r
+  //\r
+  Sent = FALSE;\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {\r
+    RemoveEntryList (Entry);\r
+\r
+    Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);\r
+    IP6_COPY_LINK_ADDRESS (&Token->DstMac, &ArpQue->LinkAddress);\r
+\r
+    //\r
+    // Insert the tx token before transmitting it via MNP as the FrameSentDpc\r
+    // may be called before Mnp->Transmit returns which will remove this tx\r
+    // token from the SentFrames list. Remove it from the list if the returned\r
+    // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the\r
+    // FrameSentDpc won't be queued.\r
+    //\r
+    InsertTailList (&ArpQue->Interface->SentFrames, &Token->Link);\r
+\r
+    Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);\r
+    if (EFI_ERROR (Status)) {\r
+      RemoveEntryList (&Token->Link);\r
+      Token->CallBack (Token->Packet, Status, 0, Token->Context);\r
+\r
+      Ip6FreeLinkTxToken (Token);\r
+      continue;\r
+    } else {\r
+      Sent = TRUE;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Free the ArpQue only but not the whole neighbor entry.\r
+  //\r
+  Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, FALSE, EFI_SUCCESS, NULL, NULL);\r
+\r
+  if (Sent && (ArpQue->State == EfiNeighborStale)) {\r
+    ArpQue->State = EfiNeighborDelay;\r
+    ArpQue->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);\r
+  }\r
+}\r
+\r
+/**\r
+  Allocate and initialize an IP6 neighbor cache entry.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  CallBack          The callback function to be called when\r
+                                address resolution is finished.\r
+  @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.\r
+  @param[in]  LinkAddress       Points to the MAC address of the neighbor.\r
+                                Ignored if NULL.\r
+\r
+  @return NULL if failed to allocate memory for the neighbor cache entry.\r
+          Otherwise, point to the created neighbor cache entry.\r
+\r
+**/\r
+IP6_NEIGHBOR_ENTRY *\r
+Ip6CreateNeighborEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_ARP_CALLBACK       CallBack,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Address,\r
+  IN EFI_MAC_ADDRESS        *LinkAddress OPTIONAL\r
+  )\r
+{\r
+  IP6_NEIGHBOR_ENTRY        *Entry;\r
+  IP6_DEFAULT_ROUTER        *DefaultRouter;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (Ip6Address!= NULL);\r
+\r
+  Entry = AllocateZeroPool (sizeof (IP6_NEIGHBOR_ENTRY));\r
+  if (Entry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Entry->RefCnt    = 1;\r
+  Entry->IsRouter  = FALSE;\r
+  Entry->ArpFree   = FALSE;\r
+  Entry->Dynamic   = FALSE;\r
+  Entry->State     = EfiNeighborInComplete;\r
+  Entry->Transmit  = IP6_MAX_MULTICAST_SOLICIT + 1;\r
+  Entry->CallBack  = CallBack;\r
+  Entry->Interface = NULL;\r
+\r
+  InitializeListHead (&Entry->Frames);\r
+\r
+  IP6_COPY_ADDRESS (&Entry->Neighbor, Ip6Address);\r
+\r
+  if (LinkAddress != NULL) {\r
+    IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, LinkAddress);\r
+  } else {\r
+    IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, &mZeroMacAddress);\r
+  }\r
+\r
+  InsertHeadList (&IpSb->NeighborTable, &Entry->Link);\r
+\r
+  //\r
+  // If corresponding default router entry exists, establish the relationship.\r
+  //\r
+  DefaultRouter = Ip6FindDefaultRouter (IpSb, Ip6Address);\r
+  if (DefaultRouter != NULL) {\r
+    DefaultRouter->NeighborCache = Entry;\r
+  }\r
+\r
+  return Entry;\r
+}\r
+\r
+/**\r
+  Search a IP6 neighbor cache entry.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.\r
+\r
+  @return NULL if it failed to find the matching neighbor cache entry.\r
+          Otherwise, point to the found neighbor cache entry.\r
+\r
+**/\r
+IP6_NEIGHBOR_ENTRY *\r
+Ip6FindNeighborEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Address\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_NEIGHBOR_ENTRY        *Neighbor;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (Ip6Address != NULL);\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {\r
+    Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);\r
+    if (EFI_IP6_EQUAL (Ip6Address, &Neighbor->Neighbor)) {\r
+      RemoveEntryList (Entry);\r
+      InsertHeadList (&IpSb->NeighborTable, Entry);\r
+\r
+      return Neighbor;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Free a IP6 neighbor cache entry and remove all the frames on the address\r
+  resolution queue that pass the FrameToCancel. That is, either FrameToCancel\r
+  is NULL, or it returns true for the frame.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  NeighborCache     The to be free neighbor cache entry.\r
+  @param[in]  SendIcmpError     If TRUE, send out ICMP error.\r
+  @param[in]  FullFree          If TRUE, remove the neighbor cache entry.\r
+                                Otherwise remove the pending frames.\r
+  @param[in]  IoStatus          The status returned to the cancelled frames'\r
+                                callback function.\r
+  @param[in]  FrameToCancel     Function to select which frame to cancel.\r
+                                This is an optional parameter that may be NULL.\r
+  @param[in]  Context           Opaque parameter to the FrameToCancel.\r
+                                Ignored if FrameToCancel is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER The input parameter is invalid.\r
+  @retval EFI_SUCCESS           The operation finished successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FreeNeighborEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_NEIGHBOR_ENTRY     *NeighborCache,\r
+  IN BOOLEAN                SendIcmpError,\r
+  IN BOOLEAN                FullFree,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN IP6_FRAME_TO_CANCEL    FrameToCancel OPTIONAL,\r
+  IN VOID                   *Context      OPTIONAL\r
+  )\r
+{\r
+  IP6_LINK_TX_TOKEN         *TxToken;\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_DEFAULT_ROUTER        *DefaultRouter;\r
+\r
+  //\r
+  // If FrameToCancel fails, the token will not be released.\r
+  // To avoid the memory leak, stop this usage model.\r
+  //\r
+  if (FullFree && FrameToCancel != NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &NeighborCache->Frames) {\r
+    TxToken = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);\r
+\r
+    if (SendIcmpError && !IP6_IS_MULTICAST (&TxToken->Packet->Ip.Ip6->DestinationAddress)) {\r
+      Ip6SendIcmpError (\r
+        IpSb,\r
+        TxToken->Packet,\r
+        NULL,\r
+        &TxToken->Packet->Ip.Ip6->SourceAddress,\r
+        ICMP_V6_DEST_UNREACHABLE,\r
+        ICMP_V6_ADDR_UNREACHABLE,\r
+        NULL\r
+        );\r
+    }\r
+\r
+    if ((FrameToCancel == NULL) || FrameToCancel (TxToken, Context)) {\r
+      RemoveEntryList (Entry);\r
+      TxToken->CallBack (TxToken->Packet, IoStatus, 0, TxToken->Context);\r
+      Ip6FreeLinkTxToken (TxToken);\r
+    }\r
+  }\r
+\r
+  if (NeighborCache->ArpFree && IsListEmpty (&NeighborCache->Frames)) {\r
+    RemoveEntryList (&NeighborCache->ArpList);\r
+    NeighborCache->ArpFree = FALSE;\r
+  }\r
+\r
+  if (FullFree) {\r
+    if (NeighborCache->IsRouter) {\r
+      DefaultRouter = Ip6FindDefaultRouter (IpSb, &NeighborCache->Neighbor);\r
+      if (DefaultRouter != NULL) {\r
+        Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+      }\r
+    }\r
+\r
+    RemoveEntryList (&NeighborCache->Link);\r
+    FreePool (NeighborCache);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Allocate and initialize an IP6 default router entry.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  Ip6Address        The IPv6 address of the default router.\r
+  @param[in]  RouterLifetime    The lifetime associated with the default\r
+                                router, in units of seconds.\r
+\r
+  @return NULL if it failed to allocate memory for the default router node.\r
+          Otherwise, point to the created default router node.\r
+\r
+**/\r
+IP6_DEFAULT_ROUTER *\r
+Ip6CreateDefaultRouter (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Address,\r
+  IN UINT16                 RouterLifetime\r
+  )\r
+{\r
+  IP6_DEFAULT_ROUTER        *Entry;\r
+  IP6_ROUTE_ENTRY           *RtEntry;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (Ip6Address != NULL);\r
+\r
+  Entry = AllocatePool (sizeof (IP6_DEFAULT_ROUTER));\r
+  if (Entry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Entry->RefCnt        = 1;\r
+  Entry->Lifetime      = RouterLifetime;\r
+  Entry->NeighborCache = Ip6FindNeighborEntry (IpSb, Ip6Address);\r
+  IP6_COPY_ADDRESS (&Entry->Router, Ip6Address);\r
+\r
+  //\r
+  // Add a default route into route table with both Destination and PrefixLength set to zero.\r
+  //\r
+  RtEntry = Ip6CreateRouteEntry (NULL, 0, Ip6Address);\r
+  if (RtEntry == NULL) {\r
+    FreePool (Entry);\r
+    return NULL;\r
+  }\r
+\r
+  InsertHeadList (&IpSb->RouteTable->RouteArea[0], &RtEntry->Link);\r
+  IpSb->RouteTable->TotalNum++;\r
+\r
+  InsertTailList (&IpSb->DefaultRouterList, &Entry->Link);\r
+\r
+  return Entry;\r
+}\r
+\r
+/**\r
+  Destroy an IP6 default router entry.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  DefaultRouter     The to be destroyed IP6_DEFAULT_ROUTER.\r
+\r
+**/\r
+VOID\r
+Ip6DestroyDefaultRouter (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_DEFAULT_ROUTER     *DefaultRouter\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  RemoveEntryList (&DefaultRouter->Link);\r
+\r
+  //\r
+  // Update the Destination Cache - all entries using the time-out router as next-hop\r
+  // should perform next-hop determination again.\r
+  //\r
+  do {\r
+    Status = Ip6DelRoute (IpSb->RouteTable, NULL, 0, &DefaultRouter->Router);\r
+  } while (Status != EFI_NOT_FOUND);\r
+\r
+  FreePool (DefaultRouter);\r
+}\r
+\r
+/**\r
+  Clean an IP6 default router list.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  DefaultRouter     The to be destroyed IP6_DEFAULT_ROUTER.\r
+\r
+**/\r
+VOID\r
+Ip6CleanDefaultRouterList (\r
+  IN IP6_SERVICE            *IpSb\r
+  )\r
+{\r
+  IP6_DEFAULT_ROUTER        *DefaultRouter;\r
+\r
+  while (!IsListEmpty (&IpSb->DefaultRouterList)) {\r
+    DefaultRouter = NET_LIST_HEAD (&IpSb->DefaultRouterList, IP6_DEFAULT_ROUTER, Link);\r
+    Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+  }\r
+}\r
+\r
+/**\r
+  Search a default router node from an IP6 default router list.\r
+\r
+  @param[in]  IpSb          The pointer to the IP6_SERVICE instance.\r
+  @param[in]  Ip6Address    The IPv6 address of the to be searched default router node.\r
+\r
+  @return NULL if it failed to find the matching default router node.\r
+          Otherwise, point to the found default router node.\r
+\r
+**/\r
+IP6_DEFAULT_ROUTER *\r
+Ip6FindDefaultRouter (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Address\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  IP6_DEFAULT_ROUTER        *DefaultRouter;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (Ip6Address != NULL);\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->DefaultRouterList) {\r
+    DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);\r
+    if (EFI_IP6_EQUAL (Ip6Address, &DefaultRouter->Router)) {\r
+      return DefaultRouter;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  The function to be called after DAD (Duplicate Address Detection) is performed.\r
+\r
+  @param[in]  IsDadPassed   If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.\r
+  @param[in]  IpIf          Points to the IP6_INTERFACE.\r
+  @param[in]  DadEntry      The DAD entry which already performed DAD.\r
+\r
+**/\r
+VOID\r
+Ip6OnDADFinished (\r
+  IN BOOLEAN        IsDadPassed,\r
+  IN IP6_INTERFACE  *IpIf,\r
+  IN IP6_DAD_ENTRY  *DadEntry\r
+  )\r
+{\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_ADDRESS_INFO          *AddrInfo;\r
+  EFI_DHCP6_PROTOCOL        *Dhcp6;\r
+  UINT16                    OptBuf[4];\r
+  EFI_DHCP6_PACKET_OPTION   *Oro;\r
+  EFI_DHCP6_RETRANSMISSION  InfoReqReXmit;\r
+\r
+  IpSb     = IpIf->Service;\r
+  AddrInfo = DadEntry->AddressInfo;\r
+\r
+  if (IsDadPassed) {\r
+    //\r
+    // DAD succeed.\r
+    //\r
+    if (NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {\r
+      ASSERT (!IpSb->LinkLocalOk);\r
+\r
+      IP6_COPY_ADDRESS (&IpSb->LinkLocalAddr, &AddrInfo->Address);\r
+      IpSb->LinkLocalOk = TRUE;\r
+      IpIf->Configured  = TRUE;\r
+\r
+      //\r
+      // Check whether DHCP6 need to be started.\r
+      //\r
+      Dhcp6 = IpSb->Ip6ConfigInstance.Dhcp6;\r
+\r
+      if (IpSb->Dhcp6NeedStart) {\r
+        Dhcp6->Start (Dhcp6);\r
+        IpSb->Dhcp6NeedStart = FALSE;\r
+      }\r
+\r
+      if (IpSb->Dhcp6NeedInfoRequest) {\r
+        //\r
+        // Set the exta options to send. Here we only want the option request option\r
+        // with DNS SERVERS.\r
+        //\r
+        Oro         = (EFI_DHCP6_PACKET_OPTION *) OptBuf;\r
+        Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO);\r
+        Oro->OpLen  = HTONS (2);\r
+        *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);\r
+\r
+        InfoReqReXmit.Irt = 4;\r
+        InfoReqReXmit.Mrc = 64;\r
+        InfoReqReXmit.Mrt = 60;\r
+        InfoReqReXmit.Mrd = 0;\r
+\r
+        Dhcp6->InfoRequest (\r
+                 Dhcp6,\r
+                 TRUE,\r
+                 Oro,\r
+                 0,\r
+                 NULL,\r
+                 &InfoReqReXmit,\r
+                 IpSb->Ip6ConfigInstance.Dhcp6Event,\r
+                 Ip6ConfigOnDhcp6Reply,\r
+                 &IpSb->Ip6ConfigInstance\r
+                 );\r
+      }\r
+\r
+      //\r
+      // Add an on-link prefix for link-local address.\r
+      //\r
+      Ip6CreatePrefixListEntry (\r
+        IpSb,\r
+        TRUE,\r
+        (UINT32) IP6_INFINIT_LIFETIME,\r
+        (UINT32) IP6_INFINIT_LIFETIME,\r
+        IP6_LINK_LOCAL_PREFIX_LENGTH,\r
+        &IpSb->LinkLocalAddr\r
+        );\r
+\r
+    } else {\r
+      //\r
+      // Global scope unicast address.\r
+      //\r
+      Ip6AddAddr (IpIf, AddrInfo);\r
+\r
+      //\r
+      // Add an on-link prefix for this address.\r
+      //\r
+      Ip6CreatePrefixListEntry (\r
+        IpSb,\r
+        TRUE,\r
+        AddrInfo->ValidLifetime,\r
+        AddrInfo->PreferredLifetime,\r
+        AddrInfo->PrefixLength,\r
+        &AddrInfo->Address\r
+        );\r
+\r
+      IpIf->Configured = TRUE;\r
+    }\r
+  } else {\r
+    //\r
+    // Leave the group we joined before.\r
+    //\r
+    Ip6LeaveGroup (IpSb, &DadEntry->Destination);\r
+  }\r
+\r
+  if (DadEntry->Callback != NULL) {\r
+    DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context);\r
+  }\r
+\r
+  if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {\r
+    FreePool (AddrInfo);\r
+    RemoveEntryList (&DadEntry->Link);\r
+    FreePool (DadEntry);\r
+    //\r
+    // Disable IP operation since link-local address is a duplicate address.\r
+    //\r
+    IpSb->LinkLocalDadFail = TRUE;\r
+    IpSb->Mnp->Configure (IpSb->Mnp, NULL);\r
+    gBS->SetTimer (IpSb->Timer, TimerCancel, 0);\r
+    gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);\r
+    return ;\r
+  }\r
+\r
+  if (!IsDadPassed || NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {\r
+    //\r
+    // Free the AddressInfo we hold if DAD fails or it is a link-local address.\r
+    //\r
+    FreePool (AddrInfo);\r
+  }\r
+\r
+  RemoveEntryList (&DadEntry->Link);\r
+  FreePool (DadEntry);\r
+}\r
+\r
+/**\r
+  Create a DAD (Duplicate Address Detection) entry and queue it to be performed.\r
+\r
+  @param[in]  IpIf          Points to the IP6_INTERFACE.\r
+  @param[in]  AddressInfo   The address information which needs DAD performed.\r
+  @param[in]  Callback      The callback routine that will be called after DAD\r
+                            is performed. This is an optional parameter that\r
+                            may be NULL.\r
+  @param[in]  Context       The opaque parameter for a DAD callback routine.\r
+                            This is an optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS           The DAD entry was created and queued.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory to complete the\r
+                                operation.\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InitDADProcess (\r
+  IN IP6_INTERFACE          *IpIf,\r
+  IN IP6_ADDRESS_INFO       *AddressInfo,\r
+  IN IP6_DAD_CALLBACK       Callback  OPTIONAL,\r
+  IN VOID                   *Context  OPTIONAL\r
+  )\r
+{\r
+  IP6_DAD_ENTRY                             *Entry;\r
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  *DadXmits;\r
+  IP6_SERVICE                               *IpSb;\r
+  EFI_STATUS                                Status;\r
+  UINT32                                    MaxDelayTick;\r
+\r
+  NET_CHECK_SIGNATURE (IpIf, IP6_INTERFACE_SIGNATURE);\r
+  ASSERT (AddressInfo != NULL);\r
+\r
+  Status   = EFI_SUCCESS;\r
+  IpSb     = IpIf->Service;\r
+  DadXmits = &IpSb->Ip6ConfigInstance.DadXmits;\r
+\r
+  //\r
+  // Allocate the resources and insert info\r
+  //\r
+  Entry = AllocatePool (sizeof (IP6_DAD_ENTRY));\r
+  if (Entry == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Map the incoming unicast address to solicited-node multicast address\r
+  //\r
+  Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination);\r
+\r
+  //\r
+  // Join in the solicited-node multicast address.\r
+  //\r
+  Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Entry);\r
+    return Status;\r
+  }\r
+\r
+  Entry->Signature    = IP6_DAD_ENTRY_SIGNATURE;\r
+  Entry->MaxTransmit  = DadXmits->DupAddrDetectTransmits;\r
+  Entry->Transmit     = 0;\r
+  Entry->Receive      = 0;\r
+  MaxDelayTick        = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS;\r
+  Entry->RetransTick  = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5;\r
+  Entry->AddressInfo  = AddressInfo;\r
+  Entry->Callback     = Callback;\r
+  Entry->Context      = Context;\r
+  InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link);\r
+\r
+  if (Entry->MaxTransmit == 0) {\r
+    //\r
+    // DAD is disabled on this interface, immediately mark this DAD successful.\r
+    //\r
+    Ip6OnDADFinished (TRUE, IpIf, Entry);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Search IP6_DAD_ENTRY from the Duplicate Address Detection List.\r
+\r
+  @param[in]  IpSb          The pointer to the IP6_SERVICE instance.\r
+  @param[in]  Target        The address information which needs DAD performed .\r
+  @param[out] Interface     If not NULL, output the IP6 interface that configures\r
+                            the tentative address.\r
+\r
+  @return NULL if failed to find the matching DAD entry.\r
+          Otherwise, point to the found DAD entry.\r
+\r
+**/\r
+IP6_DAD_ENTRY *\r
+Ip6FindDADEntry (\r
+  IN  IP6_SERVICE      *IpSb,\r
+  IN  EFI_IPv6_ADDRESS *Target,\r
+  OUT IP6_INTERFACE    **Interface OPTIONAL\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Entry2;\r
+  IP6_INTERFACE             *IpIf;\r
+  IP6_DAD_ENTRY             *DupAddrDetect;\r
+  IP6_ADDRESS_INFO          *AddrInfo;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+    NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) {\r
+      DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);\r
+      AddrInfo      = DupAddrDetect->AddressInfo;\r
+      if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) {\r
+        if (Interface != NULL) {\r
+          *Interface = IpIf;\r
+        }\r
+        return DupAddrDetect;\r
+      }\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Generate router solicit message and send it out to Destination Address or\r
+  All Router Link Local scope multicast address.\r
+\r
+  @param[in]  IpSb               The IP service to send the packet.\r
+  @param[in]  Interface          If not NULL, points to the IP6 interface to send\r
+                                 the packet.\r
+  @param[in]  SourceAddress      If not NULL, the source address of the message.\r
+  @param[in]  DestinationAddress If not NULL, the destination address of the message.\r
+  @param[in]  SourceLinkAddress  If not NULL, the MAC address of the source.\r
+                                 A source link-layer address option will be appended\r
+                                 to the message.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The router solicit message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendRouterSolicit (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_INTERFACE          *Interface          OPTIONAL,\r
+  IN EFI_IPv6_ADDRESS       *SourceAddress      OPTIONAL,\r
+  IN EFI_IPv6_ADDRESS       *DestinationAddress OPTIONAL,\r
+  IN EFI_MAC_ADDRESS        *SourceLinkAddress  OPTIONAL\r
+  )\r
+{\r
+  NET_BUF                   *Packet;\r
+  EFI_IP6_HEADER            Head;\r
+  IP6_ICMP_INFORMATION_HEAD *IcmpHead;\r
+  IP6_ETHER_ADDR_OPTION     *LinkLayerOption;\r
+  UINT16                    PayloadLen;\r
+  IP6_INTERFACE             *IpIf;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  IpIf = Interface;\r
+  if (IpIf == NULL && IpSb->DefaultInterface != NULL) {\r
+    IpIf = IpSb->DefaultInterface;\r
+  }\r
+\r
+  //\r
+  // Generate the packet to be sent\r
+  //\r
+\r
+  PayloadLen = (UINT16) sizeof (IP6_ICMP_INFORMATION_HEAD);\r
+  if (SourceLinkAddress != NULL) {\r
+    PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION);\r
+  }\r
+\r
+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Create the basic IPv6 header.\r
+  //\r
+  Head.FlowLabelL     = 0;\r
+  Head.FlowLabelH     = 0;\r
+  Head.PayloadLength  = HTONS (PayloadLen);\r
+  Head.NextHeader     = IP6_ICMP;\r
+  Head.HopLimit       = IP6_HOP_LIMIT;\r
+\r
+  if (SourceAddress != NULL) {\r
+    IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);\r
+  } else {\r
+    ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
+  }\r
+\r
+\r
+  if (DestinationAddress != NULL) {\r
+    IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);\r
+  } else {\r
+    Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress);\r
+  }\r
+\r
+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+  //\r
+  // Fill in the ICMP header, and Source link-layer address if contained.\r
+  //\r
+\r
+  IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);\r
+  ASSERT (IcmpHead != NULL);\r
+  ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));\r
+  IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT;\r
+  IcmpHead->Head.Code = 0;\r
+\r
+  LinkLayerOption = NULL;\r
+  if (SourceLinkAddress != NULL) {\r
+    LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (\r
+                                                  Packet,\r
+                                                  sizeof (IP6_ETHER_ADDR_OPTION),\r
+                                                  FALSE\r
+                                                  );\r
+    ASSERT (LinkLayerOption != NULL);\r
+    LinkLayerOption->Type   = Ip6OptionEtherSource;\r
+    LinkLayerOption->Length = (UINT8) sizeof (IP6_ETHER_ADDR_OPTION);\r
+    CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);\r
+  }\r
+\r
+  //\r
+  // Transmit the packet\r
+  //\r
+  return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+  Generate a Neighbor Advertisement message and send it out to Destination Address.\r
+\r
+  @param[in]  IpSb               The IP service to send the packet.\r
+  @param[in]  SourceAddress      The source address of the message.\r
+  @param[in]  DestinationAddress The destination address of the message.\r
+  @param[in]  TargetIp6Address   The target address field in the Neighbor Solicitation\r
+                                 message that prompted this advertisement.\r
+  @param[in]  TargetLinkAddress  The MAC address for the target, i.e. the sender\r
+                                 of the advertisement.\r
+  @param[in]  IsRouter           If TRUE, indicates the sender is a router.\r
+  @param[in]  Override           If TRUE, indicates the advertisement should override\r
+                                 an existing cache entry and update the MAC address.\r
+  @param[in]  Solicited          If TRUE, indicates the advertisement was sent\r
+                                 in response to a Neighbor Solicitation from\r
+                                 the Destination address.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendNeighborAdvertise (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *SourceAddress,\r
+  IN EFI_IPv6_ADDRESS       *DestinationAddress,\r
+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,\r
+  IN EFI_MAC_ADDRESS        *TargetLinkAddress,\r
+  IN BOOLEAN                IsRouter,\r
+  IN BOOLEAN                Override,\r
+  IN BOOLEAN                Solicited\r
+  )\r
+{\r
+  NET_BUF                   *Packet;\r
+  EFI_IP6_HEADER            Head;\r
+  IP6_ICMP_INFORMATION_HEAD *IcmpHead;\r
+  IP6_ETHER_ADDR_OPTION     *LinkLayerOption;\r
+  EFI_IPv6_ADDRESS          *Target;\r
+  UINT16                    PayloadLen;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  //\r
+  // The Neighbor Advertisement message must include a Target link-layer address option\r
+  // when responding to multicast solicitation and should include such option when\r
+  // responding to unicast solicitation. It also must include such option as unsolicited\r
+  // advertisement.\r
+  //\r
+  ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL);\r
+\r
+  PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION));\r
+\r
+  //\r
+  // Generate the packet to be sent\r
+  //\r
+\r
+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Create the basic IPv6 header.\r
+  //\r
+  Head.FlowLabelL     = 0;\r
+  Head.FlowLabelH     = 0;\r
+  Head.PayloadLength  = HTONS (PayloadLen);\r
+  Head.NextHeader     = IP6_ICMP;\r
+  Head.HopLimit       = IP6_HOP_LIMIT;\r
+\r
+  IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);\r
+  IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);\r
+\r
+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+  //\r
+  // Fill in the ICMP header, Target address, and Target link-layer address.\r
+  // Set the Router flag, Solicited flag and Override flag.\r
+  //\r
+\r
+  IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);\r
+  ASSERT (IcmpHead != NULL);\r
+  ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));\r
+  IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE;\r
+  IcmpHead->Head.Code = 0;\r
+\r
+  if (IsRouter) {\r
+    IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG;\r
+  }\r
+\r
+  if (Solicited) {\r
+    IcmpHead->Fourth |= IP6_SOLICITED_FLAG;\r
+  }\r
+\r
+  if (Override) {\r
+    IcmpHead->Fourth |= IP6_OVERRIDE_FLAG;\r
+  }\r
+\r
+  Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);\r
+  ASSERT (Target != NULL);\r
+  IP6_COPY_ADDRESS (Target, TargetIp6Address);\r
+\r
+  LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (\r
+                                                Packet,\r
+                                                sizeof (IP6_ETHER_ADDR_OPTION),\r
+                                                FALSE\r
+                                                );\r
+  ASSERT (LinkLayerOption != NULL);\r
+  LinkLayerOption->Type   = Ip6OptionEtherTarget;\r
+  LinkLayerOption->Length = 1;\r
+  CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6);\r
+\r
+  //\r
+  // Transmit the packet\r
+  //\r
+  return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+  Generate the Neighbor Solicitation message and send it to the Destination Address.\r
+\r
+  @param[in]  IpSb               The IP service to send the packet\r
+  @param[in]  SourceAddress      The source address of the message.\r
+  @param[in]  DestinationAddress The destination address of the message.\r
+  @param[in]  TargetIp6Address   The IP address of the target of the solicitation.\r
+                                 It must not be a multicast address.\r
+  @param[in]  SourceLinkAddress  The MAC address for the sender. If not NULL,\r
+                                 a source link-layer address option will be appended\r
+                                 to the message.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendNeighborSolicit (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *SourceAddress,\r
+  IN EFI_IPv6_ADDRESS       *DestinationAddress,\r
+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,\r
+  IN EFI_MAC_ADDRESS        *SourceLinkAddress OPTIONAL\r
+  )\r
+{\r
+  NET_BUF                   *Packet;\r
+  EFI_IP6_HEADER            Head;\r
+  IP6_ICMP_INFORMATION_HEAD *IcmpHead;\r
+  IP6_ETHER_ADDR_OPTION     *LinkLayerOption;\r
+  EFI_IPv6_ADDRESS          *Target;\r
+  BOOLEAN                   IsDAD;\r
+  UINT16                    PayloadLen;\r
+  IP6_NEIGHBOR_ENTRY        *Neighbor;\r
+\r
+  //\r
+  // Check input parameters\r
+  //\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  if (DestinationAddress == NULL || TargetIp6Address == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IsDAD = FALSE;\r
+\r
+  if (SourceAddress == NULL || (SourceAddress != NULL && NetIp6IsUnspecifiedAddr (SourceAddress))) {\r
+    IsDAD = TRUE;\r
+  }\r
+\r
+  //\r
+  // The Neighbor Solicitation message should include a source link-layer address option\r
+  // if the solicitation is not sent by performing DAD - Duplicate Address Detection.\r
+  // Otherwise must not include it.\r
+  //\r
+  PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  if (!IsDAD) {\r
+    if (SourceLinkAddress == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    PayloadLen = (UINT16) (PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION));\r
+  }\r
+\r
+  //\r
+  // Generate the packet to be sent\r
+  //\r
+\r
+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Create the basic IPv6 header\r
+  //\r
+  Head.FlowLabelL     = 0;\r
+  Head.FlowLabelH     = 0;\r
+  Head.PayloadLength  = HTONS (PayloadLen);\r
+  Head.NextHeader     = IP6_ICMP;\r
+  Head.HopLimit       = IP6_HOP_LIMIT;\r
+\r
+  if (SourceAddress != NULL) {\r
+    IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);\r
+  } else {\r
+    ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
+  }\r
+\r
+  IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);\r
+\r
+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+  //\r
+  // Fill in the ICMP header, Target address, and Source link-layer address.\r
+  //\r
+  IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);\r
+  ASSERT (IcmpHead != NULL);\r
+  ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));\r
+  IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT;\r
+  IcmpHead->Head.Code = 0;\r
+\r
+  Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);\r
+  ASSERT (Target != NULL);\r
+  IP6_COPY_ADDRESS (Target, TargetIp6Address);\r
+\r
+  LinkLayerOption = NULL;\r
+  if (!IsDAD) {\r
+\r
+    //\r
+    // Fill in the source link-layer address option\r
+    //\r
+    LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (\r
+                                                  Packet,\r
+                                                  sizeof (IP6_ETHER_ADDR_OPTION),\r
+                                                  FALSE\r
+                                                  );\r
+    ASSERT (LinkLayerOption != NULL);\r
+    LinkLayerOption->Type   = Ip6OptionEtherSource;\r
+    LinkLayerOption->Length = 1;\r
+    CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);\r
+  }\r
+\r
+  //\r
+  // Create a Neighbor Cache entry in the INCOMPLETE state when performing\r
+  // address resolution.\r
+  //\r
+  if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) {\r
+    Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);\r
+    if (Neighbor == NULL) {\r
+      Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL);\r
+      ASSERT (Neighbor != NULL);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Transmit the packet\r
+  //\r
+  return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+  Process the Neighbor Solicitation message. The message may be sent for Duplicate\r
+  Address Detection or Address Resolution.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the message.\r
+  @param[in]  Packet             The content of the message with IP head removed.\r
+\r
+  @retval EFI_SUCCESS            The packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.\r
+  @retval Others                 Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessNeighborSolicit (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_INFORMATION_HEAD Icmp;\r
+  EFI_IPv6_ADDRESS          Target;\r
+  IP6_ETHER_ADDR_OPTION     LinkLayerOption;\r
+  BOOLEAN                   IsDAD;\r
+  BOOLEAN                   IsUnicast;\r
+  BOOLEAN                   IsMaintained;\r
+  IP6_DAD_ENTRY             *DupAddrDetect;\r
+  IP6_INTERFACE             *IpIf;\r
+  IP6_NEIGHBOR_ENTRY        *Neighbor;\r
+  BOOLEAN                   Solicited;\r
+  BOOLEAN                   UpdateCache;\r
+  EFI_IPv6_ADDRESS          Dest;\r
+  UINT16                    OptionLen;\r
+  UINT8                     *Option;\r
+  BOOLEAN                   Provided;\r
+  EFI_STATUS                Status;\r
+  VOID                      *MacAddress;\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+  NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);\r
+\r
+  //\r
+  // Perform Message Validation:\r
+  // The IP Hop Limit field has a value of 255, i.e., the packet\r
+  // could not possibly have been forwarded by a router.\r
+  // ICMP Code is 0.\r
+  // Target Address is not a multicast address.\r
+  //\r
+  Status = EFI_INVALID_PARAMETER;\r
+\r
+  if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // ICMP length is 24 or more octets.\r
+  //\r
+  OptionLen = 0;\r
+  if (Head->PayloadLength < IP6_ND_LENGTH) {\r
+    goto Exit;\r
+  } else {\r
+    OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);\r
+    Option    = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);\r
+\r
+    //\r
+    // All included options should have a length that is greater than zero.\r
+    //\r
+    if (!Ip6IsNDOptionValid (Option, OptionLen)) {\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  IsDAD        = NetIp6IsUnspecifiedAddr (&Head->SourceAddress);\r
+  IsUnicast    = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress);\r
+  IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL);\r
+\r
+  Provided = FALSE;\r
+  if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {\r
+    NetbufCopy (\r
+      Packet,\r
+      IP6_ND_LENGTH,\r
+      sizeof (IP6_ETHER_ADDR_OPTION),\r
+      (UINT8 *) &LinkLayerOption\r
+      );\r
+    //\r
+    // The solicitation for neighbor discovery should include a source link-layer\r
+    // address option. If the option is not recognized, silently ignore it.\r
+    //\r
+    if (LinkLayerOption.Type == Ip6OptionEtherSource) {\r
+      if (IsDAD) {\r
+        //\r
+        // If the IP source address is the unspecified address, the source\r
+        // link-layer address option must not be included in the message.\r
+        //\r
+        goto Exit;\r
+      }\r
+\r
+      Provided = TRUE;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If the IP source address is the unspecified address, the IP\r
+  // destination address is a solicited-node multicast address.\r
+  //\r
+  if (IsDAD && IsUnicast) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // If the target address is tentative, and the source address is a unicast address,\r
+  // the solicitation's sender is performing address resolution on the target;\r
+  //  the solicitation should be silently ignored.\r
+  //\r
+  if (!IsDAD && !IsMaintained) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // If received unicast neighbor solicitation but destination is not this node,\r
+  // drop the packet.\r
+  //\r
+  if (IsUnicast && !IsMaintained) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // In DAD, when target address is a tentative address,\r
+  // process the received neighbor solicitation message but not send out response.\r
+  //\r
+  if (IsDAD && !IsMaintained) {\r
+    DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);\r
+    if (DupAddrDetect != NULL) {\r
+      if (DupAddrDetect->Transmit == 0) {\r
+        //\r
+        // The NS is from another node to performing DAD on the same address since\r
+        // we haven't send out any NS yet. Fail DAD for the tentative address.\r
+        //\r
+        Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);\r
+        Status = EFI_ICMP_ERROR;\r
+        goto Exit;\r
+      }\r
+\r
+      //\r
+      // Check the MAC address of the incoming packet.\r
+      //\r
+      if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) {\r
+        goto Exit;\r
+      }\r
+\r
+      MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress;\r
+      if (MacAddress != NULL) {\r
+        if (CompareMem (\r
+              MacAddress,\r
+              &IpSb->SnpMode.CurrentAddress,\r
+              IpSb->SnpMode.HwAddressSize\r
+              ) != 0) {\r
+          //\r
+          // The NS is from another node to performing DAD on the same address.\r
+          // Fail DAD for the tentative address.\r
+          //\r
+          Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);\r
+          Status = EFI_ICMP_ERROR;\r
+        } else {\r
+          //\r
+          // The below layer loopback the NS we sent. Record it and wait for more.\r
+          //\r
+          DupAddrDetect->Receive++;\r
+          Status = EFI_SUCCESS;\r
+        }\r
+      }\r
+    }\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // If the solicitation does not contain a link-layer address, DO NOT create or\r
+  // update the neighbor cache entries.\r
+  //\r
+  if (Provided) {\r
+    Neighbor    = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);\r
+    UpdateCache = FALSE;\r
+\r
+    if (Neighbor == NULL) {\r
+      Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL);\r
+      if (Neighbor == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Exit;\r
+      }\r
+      UpdateCache = TRUE;\r
+    } else {\r
+      if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) {\r
+        UpdateCache = TRUE;\r
+      }\r
+    }\r
+\r
+    if (UpdateCache) {\r
+      Neighbor->State = EfiNeighborStale;\r
+      Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+      CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);\r
+      //\r
+      // Send queued packets if exist.\r
+      //\r
+      Neighbor->CallBack ((VOID *) Neighbor);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Sends a Neighbor Advertisement as response.\r
+  // Set the Router flag to zero since the node is a host.\r
+  // If the source address of the solicitation is unspeicifed, and target address\r
+  // is one of the maintained address, reply a unsolicited multicast advertisement.\r
+  //\r
+  if (IsDAD && IsMaintained) {\r
+    Solicited = FALSE;\r
+    Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest);\r
+  } else {\r
+    Solicited = TRUE;\r
+    IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress);\r
+  }\r
+\r
+  Status = Ip6SendNeighborAdvertise (\r
+             IpSb,\r
+             &Target,\r
+             &Dest,\r
+             &Target,\r
+             &IpSb->SnpMode.CurrentAddress,\r
+             FALSE,\r
+             TRUE,\r
+             Solicited\r
+             );\r
+Exit:\r
+  NetbufFree (Packet);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Process the Neighbor Advertisement message.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the message.\r
+  @param[in]  Packet             The content of the message with IP head removed.\r
+\r
+  @retval EFI_SUCCESS            The packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.\r
+  @retval Others                 Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessNeighborAdvertise (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_INFORMATION_HEAD Icmp;\r
+  EFI_IPv6_ADDRESS          Target;\r
+  IP6_ETHER_ADDR_OPTION     LinkLayerOption;\r
+  BOOLEAN                   Provided;\r
+  INTN                      Compare;\r
+  IP6_NEIGHBOR_ENTRY        *Neighbor;\r
+  IP6_DEFAULT_ROUTER        *DefaultRouter;\r
+  BOOLEAN                   Solicited;\r
+  BOOLEAN                   IsRouter;\r
+  BOOLEAN                   Override;\r
+  IP6_DAD_ENTRY             *DupAddrDetect;\r
+  IP6_INTERFACE             *IpIf;\r
+  UINT16                    OptionLen;\r
+  UINT8                     *Option;\r
+  EFI_STATUS                Status;\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+  NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);\r
+\r
+  //\r
+  // Validate the incoming Neighbor Advertisement\r
+  //\r
+  Status = EFI_INVALID_PARAMETER;\r
+  //\r
+  // The IP Hop Limit field has a value of 255, i.e., the packet\r
+  // could not possibly have been forwarded by a router.\r
+  // ICMP Code is 0.\r
+  // Target Address is not a multicast address.\r
+  //\r
+  if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // ICMP length is 24 or more octets.\r
+  //\r
+  Provided  = FALSE;\r
+  OptionLen = 0;\r
+  if (Head->PayloadLength < IP6_ND_LENGTH) {\r
+    goto Exit;\r
+  } else {\r
+    OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);\r
+    Option    = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);\r
+\r
+    //\r
+    // All included options should have a length that is greater than zero.\r
+    //\r
+    if (!Ip6IsNDOptionValid (Option, OptionLen)) {\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If the IP destination address is a multicast address, Solicited Flag is ZERO.\r
+  //\r
+  Solicited = FALSE;\r
+  if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) {\r
+    Solicited = TRUE;\r
+  }\r
+  if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // DAD - Check whether the Target is one of our tentative address.\r
+  //\r
+  DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);\r
+  if (DupAddrDetect != NULL) {\r
+    //\r
+    // DAD fails, some other node is using this address.\r
+    //\r
+    NetbufFree (Packet);\r
+    Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);\r
+    return EFI_ICMP_ERROR;\r
+  }\r
+\r
+  //\r
+  // Search the Neighbor Cache for the target's entry. If no entry exists,\r
+  // the advertisement should be silently discarded.\r
+  //\r
+  Neighbor = Ip6FindNeighborEntry (IpSb, &Target);\r
+  if (Neighbor == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Get IsRouter Flag and Override Flag\r
+  //\r
+  IsRouter = FALSE;\r
+  Override = FALSE;\r
+  if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) {\r
+    IsRouter = TRUE;\r
+  }\r
+  if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) {\r
+    Override = TRUE;\r
+  }\r
+\r
+  //\r
+  // Check whether link layer option is included.\r
+  //\r
+  if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {\r
+    NetbufCopy (\r
+      Packet,\r
+      IP6_ND_LENGTH,\r
+      sizeof (IP6_ETHER_ADDR_OPTION),\r
+      (UINT8 *) &LinkLayerOption\r
+      );\r
+\r
+    if (LinkLayerOption.Type == Ip6OptionEtherTarget) {\r
+      Provided = TRUE;\r
+    }\r
+  }\r
+\r
+  Compare = 0;\r
+  if (Provided) {\r
+    Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);\r
+  }\r
+\r
+  if (!Neighbor->IsRouter && IsRouter) {\r
+    DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);\r
+    if (DefaultRouter != NULL) {\r
+      DefaultRouter->NeighborCache = Neighbor;\r
+    }\r
+  }\r
+\r
+  if (Neighbor->State == EfiNeighborInComplete) {\r
+    //\r
+    // If the target's Neighbor Cache entry is in INCOMPLETE state and no\r
+    // Target Link-Layer address option is included while link layer has\r
+    // address, the message should be silently discarded.\r
+    //\r
+    if (!Provided) {\r
+      goto Exit;\r
+    }\r
+    //\r
+    // Update the Neighbor Cache\r
+    //\r
+    CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);\r
+    if (Solicited) {\r
+      Neighbor->State = EfiNeighborReachable;\r
+      Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);\r
+    } else {\r
+      Neighbor->State = EfiNeighborStale;\r
+      Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+      //\r
+      // Send any packets queued for the neighbor awaiting address resolution.\r
+      //\r
+      Neighbor->CallBack ((VOID *) Neighbor);\r
+    }\r
+\r
+    Neighbor->IsRouter = IsRouter;\r
+\r
+  } else {\r
+    if (!Override && Compare != 0) {\r
+      //\r
+      // When the Override Flag is clear and supplied link-layer address differs from\r
+      // that in the cache, if the state of the entry is not REACHABLE, ignore the\r
+      // message. Otherwise set it to STALE but do not update the entry in any\r
+      // other way.\r
+      //\r
+      if (Neighbor->State == EfiNeighborReachable) {\r
+        Neighbor->State = EfiNeighborStale;\r
+        Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+      }\r
+    } else {\r
+      if (Compare != 0) {\r
+        CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);\r
+      }\r
+      //\r
+      // Update the entry's state\r
+      //\r
+      if (Solicited) {\r
+        Neighbor->State = EfiNeighborReachable;\r
+        Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);\r
+      } else {\r
+        if (Compare != 0) {\r
+          Neighbor->State = EfiNeighborStale;\r
+          Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+        }\r
+      }\r
+\r
+      //\r
+      // When IsRouter is changed from TRUE to FALSE, remove the router from the\r
+      // Default Router List and remove the Destination Cache entries for all destinations\r
+      // using the neighbor as a router.\r
+      //\r
+      if (Neighbor->IsRouter && !IsRouter) {\r
+        DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);\r
+        if (DefaultRouter != NULL) {\r
+          Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+        }\r
+      }\r
+\r
+      Neighbor->IsRouter = IsRouter;\r
+    }\r
+  }\r
+\r
+  if (Neighbor->State == EfiNeighborReachable) {\r
+    Neighbor->CallBack ((VOID *) Neighbor);\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+  NetbufFree (Packet);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Process the Router Advertisement message according to RFC4861.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the message.\r
+  @param[in]  Packet             The content of the message with the IP head removed.\r
+\r
+  @retval EFI_SUCCESS            The packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the\r
+                                 operation.\r
+  @retval Others                 Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessRouterAdvertise (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_INFORMATION_HEAD Icmp;\r
+  UINT32                    ReachableTime;\r
+  UINT32                    RetransTimer;\r
+  UINT16                    RouterLifetime;\r
+  UINT16                    Offset;\r
+  UINT8                     Type;\r
+  UINT8                     Length;\r
+  IP6_ETHER_ADDR_OPTION     LinkLayerOption;\r
+  UINT32                    Fourth;\r
+  UINT8                     CurHopLimit;\r
+  BOOLEAN                   Mflag;\r
+  BOOLEAN                   Oflag;\r
+  IP6_DEFAULT_ROUTER        *DefaultRouter;\r
+  IP6_NEIGHBOR_ENTRY        *NeighborCache;\r
+  EFI_MAC_ADDRESS           LinkLayerAddress;\r
+  IP6_MTU_OPTION            MTUOption;\r
+  IP6_PREFIX_INFO_OPTION    PrefixOption;\r
+  IP6_PREFIX_LIST_ENTRY     *PrefixList;\r
+  BOOLEAN                   OnLink;\r
+  BOOLEAN                   Autonomous;\r
+  EFI_IPv6_ADDRESS          StatelessAddress;\r
+  EFI_STATUS                Status;\r
+  UINT16                    OptionLen;\r
+  UINT8                     *Option;\r
+  INTN                      Result;\r
+\r
+  Status = EFI_INVALID_PARAMETER;\r
+\r
+  if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) {\r
+    //\r
+    // Skip the process below as it's not required under the current policy.\r
+    //\r
+    goto Exit;\r
+  }\r
+\r
+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+\r
+  //\r
+  // Validate the incoming Router Advertisement\r
+  //\r
+\r
+  //\r
+  // The IP source address must be a link-local address\r
+  //\r
+  if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
+    goto Exit;\r
+  }\r
+  //\r
+  // The IP Hop Limit field has a value of 255, i.e. the packet\r
+  // could not possibly have been forwarded by a router.\r
+  // ICMP Code is 0.\r
+  // ICMP length (derived from the IP length) is 16 or more octets.\r
+  //\r
+  if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 ||\r
+      Head->PayloadLength < IP6_RA_LENGTH) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // All included options have a length that is greater than zero.\r
+  //\r
+  OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH);\r
+  Option    = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL);\r
+\r
+  if (!Ip6IsNDOptionValid (Option, OptionLen)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Process Fourth field.\r
+  // In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag,\r
+  // and Router Lifetime (16 bit).\r
+  //\r
+\r
+  Fourth = NTOHL (Icmp.Fourth);\r
+  CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16));\r
+\r
+  //\r
+  // If the source address already in the default router list, update it.\r
+  // Otherwise create a new entry.\r
+  // A Lifetime of zero indicates that the router is not a default router.\r
+  //\r
+  DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress);\r
+  if (DefaultRouter == NULL) {\r
+    if (RouterLifetime != 0) {\r
+      DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime);\r
+      if (DefaultRouter == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Exit;\r
+      }\r
+    }\r
+  } else {\r
+    if (RouterLifetime != 0) {\r
+      DefaultRouter->Lifetime = RouterLifetime;\r
+      //\r
+      // Check the corresponding neighbor cache entry here.\r
+      //\r
+      if (DefaultRouter->NeighborCache == NULL) {\r
+        DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);\r
+      }\r
+    } else {\r
+      //\r
+      // If the address is in the host's default router list and the router lifetime is zero,\r
+      // immediately time-out the entry.\r
+      //\r
+      Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+    }\r
+  }\r
+\r
+  CurHopLimit = *((UINT8 *) &Fourth + 3);\r
+  if (CurHopLimit != 0) {\r
+    IpSb->CurHopLimit = CurHopLimit;\r
+  }\r
+\r
+  Mflag = FALSE;\r
+  Oflag = FALSE;\r
+  if ((*((UINT8 *) &Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) {\r
+    Mflag = TRUE;\r
+  } else {\r
+    if ((*((UINT8 *) &Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) {\r
+      Oflag = TRUE;\r
+    }\r
+  }\r
+\r
+  if (Mflag || Oflag) {\r
+    //\r
+    // Use Ip6Config to get available addresses or other configuration from DHCP.\r
+    //\r
+    Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag);\r
+  }\r
+\r
+  //\r
+  // Process Reachable Time and Retrans Timer fields.\r
+  //\r
+  NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *) &ReachableTime);\r
+  NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *) &RetransTimer);\r
+  ReachableTime = NTOHL (ReachableTime);\r
+  RetransTimer  = NTOHL (RetransTimer);\r
+\r
+  if (ReachableTime != 0 && ReachableTime != IpSb->BaseReachableTime) {\r
+    //\r
+    // If new value is not unspecified and differs from the previous one, record it\r
+    // in BaseReachableTime and recompute a ReachableTime.\r
+    //\r
+    IpSb->BaseReachableTime = ReachableTime;\r
+    Ip6UpdateReachableTime (IpSb);\r
+  }\r
+\r
+  if (RetransTimer != 0) {\r
+    IpSb->RetransTimer = RetransTimer;\r
+  }\r
+\r
+  //\r
+  // IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists.\r
+  //\r
+  NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);\r
+  if (NeighborCache != NULL) {\r
+    NeighborCache->IsRouter = TRUE;\r
+  }\r
+\r
+  //\r
+  // If an valid router advertisment is received, stops router solicitation.\r
+  //\r
+  IpSb->RouterAdvertiseReceived = TRUE;\r
+\r
+  //\r
+  // The only defined options that may appear are the Source\r
+  // Link-Layer Address, Prefix information and MTU options.\r
+  // All included options have a length that is greater than zero.\r
+  //\r
+  Offset = 16;\r
+  while (Offset < Head->PayloadLength) {\r
+    NetbufCopy (Packet, Offset, sizeof (UINT8), &Type);\r
+    switch (Type) {\r
+    case Ip6OptionEtherSource:\r
+      //\r
+      // Update the neighbor cache\r
+      //\r
+      NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption);\r
+      if (LinkLayerOption.Length <= 0) {\r
+        goto Exit;\r
+      }\r
+\r
+      ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS));\r
+      CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6);\r
+\r
+      if (NeighborCache == NULL) {\r
+        NeighborCache = Ip6CreateNeighborEntry (\r
+                          IpSb,\r
+                          Ip6OnArpResolved,\r
+                          &Head->SourceAddress,\r
+                          &LinkLayerAddress\r
+                          );\r
+        if (NeighborCache == NULL) {\r
+          Status = EFI_OUT_OF_RESOURCES;\r
+          goto Exit;\r
+        }\r
+        NeighborCache->IsRouter = TRUE;\r
+        NeighborCache->State    = EfiNeighborStale;\r
+        NeighborCache->Ticks    = (UINT32) IP6_INFINIT_LIFETIME;\r
+      } else {\r
+        Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6);\r
+\r
+        //\r
+        // If the link-local address is the same as that already in the cache,\r
+        // the cache entry's state remains unchanged. Otherwise update the\r
+        // reachability state to STALE.\r
+        //\r
+        if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {\r
+          CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6);\r
+\r
+          NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+\r
+          if (NeighborCache->State == EfiNeighborInComplete) {\r
+            //\r
+            // Send queued packets if exist.\r
+            //\r
+            NeighborCache->State = EfiNeighborStale;\r
+            NeighborCache->CallBack ((VOID *) NeighborCache);\r
+          } else {\r
+            NeighborCache->State = EfiNeighborStale;\r
+          }\r
+        }\r
+      }\r
+\r
+      Offset = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8);\r
+      break;\r
+    case Ip6OptionPrefixInfo:\r
+      NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption);\r
+      if (PrefixOption.Length != 4) {\r
+        goto Exit;\r
+      }\r
+      PrefixOption.ValidLifetime     = NTOHL (PrefixOption.ValidLifetime);\r
+      PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime);\r
+\r
+      //\r
+      // Get L and A flag, recorded in the lower 2 bits of Reserved1\r
+      //\r
+      OnLink = FALSE;\r
+      if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) {\r
+        OnLink = TRUE;\r
+      }\r
+      Autonomous = FALSE;\r
+      if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) {\r
+        Autonomous = TRUE;\r
+      }\r
+\r
+      //\r
+      // If the prefix is the link-local prefix, silently ignore the prefix option.\r
+      //\r
+      if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH &&\r
+          NetIp6IsLinkLocalAddr (&PrefixOption.Prefix)\r
+          ) {\r
+        Offset += sizeof (IP6_PREFIX_INFO_OPTION);\r
+        break;\r
+      }\r
+      //\r
+      // Do following if on-link flag is set according to RFC4861.\r
+      //\r
+      if (OnLink) {\r
+        PrefixList = Ip6FindPrefixListEntry (\r
+                       IpSb,\r
+                       TRUE,\r
+                       PrefixOption.PrefixLength,\r
+                       &PrefixOption.Prefix\r
+                       );\r
+        //\r
+        // Create a new entry for the prefix, if the ValidLifetime is zero,\r
+        // silently ignore the prefix option.\r
+        //\r
+        if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) {\r
+          PrefixList = Ip6CreatePrefixListEntry (\r
+                         IpSb,\r
+                         TRUE,\r
+                         PrefixOption.ValidLifetime,\r
+                         PrefixOption.PreferredLifetime,\r
+                         PrefixOption.PrefixLength,\r
+                         &PrefixOption.Prefix\r
+                         );\r
+          if (PrefixList == NULL) {\r
+            Status = EFI_OUT_OF_RESOURCES;\r
+            goto Exit;\r
+          }\r
+        } else if (PrefixList != NULL) {\r
+          if (PrefixOption.ValidLifetime != 0) {\r
+            PrefixList->ValidLifetime = PrefixOption.ValidLifetime;\r
+          } else {\r
+            //\r
+            // If the prefix exists and incoming ValidLifetime is zero, immediately\r
+            // remove the prefix.\r
+            Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);\r
+          }\r
+        }\r
+      }\r
+\r
+      //\r
+      // Do following if Autonomous flag is set according to RFC4862.\r
+      //\r
+      if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) {\r
+        PrefixList = Ip6FindPrefixListEntry (\r
+                       IpSb,\r
+                       FALSE,\r
+                       PrefixOption.PrefixLength,\r
+                       &PrefixOption.Prefix\r
+                       );\r
+        //\r
+        // Create a new entry for the prefix, and form an address by prefix + interface id\r
+        // If the sum of the prefix length and interface identifier length\r
+        // does not equal 128 bits, the Prefix Information option MUST be ignored.\r
+        //\r
+        if (PrefixList == NULL &&\r
+            PrefixOption.ValidLifetime != 0 &&\r
+            PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128\r
+            ) {\r
+          //\r
+          // Form the address in network order.\r
+          //\r
+          CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64));\r
+          CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64));\r
+\r
+          //\r
+          // If the address is not yet in the assigned address list, adds it into.\r
+          //\r
+          if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) {\r
+            //\r
+            // And also not in the DAD process, check its uniqeness firstly.\r
+            //\r
+            if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) {\r
+              Status = Ip6SetAddress (\r
+                         IpSb->DefaultInterface,\r
+                         &StatelessAddress,\r
+                         FALSE,\r
+                         PrefixOption.PrefixLength,\r
+                         PrefixOption.ValidLifetime,\r
+                         PrefixOption.PreferredLifetime,\r
+                         NULL,\r
+                         NULL\r
+                         );\r
+              if (EFI_ERROR (Status)) {\r
+                goto Exit;\r
+              }\r
+            }\r
+          }\r
+\r
+          //\r
+          // Adds the prefix option to stateless prefix option list.\r
+          //\r
+          PrefixList = Ip6CreatePrefixListEntry (\r
+                         IpSb,\r
+                         FALSE,\r
+                         PrefixOption.ValidLifetime,\r
+                         PrefixOption.PreferredLifetime,\r
+                         PrefixOption.PrefixLength,\r
+                         &PrefixOption.Prefix\r
+                         );\r
+          if (PrefixList == NULL) {\r
+            Status = EFI_OUT_OF_RESOURCES;\r
+            goto Exit;\r
+          }\r
+        } else if (PrefixList != NULL) {\r
+\r
+          //\r
+          // Reset the preferred lifetime of the address if the advertised prefix exists.\r
+          // Perform specific action to valid lifetime together.\r
+          //\r
+          PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime;\r
+          if ((PrefixOption.ValidLifetime > 7200) ||\r
+              (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) {\r
+            //\r
+            // If the received Valid Lifetime is greater than 2 hours or\r
+            // greater than RemainingLifetime, set the valid lifetime of the\r
+            // corresponding address to the advertised Valid Lifetime.\r
+            //\r
+            PrefixList->ValidLifetime = PrefixOption.ValidLifetime;\r
+\r
+          } else if (PrefixList->ValidLifetime <= 7200) {\r
+            //\r
+            // If RemainingLifetime is less than or equls to 2 hours, ignore the\r
+            // Prefix Information option with regards to the valid lifetime.\r
+            // TODO: If this option has been authenticated, set the valid lifetime.\r
+            //\r
+          } else {\r
+            //\r
+            // Otherwise, reset the valid lifetime of the corresponding\r
+            // address to 2 hours.\r
+            //\r
+            PrefixList->ValidLifetime = 7200;\r
+          }\r
+        }\r
+      }\r
+\r
+      Offset += sizeof (IP6_PREFIX_INFO_OPTION);\r
+      break;\r
+    case Ip6OptionMtu:\r
+      NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption);\r
+      if (MTUOption.Length != 1) {\r
+        goto Exit;\r
+      }\r
+\r
+      //\r
+      // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order\r
+      // to omit implementation of Path MTU Discovery. Thus ignore the MTU option\r
+      // in Router Advertisement.\r
+      //\r
+\r
+      Offset += sizeof (IP6_MTU_OPTION);\r
+      break;\r
+    default:\r
+      //\r
+      // Silently ignore unrecognized options\r
+      //\r
+      NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length);\r
+      if (Length <= 0) {\r
+        goto Exit;\r
+      }\r
+\r
+      Offset = (UINT16) (Offset + (UINT16) Length * 8);\r
+      break;\r
+    }\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+  NetbufFree (Packet);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Process the ICMPv6 redirect message. Find the instance, then update\r
+  its route cache.\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance that received\r
+                                 the packet.\r
+  @param[in]  Head               The IP head of the received ICMPv6 packet.\r
+  @param[in]  Packet             The content of the ICMPv6 redirect packet with\r
+                                 the IP head removed.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Insuffcient resources to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            Successfully updated the route caches.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessRedirect (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  IP6_ICMP_INFORMATION_HEAD *Icmp;\r
+  EFI_IPv6_ADDRESS          *Target;\r
+  EFI_IPv6_ADDRESS          *IcmpDest;\r
+  UINT8                     *Option;\r
+  UINT16                    OptionLen;\r
+  IP6_ROUTE_ENTRY           *RouteEntry;\r
+  IP6_ROUTE_CACHE_ENTRY     *RouteCache;\r
+  IP6_NEIGHBOR_ENTRY        *NeighborCache;\r
+  INT32                     Length;\r
+  UINT8                     OptLen;\r
+  IP6_ETHER_ADDR_OPTION     *LinkLayerOption;\r
+  EFI_MAC_ADDRESS           Mac;\r
+  UINT32                    Index;\r
+  BOOLEAN                   IsRouter;\r
+  EFI_STATUS                Status;\r
+  INTN                      Result;\r
+\r
+  Status = EFI_INVALID_PARAMETER;\r
+\r
+  Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+  if (Icmp == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Validate the incoming Redirect message\r
+  //\r
+\r
+  //\r
+  // The IP Hop Limit field has a value of 255, i.e. the packet\r
+  // could not possibly have been forwarded by a router.\r
+  // ICMP Code is 0.\r
+  // ICMP length (derived from the IP length) is 40 or more octets.\r
+  //\r
+  if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 ||\r
+      Head->PayloadLength < IP6_REDITECT_LENGTH) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // The IP source address must be a link-local address\r
+  //\r
+  if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // The dest of this ICMP redirect message is not us.\r
+  //\r
+  if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // All included options have a length that is greater than zero.\r
+  //\r
+  OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH);\r
+  Option    = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL);\r
+\r
+  if (!Ip6IsNDOptionValid (Option, OptionLen)) {\r
+    goto Exit;\r
+  }\r
+\r
+  Target   = (EFI_IPv6_ADDRESS *) (Icmp + 1);\r
+  IcmpDest = Target + 1;\r
+\r
+  //\r
+  // The ICMP Destination Address field in the redirect message does not contain\r
+  // a multicast address.\r
+  //\r
+  if (IP6_IS_MULTICAST (IcmpDest)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // The ICMP Target Address is either a link-local address (when redirected to\r
+  // a router) or the same as the ICMP Destination Address (when redirected to\r
+  // the on-link destination).\r
+  //\r
+  IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest);\r
+  if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Check the options. The only interested option here is the target-link layer\r
+  // address option.\r
+  //\r
+  Length          = Packet->TotalSize - 40;\r
+  Option          = (UINT8 *) (IcmpDest + 1);\r
+  LinkLayerOption = NULL;\r
+  while (Length > 0) {\r
+    switch (*Option) {\r
+    case Ip6OptionEtherTarget:\r
+\r
+      LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) Option;\r
+      OptLen          = LinkLayerOption->Length;\r
+      if (OptLen != 1) {\r
+        //\r
+        // For ethernet, the length must be 1.\r
+        //\r
+        goto Exit;\r
+      }\r
+      break;\r
+\r
+    default:\r
+\r
+      OptLen = *(Option + 1);\r
+      if (OptLen == 0) {\r
+        //\r
+        // A length of 0 is invalid.\r
+        //\r
+        goto Exit;\r
+      }\r
+      break;\r
+    }\r
+\r
+    Length -= 8 * OptLen;\r
+    Option += 8 * OptLen;\r
+  }\r
+\r
+  if (Length != 0) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // The IP source address of the Redirect is the same as the current\r
+  // first-hop router for the specified ICMP Destination Address.\r
+  //\r
+  RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress);\r
+  if (RouteCache != NULL) {\r
+    if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) {\r
+      //\r
+      // The source of this Redirect message must match the NextHop of the\r
+      // corresponding route cache entry.\r
+      //\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Update the NextHop.\r
+    //\r
+    IP6_COPY_ADDRESS (&RouteCache->NextHop, Target);\r
+\r
+    if (!IsRouter) {\r
+      RouteEntry = (IP6_ROUTE_ENTRY *) RouteCache->Tag;\r
+      RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE;\r
+    }\r
+\r
+  } else {\r
+    //\r
+    // Get the Route Entry.\r
+    //\r
+    RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL);\r
+    if (RouteEntry == NULL) {\r
+      RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL);\r
+      if (RouteEntry == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Exit;\r
+      }\r
+    }\r
+\r
+    if (!IsRouter) {\r
+      RouteEntry->Flag = IP6_DIRECT_ROUTE;\r
+    }\r
+\r
+    //\r
+    // Create a route cache for this.\r
+    //\r
+    RouteCache = Ip6CreateRouteCacheEntry (\r
+                   IcmpDest,\r
+                   &Head->DestinationAddress,\r
+                   Target,\r
+                   (UINTN) RouteEntry\r
+                   );\r
+    if (RouteCache == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Insert the newly created route cache entry.\r
+    //\r
+    Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress);\r
+    InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link);\r
+  }\r
+\r
+  //\r
+  // Try to locate the neighbor cache for the Target.\r
+  //\r
+  NeighborCache = Ip6FindNeighborEntry (IpSb, Target);\r
+\r
+  if (LinkLayerOption != NULL) {\r
+    if (NeighborCache == NULL) {\r
+      //\r
+      // Create a neighbor cache for the Target.\r
+      //\r
+      ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS));\r
+      CopyMem (&Mac, LinkLayerOption->EtherAddr, 6);\r
+      NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac);\r
+      if (NeighborCache == NULL) {\r
+        //\r
+        // Just report a success here. The neighbor cache can be created in\r
+        // some other place.\r
+        //\r
+        Status = EFI_SUCCESS;\r
+        goto Exit;\r
+      }\r
+\r
+      NeighborCache->State = EfiNeighborStale;\r
+      NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+    } else {\r
+      Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6);\r
+\r
+      //\r
+      // If the link-local address is the same as that already in the cache,\r
+      // the cache entry's state remains unchanged. Otherwise update the\r
+      // reachability state to STALE.\r
+      //\r
+      if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {\r
+        CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6);\r
+\r
+        NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+\r
+        if (NeighborCache->State == EfiNeighborInComplete) {\r
+          //\r
+          // Send queued packets if exist.\r
+          //\r
+          NeighborCache->State = EfiNeighborStale;\r
+          NeighborCache->CallBack ((VOID *) NeighborCache);\r
+        } else {\r
+          NeighborCache->State = EfiNeighborStale;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  if (NeighborCache != NULL && IsRouter) {\r
+    //\r
+    // The Target is a router, set IsRouter to TRUE.\r
+    //\r
+    NeighborCache->IsRouter = TRUE;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+  NetbufFree (Packet);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance.\r
+  @param[in]  TargetIp6Address   Pointer to Target IPv6 address.\r
+  @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.\r
+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor\r
+                                 cache. It will be deleted after Timeout. A value of zero means that\r
+                                 the entry is permanent. A non-zero value means that the entry is\r
+                                 dynamic.\r
+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will\r
+                                 be overridden and updated; if FALSE, and if a\r
+                                 corresponding cache entry already existed, EFI_ACCESS_DENIED\r
+                                 will be returned.\r
+\r
+  @retval  EFI_SUCCESS           The neighbor cache entry has been added.\r
+  @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache\r
+                                 due to insufficient resources.\r
+  @retval  EFI_NOT_FOUND         TargetLinkAddress is NULL.\r
+  @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,\r
+                                 and that entry is tagged as un-overridden (when DeleteFlag\r
+                                 is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6AddNeighbor (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,\r
+  IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,\r
+  IN UINT32                 Timeout,\r
+  IN BOOLEAN                Override\r
+  )\r
+{\r
+  IP6_NEIGHBOR_ENTRY        *Neighbor;\r
+\r
+  Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);\r
+  if (Neighbor != NULL) {\r
+    if (!Override) {\r
+      return EFI_ACCESS_DENIED;\r
+    } else {\r
+      if (TargetLinkAddress != NULL) {\r
+        IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress);\r
+      }\r
+    }\r
+  } else {\r
+    if (TargetLinkAddress == NULL) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+\r
+    Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress);\r
+    if (Neighbor == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+  Neighbor->State = EfiNeighborReachable;\r
+\r
+  if (Timeout != 0) {\r
+    Neighbor->Ticks   = IP6_GET_TICKS (Timeout / TICKS_PER_MS);\r
+    Neighbor->Dynamic = TRUE;\r
+  } else {\r
+    Neighbor->Ticks   = (UINT32) IP6_INFINIT_LIFETIME;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance.\r
+  @param[in]  TargetIp6Address   Pointer to Target IPv6 address.\r
+  @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.\r
+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor\r
+                                 cache. It will be deleted after Timeout. A value of zero means that\r
+                                 the entry is permanent. A non-zero value means that the entry is\r
+                                 dynamic.\r
+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will\r
+                                 be overridden and updated; if FALSE, and if a\r
+                                 corresponding cache entry already existed, EFI_ACCESS_DENIED\r
+                                 will be returned.\r
+\r
+  @retval  EFI_SUCCESS           The neighbor cache entry has been updated or deleted.\r
+  @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6DelNeighbor (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,\r
+  IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,\r
+  IN UINT32                 Timeout,\r
+  IN BOOLEAN                Override\r
+  )\r
+{\r
+  IP6_NEIGHBOR_ENTRY        *Neighbor;\r
+\r
+  Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);\r
+  if (Neighbor == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  RemoveEntryList (&Neighbor->Link);\r
+  FreePool (Neighbor);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.\r
+  This time routine handles DAD module and neighbor state transition.\r
+  It is also responsible for sending out router solicitations.\r
+\r
+  @param[in]  Event                 The IP6 service instance's heartbeat timer.\r
+  @param[in]  Context               The IP6 service instance.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6NdFasterTimerTicking (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  LIST_ENTRY                *Entry2;\r
+  IP6_INTERFACE             *IpIf;\r
+  IP6_DELAY_JOIN_LIST       *DelayNode;\r
+  EFI_IPv6_ADDRESS          Source;\r
+  IP6_DAD_ENTRY             *DupAddrDetect;\r
+  EFI_STATUS                Status;\r
+  IP6_NEIGHBOR_ENTRY        *NeighborCache;\r
+  EFI_IPv6_ADDRESS          Destination;\r
+  IP6_SERVICE               *IpSb;\r
+  BOOLEAN                   Flag;\r
+\r
+  IpSb = (IP6_SERVICE *) Context;\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  //\r
+  // A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router\r
+  // Solicitation messages, each separated by at least\r
+  // RTR_SOLICITATION_INTERVAL (4) seconds.\r
+  //\r
+  if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) &&\r
+      !IpSb->RouterAdvertiseReceived &&\r
+      IpSb->SolicitTimer > 0\r
+      ) {\r
+    if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) {\r
+      Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL);\r
+      if (!EFI_ERROR (Status)) {\r
+        IpSb->SolicitTimer--;\r
+        IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL);\r
+      }\r
+    }\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+    //\r
+    // Process the delay list to join the solicited-node multicast address.\r
+    //\r
+    NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) {\r
+      DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link);\r
+      if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) {\r
+        //\r
+        // The timer expires, init the duplicate address detection.\r
+        //\r
+        Ip6InitDADProcess (\r
+          DelayNode->Interface,\r
+          DelayNode->AddressInfo,\r
+          DelayNode->DadCallback,\r
+          DelayNode->Context\r
+          );\r
+\r
+        //\r
+        // Remove the delay node\r
+        //\r
+        RemoveEntryList (&DelayNode->Link);\r
+        FreePool (DelayNode);\r
+      }\r
+    }\r
+\r
+    //\r
+    // Process the duplicate address detection list.\r
+    //\r
+    NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {\r
+      DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link);\r
+\r
+      if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) {\r
+        //\r
+        // The timer expires, check the remaining transmit counts.\r
+        //\r
+        if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) {\r
+          //\r
+          // Send the Neighbor Solicitation message with\r
+          // Source - unspecified address, destination - solicited-node multicast address\r
+          // Target - the address to be validated\r
+          //\r
+          Status = Ip6SendNeighborSolicit (\r
+                     IpSb,\r
+                     NULL,\r
+                     &DupAddrDetect->Destination,\r
+                     &DupAddrDetect->AddressInfo->Address,\r
+                     NULL\r
+                     );\r
+          if (EFI_ERROR (Status)) {\r
+            return;\r
+          }\r
+\r
+          DupAddrDetect->Transmit++;\r
+          DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer);\r
+        } else {\r
+          //\r
+          // All required solicitation has been sent out, and the RetransTime after the last\r
+          // Neighbor Solicit is elapsed, finish the DAD process.\r
+          //\r
+          Flag = FALSE;\r
+          if ((DupAddrDetect->Receive == 0) ||\r
+              (DupAddrDetect->Transmit == DupAddrDetect->Receive)) {\r
+            Flag = TRUE;\r
+          }\r
+\r
+          Ip6OnDADFinished (Flag, IpIf, DupAddrDetect);\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Polling the state of Neighbor cache\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {\r
+    NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);\r
+\r
+    switch (NeighborCache->State) {\r
+    case EfiNeighborInComplete:\r
+      if (NeighborCache->Ticks > 0) {\r
+        --NeighborCache->Ticks;\r
+      }\r
+\r
+      //\r
+      // Retransmit Neighbor Solicitation messages approximately every\r
+      // RetransTimer milliseconds while awaiting a response.\r
+      //\r
+      if (NeighborCache->Ticks == 0) {\r
+        if (NeighborCache->Transmit > 1) {\r
+          //\r
+          // Send out multicast neighbor solicitation for address resolution.\r
+          // After last neighbor solicitation message has been sent out, wait\r
+          // for RetransTimer and then remove entry if no response is received.\r
+          //\r
+          Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);\r
+          Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);\r
+          if (EFI_ERROR (Status)) {\r
+            return;\r
+          }\r
+\r
+          Status = Ip6SendNeighborSolicit (\r
+                     IpSb,\r
+                     &Source,\r
+                     &Destination,\r
+                     &NeighborCache->Neighbor,\r
+                     &IpSb->SnpMode.CurrentAddress\r
+                     );\r
+          if (EFI_ERROR (Status)) {\r
+            return;\r
+          }\r
+        }\r
+\r
+        //\r
+        // Update the retransmit times.\r
+        //\r
+        if (NeighborCache->Transmit > 0) {\r
+          --NeighborCache->Transmit;\r
+          NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);\r
+        }\r
+      }\r
+\r
+      if (NeighborCache->Transmit == 0) {\r
+        //\r
+        // Timeout, send ICMP destination unreachable packet and then remove entry\r
+        //\r
+        Status = Ip6FreeNeighborEntry (\r
+                   IpSb,\r
+                   NeighborCache,\r
+                   TRUE,\r
+                   TRUE,\r
+                   EFI_ICMP_ERROR,\r
+                   NULL,\r
+                   NULL\r
+                   );\r
+        if (EFI_ERROR (Status)) {\r
+          return;\r
+        }\r
+      }\r
+\r
+      break;\r
+\r
+    case EfiNeighborReachable:\r
+      //\r
+      // This entry is inserted by EfiIp6Neighbors() as static entry\r
+      // and will not timeout.\r
+      //\r
+      if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) {\r
+        break;\r
+      }\r
+\r
+      if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {\r
+        if (NeighborCache->Dynamic) {\r
+          //\r
+          // This entry is inserted by EfiIp6Neighbors() as dynamic entry\r
+          // and will be deleted after timeout.\r
+          //\r
+          Status = Ip6FreeNeighborEntry (\r
+                     IpSb,\r
+                     NeighborCache,\r
+                     FALSE,\r
+                     TRUE,\r
+                     EFI_TIMEOUT,\r
+                     NULL,\r
+                     NULL\r
+                     );\r
+          if (EFI_ERROR (Status)) {\r
+            return;\r
+          }\r
+        } else {\r
+          NeighborCache->State = EfiNeighborStale;\r
+          NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+        }\r
+      }\r
+\r
+      break;\r
+\r
+    case EfiNeighborDelay:\r
+      if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {\r
+\r
+        NeighborCache->State    = EfiNeighborProbe;\r
+        NeighborCache->Ticks    = IP6_GET_TICKS (IpSb->RetransTimer);\r
+        NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1;\r
+        //\r
+        // Send out unicast neighbor solicitation for Neighbor Unreachability Detection\r
+        //\r
+        Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);\r
+        if (EFI_ERROR (Status)) {\r
+          return;\r
+        }\r
+\r
+        Status = Ip6SendNeighborSolicit (\r
+                   IpSb,\r
+                   &Source,\r
+                   &NeighborCache->Neighbor,\r
+                   &NeighborCache->Neighbor,\r
+                   &IpSb->SnpMode.CurrentAddress\r
+                   );\r
+        if (EFI_ERROR (Status)) {\r
+          return;\r
+        }\r
+\r
+        NeighborCache->Transmit--;\r
+      }\r
+\r
+      break;\r
+\r
+    case EfiNeighborProbe:\r
+      if (NeighborCache->Ticks > 0) {\r
+        --NeighborCache->Ticks;\r
+      }\r
+\r
+      //\r
+      // Retransmit Neighbor Solicitation messages approximately every\r
+      // RetransTimer milliseconds while awaiting a response.\r
+      //\r
+      if (NeighborCache->Ticks == 0) {\r
+        if (NeighborCache->Transmit > 1) {\r
+          //\r
+          // Send out unicast neighbor solicitation for Neighbor Unreachability\r
+          // Detection. After last neighbor solicitation message has been sent out,\r
+          // wait for RetransTimer and then remove entry if no response is received.\r
+          //\r
+          Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);\r
+          if (EFI_ERROR (Status)) {\r
+            return;\r
+          }\r
+\r
+          Status = Ip6SendNeighborSolicit (\r
+                     IpSb,\r
+                     &Source,\r
+                     &NeighborCache->Neighbor,\r
+                     &NeighborCache->Neighbor,\r
+                     &IpSb->SnpMode.CurrentAddress\r
+                     );\r
+          if (EFI_ERROR (Status)) {\r
+            return;\r
+          }\r
+        }\r
+\r
+        //\r
+        // Update the retransmit times.\r
+        //\r
+        if (NeighborCache->Transmit > 0) {\r
+          --NeighborCache->Transmit;\r
+          NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);\r
+        }\r
+      }\r
+\r
+      if (NeighborCache->Transmit == 0) {\r
+        //\r
+        // Delete the neighbor entry.\r
+        //\r
+        Status = Ip6FreeNeighborEntry (\r
+                   IpSb,\r
+                   NeighborCache,\r
+                   FALSE,\r
+                   TRUE,\r
+                   EFI_TIMEOUT,\r
+                   NULL,\r
+                   NULL\r
+                   );\r
+        if (EFI_ERROR (Status)) {\r
+          return;\r
+        }\r
+      }\r
+\r
+      break;\r
+\r
+    default:\r
+      break;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  The heartbeat timer of ND module in 1 second. This time routine handles following\r
+  things: 1) maitain default router list; 2) maintain prefix options;\r
+  3) maintain route caches.\r
+\r
+  @param[in]  IpSb              The IP6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6NdTimerTicking (\r
+  IN IP6_SERVICE            *IpSb\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_DEFAULT_ROUTER        *DefaultRouter;\r
+  IP6_PREFIX_LIST_ENTRY     *PrefixOption;\r
+  UINT8                     Index;\r
+  IP6_ROUTE_CACHE_ENTRY     *RouteCache;\r
+\r
+  //\r
+  // Decrease the lifetime of default router, if expires remove it from default router list.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) {\r
+    DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);\r
+    if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) {\r
+      if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) {\r
+        Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) {\r
+    PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+    if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {\r
+      if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) {\r
+        if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) &&\r
+            (PrefixOption->PreferredLifetime > 0)\r
+            ) {\r
+          --PrefixOption->PreferredLifetime;\r
+        }\r
+      } else {\r
+        Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE);\r
+      }\r
+    }\r
+  }\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) {\r
+    PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+    if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {\r
+      if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) {\r
+        Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE);\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries.\r
+  // Remove the entries at the tail of the bucket. These entries\r
+  // are likely to be used least.\r
+  // Reclaim frequency is set to 1 second.\r
+  //\r
+  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {\r
+    while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) {\r
+      Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]);\r
+      if (Entry == NULL) {\r
+        break;\r
+      }\r
+\r
+      RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);\r
+      Ip6FreeRouteCacheEntry (RouteCache);\r
+      ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0);\r
+      IpSb->RouteTable->Cache.CacheNum[Index]--;\r
+    }\r
+  }\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Nd.h b/NetworkPkg/Ip6Dxe/Ip6Nd.h
new file mode 100644 (file)
index 0000000..57fb8fe
--- /dev/null
@@ -0,0 +1,750 @@
+/** @file\r
+  Definition of Neighbor Discovery support routines.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_ND_H__\r
+#define __EFI_IP6_ND_H__\r
+\r
+#define IP6_GET_TICKS(Ms)  (((Ms) + IP6_TIMER_INTERVAL_IN_MS - 1) / IP6_TIMER_INTERVAL_IN_MS)\r
+\r
+enum {\r
+  IP6_INF_ROUTER_LIFETIME        = 0xFFFF,\r
+\r
+  IP6_MAX_RTR_SOLICITATION_DELAY = 1000, ///< 1000 milliseconds\r
+  IP6_MAX_RTR_SOLICITATIONS      = 3,\r
+  IP6_RTR_SOLICITATION_INTERVAL  = 4000,\r
+\r
+  IP6_MIN_RANDOM_FACTOR_SCALED   = 1,\r
+  IP6_MAX_RANDOM_FACTOR_SCALED   = 3,\r
+  IP6_RANDOM_FACTOR_SCALE        = 2,\r
+\r
+  IP6_MAX_MULTICAST_SOLICIT      = 3,\r
+  IP6_MAX_UNICAST_SOLICIT        = 3,\r
+  IP6_MAX_ANYCAST_DELAY_TIME     = 1,\r
+  IP6_MAX_NEIGHBOR_ADV           = 3,\r
+  IP6_REACHABLE_TIME             = 30000,\r
+  IP6_RETRANS_TIMER              = 1000,\r
+  IP6_DELAY_FIRST_PROBE_TIME     = 5000,\r
+\r
+  IP6_MIN_LINK_MTU               = 1280,\r
+  IP6_MAX_LINK_MTU               = 1500,\r
+\r
+  IP6_IS_ROUTER_FLAG             = 0x80,\r
+  IP6_SOLICITED_FLAG             = 0x40,\r
+  IP6_OVERRIDE_FLAG              = 0x20,\r
+\r
+  IP6_M_ADDR_CONFIG_FLAG         = 0x80,\r
+  IP6_O_CONFIG_FLAG              = 0x40,\r
+\r
+  IP6_ON_LINK_FLAG               = 0x80,\r
+  IP6_AUTO_CONFIG_FLAG           = 0x40,\r
+\r
+  IP6_ND_LENGTH                  = 24,\r
+  IP6_RA_LENGTH                  = 16,\r
+  IP6_REDITECT_LENGTH            = 40,\r
+  IP6_DAD_ENTRY_SIGNATURE        = SIGNATURE_32 ('I', 'P', 'D', 'E')\r
+};\r
+\r
+typedef\r
+VOID\r
+(*IP6_ARP_CALLBACK) (\r
+  VOID                      *Context\r
+  );\r
+\r
+typedef struct _IP6_ETHE_ADDR_OPTION {\r
+  UINT8                     Type;\r
+  UINT8                     Length;\r
+  UINT8                     EtherAddr[6];\r
+} IP6_ETHER_ADDR_OPTION;\r
+\r
+typedef struct _IP6_MTU_OPTION {\r
+  UINT8                     Type;\r
+  UINT8                     Length;\r
+  UINT16                    Reserved;\r
+  UINT32                    Mtu;\r
+} IP6_MTU_OPTION;\r
+\r
+typedef struct _IP6_PREFIX_INFO_OPTION {\r
+  UINT8                     Type;\r
+  UINT8                     Length;\r
+  UINT8                     PrefixLength;\r
+  UINT8                     Reserved1;\r
+  UINT32                    ValidLifetime;\r
+  UINT32                    PreferredLifetime;\r
+  UINT32                    Reserved2;\r
+  EFI_IPv6_ADDRESS          Prefix;\r
+} IP6_PREFIX_INFO_OPTION;\r
+\r
+typedef\r
+VOID\r
+(*IP6_DAD_CALLBACK) (\r
+  IN BOOLEAN           IsDadPassed,\r
+  IN EFI_IPv6_ADDRESS  *TargetAddress,\r
+  IN VOID              *Context\r
+  );\r
+\r
+typedef struct _IP6_DAD_ENTRY {\r
+  UINT32                    Signature;\r
+  LIST_ENTRY                Link;\r
+  UINT32                    MaxTransmit;\r
+  UINT32                    Transmit;\r
+  UINT32                    Receive;\r
+  UINT32                    RetransTick;\r
+  IP6_ADDRESS_INFO          *AddressInfo;\r
+  EFI_IPv6_ADDRESS          Destination;\r
+  IP6_DAD_CALLBACK          Callback;\r
+  VOID                      *Context;\r
+} IP6_DAD_ENTRY;\r
+\r
+typedef struct _IP6_DELAY_JOIN_LIST {\r
+  LIST_ENTRY                Link;\r
+  UINT32                    DelayTime; ///< in tick per 50 milliseconds\r
+  IP6_INTERFACE             *Interface;\r
+  IP6_ADDRESS_INFO          *AddressInfo;\r
+  IP6_DAD_CALLBACK          DadCallback;\r
+  VOID                      *Context;\r
+} IP6_DELAY_JOIN_LIST;\r
+\r
+typedef struct _IP6_NEIGHBOR_ENTRY {\r
+  LIST_ENTRY                Link;\r
+  LIST_ENTRY                ArpList;\r
+  INTN                      RefCnt;\r
+  BOOLEAN                   IsRouter;\r
+  BOOLEAN                   ArpFree;\r
+  BOOLEAN                   Dynamic;\r
+  EFI_IPv6_ADDRESS          Neighbor;\r
+  EFI_MAC_ADDRESS           LinkAddress;\r
+  EFI_IP6_NEIGHBOR_STATE    State;\r
+  UINT32                    Transmit;\r
+  UINT32                    Ticks;\r
+\r
+  LIST_ENTRY                Frames;\r
+  IP6_INTERFACE             *Interface;\r
+  IP6_ARP_CALLBACK          CallBack;\r
+} IP6_NEIGHBOR_ENTRY;\r
+\r
+typedef struct _IP6_DEFAULT_ROUTER {\r
+  LIST_ENTRY                Link;\r
+  INTN                      RefCnt;\r
+  UINT16                    Lifetime;\r
+  EFI_IPv6_ADDRESS          Router;\r
+  IP6_NEIGHBOR_ENTRY        *NeighborCache;\r
+} IP6_DEFAULT_ROUTER;\r
+\r
+typedef struct _IP6_PREFIX_LIST_ENTRY {\r
+  LIST_ENTRY                Link;\r
+  INTN                      RefCnt;\r
+  UINT32                    ValidLifetime;\r
+  UINT32                    PreferredLifetime;\r
+  UINT8                     PrefixLength;\r
+  EFI_IPv6_ADDRESS          Prefix;\r
+} IP6_PREFIX_LIST_ENTRY;\r
+\r
+/**\r
+  Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number\r
+  of EFI_IP6_NEIGHBOR_CACHE is also returned.\r
+\r
+  @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.\r
+  @param[out] NeighborCount     The number of returned neighbor cache entries.\r
+  @param[out] NeighborCache     The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.\r
+\r
+  @retval EFI_SUCCESS           The EFI_IP6_NEIGHBOR_CACHE successfully built.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiNeighborCache (\r
+  IN IP6_PROTOCOL            *IpInstance,\r
+  OUT UINT32                 *NeighborCount,\r
+  OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache\r
+  );\r
+\r
+/**\r
+  Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number\r
+  of prefix entries is also returned.\r
+\r
+  @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.\r
+  @param[out] PrefixCount       The number of returned prefix entries.\r
+  @param[out] PrefixTable       The pointer to the array of PrefixTable.\r
+\r
+  @retval EFI_SUCCESS           The prefix table successfully built.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the prefix table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildPrefixTable (\r
+  IN IP6_PROTOCOL           *IpInstance,\r
+  OUT UINT32                *PrefixCount,\r
+  OUT EFI_IP6_ADDRESS_INFO  **PrefixTable\r
+  );\r
+\r
+/**\r
+  Allocate and initialize an IP6 default router entry.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  Ip6Address        The IPv6 address of the default router.\r
+  @param[in]  RouterLifetime    The lifetime associated with the default\r
+                                router, in units of seconds.\r
+\r
+  @return NULL if it failed to allocate memory for the default router node.\r
+          Otherwise, point to the created default router node.\r
+\r
+**/\r
+IP6_DEFAULT_ROUTER *\r
+Ip6CreateDefaultRouter (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Address,\r
+  IN UINT16                 RouterLifetime\r
+  );\r
+\r
+/**\r
+  Destroy an IP6 default router entry.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  DefaultRouter     The to be destroyed IP6_DEFAULT_ROUTER.\r
+\r
+**/\r
+VOID\r
+Ip6DestroyDefaultRouter (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_DEFAULT_ROUTER     *DefaultRouter\r
+  );\r
+\r
+/**\r
+  Clean an IP6 default router list.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  DefaultRouter     The to be destroyed IP6_DEFAULT_ROUTER.\r
+\r
+**/\r
+VOID\r
+Ip6CleanDefaultRouterList (\r
+  IN IP6_SERVICE            *IpSb\r
+  );\r
+\r
+/**\r
+  Search a default router node from an IP6 default router list.\r
+\r
+  @param[in]  IpSb          The pointer to the IP6_SERVICE instance.\r
+  @param[in]  Ip6Address    The IPv6 address of the to be searched default router node.\r
+\r
+  @return NULL if it failed to find the matching default router node.\r
+          Otherwise, point to the found default router node.\r
+\r
+**/\r
+IP6_DEFAULT_ROUTER *\r
+Ip6FindDefaultRouter (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Address\r
+  );\r
+\r
+/**\r
+  The function to be called after DAD (Duplicate Address Detection) is performed.\r
+\r
+  @param[in]  IsDadPassed   If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.\r
+  @param[in]  IpIf          Points to the IP6_INTERFACE.\r
+  @param[in]  DadEntry      The DAD entry which already performed DAD.\r
+\r
+**/\r
+VOID\r
+Ip6OnDADFinished (\r
+  IN BOOLEAN        IsDadPassed,\r
+  IN IP6_INTERFACE  *IpIf,\r
+  IN IP6_DAD_ENTRY  *DadEntry\r
+  );\r
+\r
+/**\r
+  Create a DAD (Duplicate Address Detection) entry and queue it to be performed.\r
+\r
+  @param[in]  IpIf          Points to the IP6_INTERFACE.\r
+  @param[in]  AddressInfo   The address information which needs DAD performed.\r
+  @param[in]  Callback      The callback routine that will be called after DAD\r
+                            is performed. This is an optional parameter that\r
+                            may be NULL.\r
+  @param[in]  Context       The opaque parameter for a DAD callback routine.\r
+                            This is an optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS           The DAD entry was created and queued.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory to complete the\r
+                                operation.\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InitDADProcess (\r
+  IN IP6_INTERFACE          *IpIf,\r
+  IN IP6_ADDRESS_INFO       *AddressInfo,\r
+  IN IP6_DAD_CALLBACK       Callback  OPTIONAL,\r
+  IN VOID                   *Context  OPTIONAL\r
+  );\r
+\r
+/**\r
+  Search IP6_DAD_ENTRY from the Duplicate Address Detection List.\r
+\r
+  @param[in]  IpSb          The pointer to the IP6_SERVICE instance.\r
+  @param[in]  Target        The address information which needs DAD performed .\r
+  @param[out] Interface     If not NULL, output the IP6 interface that configures\r
+                            the tentative address.\r
+\r
+  @return NULL if failed to find the matching DAD entry.\r
+          Otherwise, point to the found DAD entry.\r
+\r
+**/\r
+IP6_DAD_ENTRY *\r
+Ip6FindDADEntry (\r
+  IN  IP6_SERVICE      *IpSb,\r
+  IN  EFI_IPv6_ADDRESS *Target,\r
+  OUT IP6_INTERFACE    **Interface OPTIONAL\r
+  );\r
+\r
+/**\r
+  Allocate and initialize a IP6 prefix list entry.\r
+\r
+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.\r
+  @param[in]  OnLinkOrAuto      If TRUE, the entry is created for the on link prefix list.\r
+                                Otherwise, it is created for the autoconfiguration prefix list.\r
+  @param[in]  ValidLifetime     The length of time in seconds that the prefix\r
+                                is valid for the purpose of on-link determination.\r
+  @param[in]  PreferredLifetime The length of time in seconds that addresses\r
+                                generated from the prefix via stateless address\r
+                                autoconfiguration remain preferred.\r
+  @param[in]  PrefixLength      The prefix length of the Prefix.\r
+  @param[in]  Prefix            The prefix address.\r
+\r
+  @return NULL if it failed to allocate memory for the prefix node. Otherwise, point\r
+          to the created or existing prefix list entry.\r
+\r
+**/\r
+IP6_PREFIX_LIST_ENTRY *\r
+Ip6CreatePrefixListEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN BOOLEAN                OnLinkOrAuto,\r
+  IN UINT32                 ValidLifetime,\r
+  IN UINT32                 PreferredLifetime,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *Prefix\r
+  );\r
+\r
+/**\r
+  Destory a IP6 prefix list entry.\r
+\r
+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.\r
+  @param[in]  PrefixEntry       The to be destroyed prefix list entry.\r
+  @param[in]  OnLinkOrAuto      If TRUE, the entry is removed from on link prefix list.\r
+                                Otherwise remove from autoconfiguration prefix list.\r
+  @param[in]  ImmediateDelete   If TRUE, remove the entry directly.\r
+                                Otherwise, check the reference count to see whether\r
+                                it should be removed.\r
+\r
+**/\r
+VOID\r
+Ip6DestroyPrefixListEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_PREFIX_LIST_ENTRY  *PrefixEntry,\r
+  IN BOOLEAN                OnLinkOrAuto,\r
+  IN BOOLEAN                ImmediateDelete\r
+  );\r
+\r
+/**\r
+  Search the list array to find an IP6 prefix list entry.\r
+\r
+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.\r
+  @param[in]  OnLinkOrAuto      If TRUE, the search the link prefix list,\r
+                                Otherwise search the autoconfiguration prefix list.\r
+  @param[in]  PrefixLength      The prefix length of the Prefix\r
+  @param[in]  Prefix            The prefix address.\r
+\r
+  @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the\r
+          pointer to the IP6 prefix list entry.\r
+\r
+**/\r
+IP6_PREFIX_LIST_ENTRY *\r
+Ip6FindPrefixListEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN BOOLEAN                OnLinkOrAuto,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *Prefix\r
+  );\r
+\r
+/**\r
+  Release the resource in prefix list table, and destroy the list entry and\r
+  corresponding addresses or route entries.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  ListHead          The list entry head of the prefix list table.\r
+\r
+**/\r
+VOID\r
+Ip6CleanPrefixListTable (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN LIST_ENTRY             *ListHead\r
+  );\r
+\r
+/**\r
+  Allocate and initialize an IP6 neighbor cache entry.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  CallBack          The callback function to be called when\r
+                                address resolution is finished.\r
+  @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.\r
+  @param[in]  LinkAddress       Points to the MAC address of the neighbor.\r
+                                Ignored if NULL.\r
+\r
+  @return NULL if failed to allocate memory for the neighbor cache entry.\r
+          Otherwise, point to the created neighbor cache entry.\r
+\r
+**/\r
+IP6_NEIGHBOR_ENTRY *\r
+Ip6CreateNeighborEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_ARP_CALLBACK       CallBack,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Address,\r
+  IN EFI_MAC_ADDRESS        *LinkAddress OPTIONAL\r
+  );\r
+\r
+/**\r
+  Search a IP6 neighbor cache entry.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.\r
+\r
+  @return NULL if it failed to find the matching neighbor cache entry.\r
+          Otherwise, point to the found neighbor cache entry.\r
+\r
+**/\r
+IP6_NEIGHBOR_ENTRY *\r
+Ip6FindNeighborEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Address\r
+  );\r
+\r
+/**\r
+  Free a IP6 neighbor cache entry and remove all the frames on the address\r
+  resolution queue that pass the FrameToCancel. That is, either FrameToCancel\r
+  is NULL, or it returns true for the frame.\r
+\r
+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.\r
+  @param[in]  NeighborCache     The to be free neighbor cache entry.\r
+  @param[in]  SendIcmpError     If TRUE, send out ICMP error.\r
+  @param[in]  FullFree          If TRUE, remove the neighbor cache entry.\r
+                                Otherwise remove the pending frames.\r
+  @param[in]  IoStatus          The status returned to the cancelled frames'\r
+                                callback function.\r
+  @param[in]  FrameToCancel     Function to select which frame to cancel.\r
+                                This is an optional parameter that may be NULL.\r
+  @param[in]  Context           Opaque parameter to the FrameToCancel.\r
+                                Ignored if FrameToCancel is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER The input parameter is invalid.\r
+  @retval EFI_SUCCESS           The operation finished successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FreeNeighborEntry (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_NEIGHBOR_ENTRY     *NeighborCache,\r
+  IN BOOLEAN                SendIcmpError,\r
+  IN BOOLEAN                FullFree,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN IP6_FRAME_TO_CANCEL    FrameToCancel OPTIONAL,\r
+  IN VOID                   *Context      OPTIONAL\r
+  );\r
+\r
+/**\r
+  Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance.\r
+  @param[in]  TargetIp6Address   Pointer to Target IPv6 address.\r
+  @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.\r
+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor\r
+                                 cache. It will be deleted after Timeout. A value of zero means that\r
+                                 the entry is permanent. A non-zero value means that the entry is\r
+                                 dynamic.\r
+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will\r
+                                 be overridden and updated; if FALSE, and if a\r
+                                 corresponding cache entry already existed, EFI_ACCESS_DENIED\r
+                                 will be returned.\r
+\r
+  @retval  EFI_SUCCESS           The neighbor cache entry has been added.\r
+  @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache\r
+                                 due to insufficient resources.\r
+  @retval  EFI_NOT_FOUND         TargetLinkAddress is NULL.\r
+  @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,\r
+                                 and that entry is tagged as un-overridden (when DeleteFlag\r
+                                 is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6AddNeighbor (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,\r
+  IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,\r
+  IN UINT32                 Timeout,\r
+  IN BOOLEAN                Override\r
+  );\r
+\r
+/**\r
+  Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance.\r
+  @param[in]  TargetIp6Address   Pointer to Target IPv6 address.\r
+  @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.\r
+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor\r
+                                 cache. It will be deleted after Timeout. A value of zero means that\r
+                                 the entry is permanent. A non-zero value means that the entry is\r
+                                 dynamic.\r
+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will\r
+                                 be overridden and updated; if FALSE, and if a\r
+                                 corresponding cache entry already existed, EFI_ACCESS_DENIED\r
+                                 will be returned.\r
+\r
+  @retval  EFI_SUCCESS           The neighbor cache entry has been updated or deleted.\r
+  @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6DelNeighbor (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,\r
+  IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,\r
+  IN UINT32                 Timeout,\r
+  IN BOOLEAN                Override\r
+  );\r
+\r
+/**\r
+  Process the Neighbor Solicitation message. The message may be sent for Duplicate\r
+  Address Detection or Address Resolution.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the message.\r
+  @param[in]  Packet             The content of the message with IP head removed.\r
+\r
+  @retval EFI_SUCCESS            The packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.\r
+  @retval Others                 Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessNeighborSolicit (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  );\r
+\r
+/**\r
+  Process the Neighbor Advertisement message.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the message.\r
+  @param[in]  Packet             The content of the message with IP head removed.\r
+\r
+  @retval EFI_SUCCESS            The packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.\r
+  @retval Others                 Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessNeighborAdvertise (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  );\r
+\r
+/**\r
+  Process the Router Advertisement message according to RFC4861.\r
+\r
+  @param[in]  IpSb               The IP service that received the packet.\r
+  @param[in]  Head               The IP head of the message.\r
+  @param[in]  Packet             The content of the message with the IP head removed.\r
+\r
+  @retval EFI_SUCCESS            The packet processed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the operation.\r
+  @retval Others                 Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessRouterAdvertise (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  );\r
+\r
+/**\r
+  Process the ICMPv6 redirect message. Find the instance, then update\r
+  its route cache.\r
+\r
+  @param[in]  IpSb               The IP6 service binding instance that received\r
+                                 the packet.\r
+  @param[in]  Head               The IP head of the received ICMPv6 packet.\r
+  @param[in]  Packet             The content of the ICMPv6 redirect packet with\r
+                                 the IP head removed.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Insuffcient resources to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            Successfully updated the route caches.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessRedirect (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN NET_BUF                *Packet\r
+  );\r
+\r
+/**\r
+  Generate router solicit message and send it out to Destination Address or\r
+  All Router Link Local scope multicast address.\r
+\r
+  @param[in]  IpSb               The IP service to send the packet.\r
+  @param[in]  Interface          If not NULL, points to the IP6 interface to send\r
+                                 the packet.\r
+  @param[in]  SourceAddress      If not NULL, the source address of the message.\r
+  @param[in]  DestinationAddress If not NULL, the destination address of the message.\r
+  @param[in]  SourceLinkAddress  If not NULL, the MAC address of the source.\r
+                                 A source link-layer address option will be appended\r
+                                 to the message.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the operation.\r
+  @retval EFI_SUCCESS            The router solicit message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendRouterSolicit (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_INTERFACE          *Interface          OPTIONAL,\r
+  IN EFI_IPv6_ADDRESS       *SourceAddress      OPTIONAL,\r
+  IN EFI_IPv6_ADDRESS       *DestinationAddress OPTIONAL,\r
+  IN EFI_MAC_ADDRESS        *SourceLinkAddress  OPTIONAL\r
+  );\r
+\r
+/**\r
+  Generate the Neighbor Solicitation message and send it to the Destination Address.\r
+\r
+  @param[in]  IpSb               The IP service to send the packet\r
+  @param[in]  SourceAddress      The source address of the message.\r
+  @param[in]  DestinationAddress The destination address of the message.\r
+  @param[in]  TargetIp6Address   The IP address of the target of the solicitation.\r
+                                 It must not be a multicast address.\r
+  @param[in]  SourceLinkAddress  The MAC address for the sender. If not NULL,\r
+                                 a source link-layer address option will be appended\r
+                                 to the message.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendNeighborSolicit (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *SourceAddress,\r
+  IN EFI_IPv6_ADDRESS       *DestinationAddress,\r
+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,\r
+  IN EFI_MAC_ADDRESS        *SourceLinkAddress OPTIONAL\r
+  );\r
+\r
+/**\r
+  Set the interface's address. This will trigger the DAD process for the\r
+  address to set. To set an already set address, the lifetimes wil be\r
+  updated to the new value passed in.\r
+\r
+  @param[in]  Interface             The interface to set the address.\r
+  @param[in]  Ip6Addr               The interface's to be assigned IPv6 address.\r
+  @param[in]  IsAnycast             If TRUE, the unicast IPv6 address is anycast.\r
+                                    Otherwise, it is not anycast.\r
+  @param[in]  PrefixLength          The prefix length of the Ip6Addr.\r
+  @param[in]  ValidLifetime         The valid lifetime for this address.\r
+  @param[in]  PreferredLifetime     The preferred lifetime for this address.\r
+  @param[in]  DadCallback           The caller's callback to trigger when DAD finishes.\r
+                                    This is an optional parameter that may be NULL.\r
+  @param[in]  Context               The context that will be passed to DadCallback.\r
+                                    This is an optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS               The interface is scheduled to be configured with\r
+                                    the specified address.\r
+  @retval EFI_OUT_OF_RESOURCES      Failed to set the interface's address due to\r
+                                    lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetAddress (\r
+  IN IP6_INTERFACE          *Interface,\r
+  IN EFI_IPv6_ADDRESS       *Ip6Addr,\r
+  IN BOOLEAN                IsAnycast,\r
+  IN UINT8                  PrefixLength,\r
+  IN UINT32                 ValidLifetime,\r
+  IN UINT32                 PreferredLifetime,\r
+  IN IP6_DAD_CALLBACK       DadCallback  OPTIONAL,\r
+  IN VOID                   *Context     OPTIONAL\r
+  );\r
+\r
+/**\r
+  The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.\r
+  This time routine handles DAD module and neighbor state transition.\r
+  It is also responsible for sending out router solicitations.\r
+\r
+  @param[in]  Event                 The IP6 service instance's heartbeat timer.\r
+  @param[in]  Context               The IP6 service instance.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6NdFasterTimerTicking (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+/**\r
+  The heartbeat timer of ND module in 1 second. This time routine handles following\r
+  things: 1) maitain default router list; 2) maintain prefix options;\r
+  3) maintain route caches.\r
+\r
+  @param[in]  IpSb              The IP6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6NdTimerTicking (\r
+  IN IP6_SERVICE            *IpSb\r
+  );\r
+\r
+/**\r
+  Callback function when address resolution is finished. It will cancel\r
+  all the queued frames if the address resolution failed, or transmit them\r
+  if the request succeeded.\r
+\r
+  @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.\r
+\r
+**/\r
+VOID\r
+Ip6OnArpResolved (\r
+  IN VOID                   *Context\r
+  );\r
+\r
+/**\r
+  Update the ReachableTime in IP6 service binding instance data, in milliseconds.\r
+\r
+  @param[in, out] IpSb     Points to the IP6_SERVICE.\r
+\r
+**/\r
+VOID\r
+Ip6UpdateReachableTime (\r
+  IN OUT IP6_SERVICE  *IpSb\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6NvData.h b/NetworkPkg/Ip6Dxe/Ip6NvData.h
new file mode 100644 (file)
index 0000000..715473f
--- /dev/null
@@ -0,0 +1,70 @@
+/** @file\r
+  NVData structure used by the IP6 configuration component.\r
+\r
+  Copyright (c) 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _IP6_NV_DATA_H_\r
+#define _IP6_NV_DATA_H_\r
+\r
+#define IP6_CONFIG_NVDATA_GUID \\r
+  { \\r
+    0x2eea107, 0x98db, 0x400e, { 0x98, 0x30, 0x46, 0xa, 0x15, 0x42, 0xd7, 0x99 } \\r
+  }\r
+\r
+#define FORMID_MAIN_FORM          1\r
+#define FORMID_MANUAL_CONFIG_FORM 2\r
+\r
+#define IP6_POLICY_AUTO           0\r
+#define IP6_POLICY_MANUAL         1\r
+#define DAD_MAX_TRANSMIT_COUNT    10\r
+\r
+#define KEY_INTERFACE_ID          0x101\r
+#define KEY_MANUAL_ADDRESS        0x102\r
+#define KEY_GATEWAY_ADDRESS       0x103\r
+#define KEY_DNS_ADDRESS           0x104\r
+#define KEY_SAVE_CHANGES          0x105\r
+#define KEY_SAVE_CONFIG_CHANGES   0x106\r
+#define KEY_IGNORE_CONFIG_CHANGES 0x107\r
+\r
+#define HOST_ADDRESS_LABEL        0x9000\r
+#define ROUTE_TABLE_LABEL         0xa000\r
+#define GATEWAY_ADDRESS_LABEL     0xb000\r
+#define DNS_ADDRESS_LABEL         0xc000\r
+#define LABEL_END                 0xffff\r
+\r
+#define INTERFACE_ID_STR_MIN_SIZE 1\r
+#define INTERFACE_ID_STR_MAX_SIZE 23\r
+#define INTERFACE_ID_STR_STORAGE  24\r
+#define IP6_STR_MAX_SIZE          40\r
+#define ADDRESS_STR_MIN_SIZE      2\r
+#define ADDRESS_STR_MAX_SIZE      255\r
+\r
+///\r
+/// IP6_CONFIG_IFR_NVDATA contains the IP6 configure\r
+/// parameters for that NIC.\r
+///\r
+#pragma pack(1)\r
+typedef struct {\r
+  UINT8           IfType;                                 ///< interface type\r
+  UINT8           Padding[3];\r
+  UINT32          Policy;                                 ///< manual or automatic\r
+  UINT32          DadTransmitCount;                       ///< dad transmits count\r
+  CHAR16          InterfaceId[INTERFACE_ID_STR_STORAGE];  ///< alternative interface id\r
+  CHAR16          ManualAddress[ADDRESS_STR_MAX_SIZE];    ///< IP addresses\r
+  CHAR16          GatewayAddress[ADDRESS_STR_MAX_SIZE];   ///< Gateway address\r
+  CHAR16          DnsAddress[ADDRESS_STR_MAX_SIZE];       ///< DNS server address\r
+} IP6_CONFIG_IFR_NVDATA;\r
+#pragma pack()\r
+\r
+#endif\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Option.c b/NetworkPkg/Ip6Dxe/Ip6Option.c
new file mode 100644 (file)
index 0000000..9a91fd7
--- /dev/null
@@ -0,0 +1,758 @@
+/** @file\r
+  IP6 option support functions and routines.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+  Validate the IP6 option format for both the packets we received\r
+  and that we will transmit. It will compute the ICMPv6 error message fields\r
+  if the option is malformated.\r
+\r
+  @param[in]  IpSb              The IP6 service data.\r
+  @param[in]  Packet            The to be validated packet.\r
+  @param[in]  Option            The first byte of the option.\r
+  @param[in]  OptionLen         The length of the whole option.\r
+  @param[in]  Pointer           Identifies the octet offset within\r
+                                the invoking packet where the error was detected.\r
+\r
+\r
+  @retval TRUE     The option is properly formatted.\r
+  @retval FALSE    The option is malformated.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsOptionValid (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN NET_BUF                *Packet,\r
+  IN UINT8                  *Option,\r
+  IN UINT8                  OptionLen,\r
+  IN UINT32                 Pointer\r
+  )\r
+{\r
+  UINT8                      Offset;\r
+  UINT8                      OptionType;\r
+\r
+  Offset = 0;\r
+\r
+  while (Offset < OptionLen) {\r
+    OptionType = *(Option + Offset);\r
+\r
+    switch (OptionType) {\r
+    case Ip6OptionPad1:\r
+      //\r
+      // It is a Pad1 option\r
+      //\r
+      Offset++;\r
+      break;\r
+    case Ip6OptionPadN:\r
+      //\r
+      // It is a PadN option\r
+      //\r
+      Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2);\r
+      break;\r
+    case Ip6OptionRouterAlert:\r
+      //\r
+      // It is a Router Alert Option\r
+      //\r
+      Offset += 4;\r
+      break;\r
+    default:\r
+      //\r
+      // The highest-order two bits specify the action must be taken if\r
+      // the processing IPv6 node does not recognize the option type.\r
+      //\r
+      switch (OptionType & Ip6OptionMask) {\r
+      case Ip6OptionSkip:\r
+        Offset = (UINT8) (Offset + *(Option + Offset + 1));\r
+        break;\r
+      case Ip6OptionDiscard:\r
+        return FALSE;\r
+      case Ip6OptionParameterProblem:\r
+        Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);\r
+        Ip6SendIcmpError (\r
+          IpSb,\r
+          Packet,\r
+          NULL,\r
+          &Packet->Ip.Ip6->SourceAddress,\r
+          ICMP_V6_PARAMETER_PROBLEM,\r
+          2,\r
+          &Pointer\r
+          );\r
+        return FALSE;\r
+      case Ip6OptionMask:\r
+        if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+          Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);\r
+          Ip6SendIcmpError (\r
+            IpSb,\r
+            Packet,\r
+            NULL,\r
+            &Packet->Ip.Ip6->SourceAddress,\r
+            ICMP_V6_PARAMETER_PROBLEM,\r
+            2,\r
+            &Pointer\r
+            );\r
+        }\r
+\r
+        return FALSE;\r
+        break;\r
+      }\r
+\r
+      break;\r
+    }\r
+\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Validate the IP6 option format for both the packets we received\r
+  and that we will transmit. It supports the defined options in Neighbor\r
+  Discovery messages.\r
+\r
+  @param[in]  Option            The first byte of the option.\r
+  @param[in]  OptionLen         The length of the whole option.\r
+\r
+  @retval TRUE     The option is properly formatted.\r
+  @retval FALSE    The option is malformated.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsNDOptionValid (\r
+  IN UINT8                  *Option,\r
+  IN UINT16                 OptionLen\r
+  )\r
+{\r
+  UINT16                    Offset;\r
+  UINT8                     OptionType;\r
+  UINT16                    Length;\r
+\r
+  Offset = 0;\r
+\r
+  while (Offset < OptionLen) {\r
+    OptionType = *(Option + Offset);\r
+     Length    = (UINT16) (*(Option + Offset + 1) * 8);\r
+\r
+    switch (OptionType) {\r
+    case Ip6OptionPrefixInfo:\r
+      if (Length != 32) {\r
+        return FALSE;\r
+      }\r
+\r
+      break;\r
+\r
+    case Ip6OptionMtu:\r
+      if (Length != 8) {\r
+        return FALSE;\r
+      }\r
+\r
+      break;\r
+\r
+    default:\r
+      //\r
+      // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and\r
+      // Ip6OptionRedirected here. For unrecognized options, silently ignore\r
+      // and continue processsing the message.\r
+      //\r
+      if (Length == 0) {\r
+        return FALSE;\r
+      }\r
+\r
+      break;\r
+    }\r
+\r
+    Offset = (UINT16) (Offset + Length);\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Validate whether the NextHeader is a known valid protocol or one of the user configured\r
+  protocols from the upper layer.\r
+\r
+  @param[in]  IpSb          The IP6 service instance.\r
+  @param[in]  NextHeader    The next header field.\r
+\r
+  @retval TRUE              The NextHeader is a known valid protocol or user configured.\r
+  @retval FALSE             The NextHeader is not a known valid protocol.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsValidProtocol (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN UINT8                  NextHeader\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  IP6_PROTOCOL              *IpInstance;\r
+\r
+  if (NextHeader == EFI_IP_PROTO_TCP ||\r
+      NextHeader == EFI_IP_PROTO_UDP ||\r
+      NextHeader == IP6_ICMP ||\r
+      NextHeader == IP6_ESP\r
+      ) {\r
+    return TRUE;\r
+  }\r
+\r
+  if (IpSb == NULL) {\r
+    return FALSE;\r
+  }\r
+\r
+  if (IpSb->Signature != IP6_SERVICE_SIGNATURE) {\r
+    return FALSE;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+    IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);\r
+    if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+      if (IpInstance->ConfigData.DefaultProtocol == NextHeader) {\r
+        return TRUE;\r
+      }\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Validate the IP6 extension header format for both the packets we received\r
+  and that we will transmit. It will compute the ICMPv6 error message fields\r
+  if the option is mal-formated.\r
+\r
+  @param[in]  IpSb          The IP6 service instance. This is an optional parameter.\r
+  @param[in]  Packet        The data of the packet. Ignored if NULL.\r
+  @param[in]  NextHeader    The next header field in IPv6 basic header.\r
+  @param[in]  ExtHdrs       The first byte of the option.\r
+  @param[in]  ExtHdrsLen    The length of the whole option.\r
+  @param[in]  Rcvd          The option is from the packet we received if TRUE,\r
+                            otherwise, the option we want to transmit.\r
+  @param[out] FormerHeader  The offset of NextHeader which points to Fragment\r
+                            Header when we received, of the ExtHdrs.\r
+                            Ignored if we transmit.\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
+  @param[out] UnFragmentLen The length of unfragmented length of extension headers.\r
+                            This is an optional parameter that may be NULL.\r
+  @param[out] Fragmented    Indicate whether the packet is fragmented.\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
+Ip6IsExtsValid (\r
+  IN IP6_SERVICE            *IpSb           OPTIONAL,\r
+  IN NET_BUF                *Packet         OPTIONAL,\r
+  IN UINT8                  *NextHeader,\r
+  IN UINT8                  *ExtHdrs,\r
+  IN UINT32                 ExtHdrsLen,\r
+  IN BOOLEAN                Rcvd,\r
+  OUT UINT32                *FormerHeader   OPTIONAL,\r
+  OUT UINT8                 **LastHeader,\r
+  OUT UINT32                *RealExtsLen    OPTIONAL,\r
+  OUT UINT32                *UnFragmentLen  OPTIONAL,\r
+  OUT BOOLEAN               *Fragmented     OPTIONAL\r
+  )\r
+{\r
+  UINT32                     Pointer;\r
+  UINT32                     Offset;\r
+  UINT8                      *Option;\r
+  UINT8                      OptionLen;\r
+  BOOLEAN                    Flag;\r
+  UINT8                      CountD;\r
+  UINT8                      CountA;\r
+  IP6_FRAGMENT_HEADER        *FragmentHead;\r
+  UINT16                     FragmentOffset;\r
+  IP6_ROUTING_HEADER         *RoutingHead;\r
+\r
+  if (RealExtsLen != NULL) {\r
+    *RealExtsLen = 0;\r
+  }\r
+\r
+  if (UnFragmentLen != NULL) {\r
+    *UnFragmentLen = 0;\r
+  }\r
+\r
+  if (Fragmented != NULL) {\r
+    *Fragmented = FALSE;\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
+  Offset  = 0;\r
+  Flag    = FALSE;\r
+  CountD  = 0;\r
+  CountA  = 0;\r
+\r
+  while (Offset <= ExtHdrsLen) {\r
+\r
+    switch (*NextHeader) {\r
+    case IP6_HOP_BY_HOP:\r
+      if (Offset != 0) {\r
+        if (!Rcvd) {\r
+          return FALSE;\r
+        }\r
+        //\r
+        // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only.\r
+        // If not, generate a ICMP parameter problem message with code value of 1.\r
+        //\r
+        if (Pointer == 0) {\r
+          Pointer = sizeof (EFI_IP6_HEADER);\r
+        } else {\r
+          Pointer = Offset + sizeof (EFI_IP6_HEADER);\r
+        }\r
+\r
+        if ((IpSb != NULL) && (Packet != NULL) &&\r
+            !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+          Ip6SendIcmpError (\r
+            IpSb,\r
+            Packet,\r
+            NULL,\r
+            &Packet->Ip.Ip6->SourceAddress,\r
+            ICMP_V6_PARAMETER_PROBLEM,\r
+            1,\r
+            &Pointer\r
+            );\r
+        }\r
+        return FALSE;\r
+      }\r
+\r
+      Flag = TRUE;\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 + Offset;\r
+      Pointer    = Offset;\r
+\r
+      Offset++;\r
+      Option     = ExtHdrs + Offset;\r
+      OptionLen  = (UINT8) ((*Option + 1) * 8 - 2);\r
+      Option++;\r
+      Offset++;\r
+\r
+      if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) {\r
+        return FALSE;\r
+      }\r
+\r
+      Offset = Offset + OptionLen;\r
+\r
+      if (Flag) {\r
+        if (UnFragmentLen != NULL) {\r
+          *UnFragmentLen = Offset;\r
+        }\r
+\r
+        Flag = FALSE;\r
+      }\r
+\r
+      break;\r
+\r
+    case IP6_ROUTING:\r
+      NextHeader = ExtHdrs + Offset;\r
+      RoutingHead = (IP6_ROUTING_HEADER *) NextHeader;\r
+\r
+      //\r
+      // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095.\r
+      // Thus all routing types are processed as unrecognized.\r
+      //\r
+      if (RoutingHead->SegmentsLeft == 0) {\r
+        //\r
+        // Ignore the routing header and proceed to process the next header.\r
+        //\r
+        Offset = Offset + (RoutingHead->HeaderLen + 1) * 8;\r
+\r
+        if (UnFragmentLen != NULL) {\r
+          *UnFragmentLen = Offset;\r
+        }\r
+\r
+      } else {\r
+        //\r
+        // Discard the packet and send an ICMP Parameter Problem, Code 0, message\r
+        // to the packet's source address, pointing to the unrecognized routing\r
+        // type.\r
+        //\r
+        Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER);\r
+        if ((IpSb != NULL) && (Packet != NULL) &&\r
+            !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+          Ip6SendIcmpError (\r
+            IpSb,\r
+            Packet,\r
+            NULL,\r
+            &Packet->Ip.Ip6->SourceAddress,\r
+            ICMP_V6_PARAMETER_PROBLEM,\r
+            0,\r
+            &Pointer\r
+            );\r
+        }\r
+\r
+        return FALSE;\r
+      }\r
+\r
+      break;\r
+\r
+    case IP6_FRAGMENT:\r
+\r
+      //\r
+      // RFC2402, AH header should after fragment header.\r
+      //\r
+      if (CountA > 1) {\r
+        return FALSE;\r
+      }\r
+\r
+      //\r
+      // RFC2460, ICMP Parameter Problem message with code 0 should be sent\r
+      // if the length of a fragment is not a multiple of 8 octects and the M\r
+      // flag of that fragment is 1, pointing to the Payload length field of the\r
+      // fragment packet.\r
+      //\r
+      if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) {\r
+        //\r
+        // Check whether it is the last fragment.\r
+        //\r
+        FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset);\r
+        if (FragmentHead == NULL) {\r
+          return FALSE;\r
+        }\r
+\r
+        FragmentOffset = NTOHS (FragmentHead->FragmentOffset);\r
+\r
+        if (((FragmentOffset & 0x1) == 0x1) &&\r
+            !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+          Pointer = sizeof (UINT32);\r
+          Ip6SendIcmpError (\r
+            IpSb,\r
+            Packet,\r
+            NULL,\r
+            &Packet->Ip.Ip6->SourceAddress,\r
+            ICMP_V6_PARAMETER_PROBLEM,\r
+            0,\r
+            &Pointer\r
+            );\r
+          return FALSE;\r
+        }\r
+      }\r
+\r
+      if (Fragmented != NULL) {\r
+        *Fragmented = TRUE;\r
+      }\r
+\r
+      if (Rcvd && FormerHeader != NULL) {\r
+        *FormerHeader = (UINT32) (NextHeader - ExtHdrs);\r
+      }\r
+\r
+      NextHeader = ExtHdrs + Offset;\r
+      Offset     = Offset + 8;\r
+      break;\r
+\r
+    case IP6_AH:\r
+      if (++CountA > 1) {\r
+        return FALSE;\r
+      }\r
+\r
+      Option     = ExtHdrs + Offset;\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
+      Offset     = Offset + OptionLen;\r
+      break;\r
+\r
+    case IP6_NO_NEXT_HEADER:\r
+      *LastHeader = NextHeader;\r
+      return FALSE;\r
+      break;\r
+\r
+    default:\r
+      if (Ip6IsValidProtocol (IpSb, *NextHeader)) {\r
+\r
+        *LastHeader = NextHeader;\r
+\r
+        if (RealExtsLen != NULL) {\r
+          *RealExtsLen = Offset;\r
+        }\r
+\r
+        return TRUE;\r
+      }\r
+\r
+      //\r
+      // The Next Header value is unrecognized by the node, discard the packet and\r
+      // send an ICMP parameter problem message with code value of 1.\r
+      //\r
+      if (Offset == 0) {\r
+        //\r
+        // The Next Header directly follows IPv6 basic header.\r
+        //\r
+        Pointer = 6;\r
+      } else {\r
+        if (Pointer == 0) {\r
+          Pointer = sizeof (EFI_IP6_HEADER);\r
+        } else {\r
+          Pointer = Offset + sizeof (EFI_IP6_HEADER);\r
+        }\r
+      }\r
+\r
+      if ((IpSb != NULL) && (Packet != NULL) &&\r
+          !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+        Ip6SendIcmpError (\r
+          IpSb,\r
+          Packet,\r
+          NULL,\r
+          &Packet->Ip.Ip6->SourceAddress,\r
+          ICMP_V6_PARAMETER_PROBLEM,\r
+          1,\r
+          &Pointer\r
+          );\r
+      }\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  *LastHeader = NextHeader;\r
+\r
+  if (RealExtsLen != NULL) {\r
+    *RealExtsLen = Offset;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Generate an IPv6 router alert option in network order and output it through Buffer.\r
+\r
+  @param[out]     Buffer         Points to a buffer to record the generated option.\r
+  @param[in, out] BufferLen      The length of Buffer, in bytes.\r
+  @param[in]      NextHeader     The 8-bit selector indicates the type of header\r
+                                 immediately following the Hop-by-Hop Options header.\r
+\r
+  @retval EFI_BUFFER_TOO_SMALL   The Buffer is too small to contain the generated\r
+                                 option. BufferLen is updated for the required size.\r
+\r
+  @retval EFI_SUCCESS            The option is generated and filled in to Buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FillHopByHop (\r
+  OUT UINT8                  *Buffer,\r
+  IN OUT UINTN               *BufferLen,\r
+  IN UINT8                   NextHeader\r
+  )\r
+{\r
+  UINT8                      BufferArray[8];\r
+\r
+  if (*BufferLen < 8) {\r
+    *BufferLen = 8;\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  //\r
+  // Form the Hop-By-Hop option in network order.\r
+  // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN\r
+  // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets.\r
+  //\r
+  ZeroMem (BufferArray, sizeof (BufferArray));\r
+  BufferArray[0] = NextHeader;\r
+  BufferArray[2] = 0x5;\r
+  BufferArray[3] = 0x2;\r
+  BufferArray[6] = 1;\r
+\r
+  CopyMem (Buffer, BufferArray, sizeof (BufferArray));\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.\r
+\r
+  @param[in]  IpSb             The IP6 service instance to transmit the packet.\r
+  @param[in]  NextHeader       The extension header type of first extension header.\r
+  @param[in]  LastHeader       The extension header type of last extension header.\r
+  @param[in]  ExtHdrs          The length of the original extension header.\r
+  @param[in]  ExtHdrsLen       The length of the extension headers.\r
+  @param[in]  FragmentOffset   The fragment offset of the data following the header.\r
+  @param[out] UpdatedExtHdrs   The updated ExtHdrs with Fragment header inserted.\r
+                               It's caller's responsiblity to free this buffer.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of\r
+                               resource.\r
+  @retval EFI_UNSUPPORTED      The extension header specified in ExtHdrs is not\r
+                               supported currently.\r
+  @retval EFI_SUCCESS          The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FillFragmentHeader (\r
+  IN  IP6_SERVICE           *IpSb,\r
+  IN  UINT8                 NextHeader,\r
+  IN  UINT8                 LastHeader,\r
+  IN  UINT8                 *ExtHdrs,\r
+  IN  UINT32                ExtHdrsLen,\r
+  IN  UINT16                FragmentOffset,\r
+  OUT UINT8                 **UpdatedExtHdrs\r
+  )\r
+{\r
+  UINT32                    Length;\r
+  UINT8                     *Buffer;\r
+  UINT32                    FormerHeader;\r
+  UINT32                    Offset;\r
+  UINT32                    Part1Len;\r
+  UINT32                    HeaderLen;\r
+  UINT8                     Current;\r
+  IP6_FRAGMENT_HEADER       FragmentHead;\r
+\r
+  if (UpdatedExtHdrs == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);\r
+  Buffer = AllocatePool (Length);\r
+  if (Buffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Offset         = 0;\r
+  Part1Len       = 0;\r
+  FormerHeader   = 0;\r
+  Current        = NextHeader;\r
+\r
+  while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) {\r
+    switch (NextHeader) {\r
+    case IP6_ROUTING:\r
+    case IP6_HOP_BY_HOP:\r
+    case IP6_DESTINATION:\r
+      Current      = NextHeader;\r
+      NextHeader   = *(ExtHdrs + Offset);\r
+\r
+      if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) {\r
+        //\r
+        // Destination Options header should occur at most twice, once before\r
+        // a Routing header and once before the upper-layer header. Here we\r
+        // find the one before the upper-layer header. Insert the Fragment\r
+        // Header before it.\r
+        //\r
+        CopyMem (Buffer, ExtHdrs, Part1Len);\r
+        *(Buffer + FormerHeader) = IP6_FRAGMENT;\r
+        //\r
+        // Exit the loop.\r
+        //\r
+        Offset = ExtHdrsLen + 1;\r
+        break;\r
+      }\r
+\r
+\r
+      FormerHeader = Offset;\r
+      HeaderLen    = (*(ExtHdrs + Offset + 1) + 1) * 8;\r
+      Part1Len     = Part1Len + HeaderLen;\r
+      Offset       = Offset + HeaderLen;\r
+      break;\r
+\r
+    case IP6_FRAGMENT:\r
+      Current    = NextHeader;\r
+\r
+      if (Part1Len != 0) {\r
+        CopyMem (Buffer, ExtHdrs, Part1Len);\r
+      }\r
+\r
+      *(Buffer + FormerHeader) = IP6_FRAGMENT;\r
+\r
+      //\r
+      // Exit the loop.\r
+      //\r
+      Offset = ExtHdrsLen + 1;\r
+      break;\r
+\r
+    case IP6_AH:\r
+      Current    = NextHeader;\r
+      NextHeader = *(ExtHdrs + Offset);\r
+      //\r
+      // RFC2402, Payload length is specified in 32-bit words, minus "2".\r
+      //\r
+      HeaderLen  = (*(ExtHdrs + Offset + 1) + 2) * 4;\r
+      Part1Len   = Part1Len + HeaderLen;\r
+      Offset     = Offset + HeaderLen;\r
+      break;\r
+\r
+    default:\r
+      if (Ip6IsValidProtocol (IpSb, NextHeader)) {\r
+        Current = NextHeader;\r
+        CopyMem (Buffer, ExtHdrs, Part1Len);\r
+        *(Buffer + FormerHeader) = IP6_FRAGMENT;\r
+        //\r
+        // Exit the loop.\r
+        //\r
+        Offset = ExtHdrsLen + 1;\r
+        break;\r
+      }\r
+\r
+      FreePool (Buffer);\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Append the Fragment header. If the fragment offset indicates the fragment\r
+  // is the first fragment.\r
+  //\r
+  if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {\r
+    FragmentHead.NextHeader = Current;\r
+  } else {\r
+    FragmentHead.NextHeader = LastHeader;\r
+  }\r
+\r
+  FragmentHead.Reserved       = 0;\r
+  FragmentHead.FragmentOffset = HTONS (FragmentOffset);\r
+  FragmentHead.Identification = mIp6Id;\r
+\r
+  CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER));\r
+\r
+  if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) {\r
+    //\r
+    // Append the part2 (fragmentable part) of Extension headers\r
+    //\r
+    CopyMem (\r
+      Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER),\r
+      ExtHdrs + Part1Len,\r
+      ExtHdrsLen - Part1Len\r
+      );\r
+  }\r
+\r
+  *UpdatedExtHdrs = Buffer;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Option.h b/NetworkPkg/Ip6Dxe/Ip6Option.h
new file mode 100644 (file)
index 0000000..b62a042
--- /dev/null
@@ -0,0 +1,191 @@
+/** @file\r
+  Definition of IP6 option process routines.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_OPTION_H__\r
+#define __EFI_IP6_OPTION_H__\r
+\r
+#define IP6_FRAGMENT_OFFSET_MASK (~0x3)\r
+\r
+typedef struct _IP6_FRAGMENT_HEADER {\r
+  UINT8                     NextHeader;\r
+  UINT8                     Reserved;\r
+  UINT16                    FragmentOffset;\r
+  UINT32                    Identification;\r
+} IP6_FRAGMENT_HEADER;\r
+\r
+typedef struct _IP6_ROUTING_HEADER {\r
+  UINT8                     NextHeader;\r
+  UINT8                     HeaderLen;\r
+  UINT8                     RoutingType;\r
+  UINT8                     SegmentsLeft;\r
+} IP6_ROUTING_HEADER;\r
+\r
+typedef enum {\r
+  Ip6OptionPad1             = 0,\r
+  Ip6OptionPadN             = 1,\r
+  Ip6OptionRouterAlert      = 5,\r
+  Ip6OptionSkip             = 0,\r
+  Ip6OptionDiscard          = 0x40,\r
+  Ip6OptionParameterProblem = 0x80,\r
+  Ip6OptionMask             = 0xc0,\r
+\r
+  Ip6OptionEtherSource      = 1,\r
+  Ip6OptionEtherTarget      = 2,\r
+  Ip6OptionPrefixInfo       = 3,\r
+  Ip6OptionRedirected       = 4,\r
+  Ip6OptionMtu              = 5\r
+} IP6_OPTION_TYPE;\r
+\r
+/**\r
+  Validate the IP6 extension header format for both the packets we received\r
+  and that we will transmit. It will compute the ICMPv6 error message fields\r
+  if the option is mal-formated.\r
+\r
+  @param[in]  IpSb          The IP6 service instance. This is an optional parameter.\r
+  @param[in]  Packet        The data of the packet. Ignored if NULL.\r
+  @param[in]  NextHeader    The next header field in IPv6 basic header.\r
+  @param[in]  ExtHdrs       The first byte of the option.\r
+  @param[in]  ExtHdrsLen    The length of the whole option.\r
+  @param[in]  Rcvd          The option is from the packet we received if TRUE,\r
+                            otherwise, the option we want to transmit.\r
+  @param[out] FormerHeader  The offset of NextHeader which points to Fragment\r
+                            Header when we received, of the ExtHdrs.\r
+                            Ignored if we transmit.\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
+  @param[out] UnFragmentLen The length of unfragmented length of extension headers.\r
+                            This is an optional parameter that may be NULL.\r
+  @param[out] Fragmented    Indicate whether the packet is fragmented.\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
+Ip6IsExtsValid (\r
+  IN IP6_SERVICE            *IpSb           OPTIONAL,\r
+  IN NET_BUF                *Packet         OPTIONAL,\r
+  IN UINT8                  *NextHeader,\r
+  IN UINT8                  *ExtHdrs,\r
+  IN UINT32                 ExtHdrsLen,\r
+  IN BOOLEAN                Rcvd,\r
+  OUT UINT32                *FormerHeader   OPTIONAL,\r
+  OUT UINT8                 **LastHeader,\r
+  OUT UINT32                *RealExtsLen    OPTIONAL,\r
+  OUT UINT32                *UnFragmentLen  OPTIONAL,\r
+  OUT BOOLEAN               *Fragmented     OPTIONAL\r
+  );\r
+\r
+/**\r
+  Generate an IPv6 router alert option in network order and output it through Buffer.\r
+\r
+  @param[out]     Buffer         Points to a buffer to record the generated option.\r
+  @param[in, out] BufferLen      The length of Buffer, in bytes.\r
+  @param[in]      NextHeader     The 8-bit selector indicates the type of header\r
+                                 immediately following the Hop-by-Hop Options header.\r
+\r
+  @retval EFI_BUFFER_TOO_SMALL   The Buffer is too small to contain the generated\r
+                                 option. BufferLen is updated for the required size.\r
+\r
+  @retval EFI_SUCCESS            The option is generated and filled in to Buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FillHopByHop (\r
+  OUT UINT8                  *Buffer,\r
+  IN OUT UINTN               *BufferLen,\r
+  IN UINT8                   NextHeader\r
+  );\r
+\r
+/**\r
+  Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.\r
+\r
+  @param[in]  IpSb             The IP6 service instance to transmit the packet.\r
+  @param[in]  NextHeader       The extension header type of first extension header.\r
+  @param[in]  LastHeader       The extension header type of last extension header.\r
+  @param[in]  ExtHdrs          The length of the original extension header.\r
+  @param[in]  ExtHdrsLen       The length of the extension headers.\r
+  @param[in]  FragmentOffset   The fragment offset of the data following the header.\r
+  @param[out] UpdatedExtHdrs   The updated ExtHdrs with Fragment header inserted.\r
+                               It's caller's responsiblity to free this buffer.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of\r
+                               resource.\r
+  @retval EFI_UNSUPPORTED      The extension header specified in ExtHdrs is not\r
+                               supported currently.\r
+  @retval EFI_SUCCESS          The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FillFragmentHeader (\r
+  IN  IP6_SERVICE           *IpSb,\r
+  IN  UINT8                 NextHeader,\r
+  IN  UINT8                 LastHeader,\r
+  IN  UINT8                 *ExtHdrs,\r
+  IN  UINT32                ExtHdrsLen,\r
+  IN  UINT16                FragmentOffset,\r
+  OUT UINT8                 **UpdatedExtHdrs\r
+  );\r
+\r
+/**\r
+  Copy the extension headers from the original to buffer. A Fragment header is\r
+  appended to the end.\r
+\r
+  @param[in]       NextHeader       The 8-bit selector indicates the type of\r
+                                    the fragment header's next header.\r
+  @param[in]       ExtHdrs          The length of the original extension header.\r
+  @param[in]       LastHeader       The pointer of next header of last extension header.\r
+  @param[in]       FragmentOffset   The fragment offset of the data following the header.\r
+  @param[in]       UnFragmentHdrLen The length of unfragmented length of extension headers.\r
+  @param[in, out]  Buf              The buffer to copy options to.\r
+  @param[in, out]  BufLen           The length of the buffer.\r
+\r
+  @retval EFI_SUCCESS           The options are copied over.\r
+  @retval EFI_BUFFER_TOO_SMALL  The buffer caller provided is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CopyExts (\r
+  IN UINT8                  NextHeader,\r
+  IN UINT8                  *ExtHdrs,\r
+  IN UINT8                  *LastHeader,\r
+  IN UINT16                 FragmentOffset,\r
+  IN UINT32                 UnFragmentHdrLen,\r
+  IN OUT UINT8              *Buf,\r
+  IN OUT UINT32             *BufLen\r
+  );\r
+\r
+/**\r
+  Validate the IP6 option format for both the packets we received\r
+  and that we will transmit. It supports the defined options in Neighbor\r
+  Discovery messages.\r
+\r
+  @param[in]  Option            The first byte of the option.\r
+  @param[in]  OptionLen         The length of the whole option.\r
+\r
+  @retval TRUE     The option is properly formatted.\r
+  @retval FALSE    The option is malformated.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsNDOptionValid (\r
+  IN UINT8                  *Option,\r
+  IN UINT16                 OptionLen\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Output.c b/NetworkPkg/Ip6Dxe/Ip6Output.c
new file mode 100644 (file)
index 0000000..baa4904
--- /dev/null
@@ -0,0 +1,1077 @@
+/** @file\r
+  The internal functions and routines to transmit the IP6 packet.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+UINT32 mIp6Id;\r
+\r
+/**\r
+  Output all the available source addresses to a list entry head SourceList. The\r
+  number of source addresses are also returned.\r
+\r
+  @param[in]       IpSb             Points to an IP6 service binding instance.\r
+  @param[out]      SourceList       The list entry head of all source addresses.\r
+                                    It is the caller's responsiblity to free the\r
+                                    resources.\r
+  @param[out]      SourceCount      The number of source addresses.\r
+\r
+  @retval EFI_SUCCESS           The source addresses were copied to a list entry head\r
+                                SourceList.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to complete the operation.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CandidateSource (\r
+  IN IP6_SERVICE            *IpSb,\r
+  OUT LIST_ENTRY            *SourceList,\r
+  OUT UINT32                *SourceCount\r
+  )\r
+{\r
+  IP6_INTERFACE             *IpIf;\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Entry2;\r
+  IP6_ADDRESS_INFO          *AddrInfo;\r
+  IP6_ADDRESS_INFO          *Copy;\r
+\r
+  *SourceCount = 0;\r
+\r
+  if (IpSb->LinkLocalOk) {\r
+    Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO));\r
+    if (Copy == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    Copy->Signature         = IP6_ADDR_INFO_SIGNATURE;\r
+    IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr);\r
+    Copy->IsAnycast         = FALSE;\r
+    Copy->PrefixLength      = IP6_LINK_LOCAL_PREFIX_LENGTH;\r
+    Copy->ValidLifetime     = (UINT32) IP6_INFINIT_LIFETIME;\r
+    Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME;\r
+\r
+    InsertTailList (SourceList, &Copy->Link);\r
+    (*SourceCount)++;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+    NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {\r
+      AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+      if (AddrInfo->IsAnycast) {\r
+        //\r
+        // Never use an anycast address.\r
+        //\r
+        continue;\r
+      }\r
+\r
+      Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo);\r
+      if (Copy == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      InsertTailList (SourceList, &Copy->Link);\r
+      (*SourceCount)++;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Caculate how many bits are the same between two IPv6 addresses.\r
+\r
+  @param[in]       AddressA         Points to an IPv6 address.\r
+  @param[in]       AddressB         Points to another IPv6 address.\r
+\r
+  @return The common bits of the AddressA and AddressB.\r
+\r
+**/\r
+UINT8\r
+Ip6CommonPrefixLen (\r
+  IN EFI_IPv6_ADDRESS       *AddressA,\r
+  IN EFI_IPv6_ADDRESS       *AddressB\r
+  )\r
+{\r
+  UINT8                     Count;\r
+  UINT8                     Index;\r
+  UINT8                     ByteA;\r
+  UINT8                     ByteB;\r
+  UINT8                     NumBits;\r
+\r
+  Count = 0;\r
+  Index = 0;\r
+\r
+  while (Index < 16) {\r
+    ByteA = AddressA->Addr[Index];\r
+    ByteB = AddressB->Addr[Index];\r
+\r
+    if (ByteA == ByteB) {\r
+      Count += 8;\r
+      Index++;\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Check how many bits are common between the two bytes.\r
+    //\r
+    NumBits = 8;\r
+    ByteA   = (UINT8) (ByteA ^ ByteB);\r
+\r
+    while (ByteA != 0) {\r
+      NumBits--;\r
+      ByteA = (UINT8) (ByteA >> 1);\r
+    }\r
+\r
+    return (UINT8) (Count + NumBits);\r
+  }\r
+\r
+  return Count;\r
+}\r
+\r
+/**\r
+  Output all the available source addresses to a list entry head SourceList. The\r
+  number of source addresses are also returned.\r
+\r
+  @param[in]       IpSb             Points to a IP6 service binding instance.\r
+  @param[in]       Destination      The IPv6 destination address.\r
+  @param[out]      Source           The selected IPv6 source address according to\r
+                                    the Destination.\r
+\r
+  @retval EFI_SUCCESS           The source addresses were copied to a list entry\r
+                                head SourceList.\r
+  @retval EFI_NO_MAPPING        The IPv6 stack is not auto configured.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SelectSourceAddress (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Destination,\r
+  OUT EFI_IPv6_ADDRESS      *Source\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  LIST_ENTRY                SourceList;\r
+  UINT32                    SourceCount;\r
+  UINT8                     ScopeD;\r
+  LIST_ENTRY                *Entry;\r
+  IP6_ADDRESS_INFO          *AddrInfo;\r
+  IP6_PREFIX_LIST_ENTRY     *Prefix;\r
+  UINT8                     LastCommonLength;\r
+  UINT8                     CurrentCommonLength;\r
+  EFI_IPv6_ADDRESS          *TmpAddress;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  Status = EFI_SUCCESS;\r
+  InitializeListHead (&SourceList);\r
+\r
+  if (!IpSb->LinkLocalOk) {\r
+    return EFI_NO_MAPPING;\r
+  }\r
+\r
+  //\r
+  // Rule 1: Prefer same address.\r
+  //\r
+  if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) {\r
+    IP6_COPY_ADDRESS (Source, Destination);\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Rule 2: Prefer appropriate scope.\r
+  //\r
+  if (IP6_IS_MULTICAST (Destination)) {\r
+    ScopeD = (UINT8) (Destination->Addr[1] >> 4);\r
+  } else if (NetIp6IsLinkLocalAddr (Destination)) {\r
+    ScopeD = 0x2;\r
+  } else {\r
+    ScopeD = 0xE;\r
+  }\r
+\r
+  if (ScopeD <= 0x2) {\r
+    //\r
+    // Return the link-local address if it exists\r
+    // One IP6_SERVICE only has one link-local address.\r
+    //\r
+    IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // All candidate source addresses are global unicast address.\r
+  //\r
+  Ip6CandidateSource (IpSb, &SourceList, &SourceCount);\r
+\r
+  if (SourceCount == 0) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto Exit;\r
+  }\r
+\r
+  IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);\r
+\r
+  if (SourceCount == 1) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Rule 3: Avoid deprecated addresses.\r
+  // TODO: check the "deprecated" state of the stateful configured address\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {\r
+    Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+    if (Prefix->PreferredLifetime == 0) {\r
+      Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength);\r
+\r
+      if (SourceCount == 1) {\r
+        goto Exit;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // TODO: Rule 4: Prefer home addresses.\r
+  // TODO: Rule 5: Prefer outgoing interface.\r
+  // TODO: Rule 6: Prefer matching label.\r
+  // TODO: Rule 7: Prefer public addresses.\r
+  //\r
+\r
+  //\r
+  // Rule 8: Use longest matching prefix.\r
+  //\r
+  LastCommonLength = Ip6CommonPrefixLen (Source, Destination);\r
+  TmpAddress       = NULL;\r
+\r
+  for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) {\r
+    AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+    CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination);\r
+    if (CurrentCommonLength > LastCommonLength) {\r
+      LastCommonLength = CurrentCommonLength;\r
+      TmpAddress       = &AddrInfo->Address;\r
+    }\r
+  }\r
+\r
+  if (TmpAddress != NULL) {\r
+    IP6_COPY_ADDRESS (Source, TmpAddress);\r
+  }\r
+\r
+Exit:\r
+\r
+  Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Select an interface to send the packet generated in the IP6 driver\r
+  itself: that is, not by the requests of the IP6 child's consumer. Such\r
+  packets include the ICMPv6 echo replies and other ICMPv6 error packets.\r
+\r
+  @param[in]  IpSb                 The IP4 service that wants to send the packets.\r
+  @param[in]  Destination          The destination of the packet.\r
+  @param[in, out]  Source          The source of the packet.\r
+\r
+  @return NULL if no proper interface is found, otherwise, the interface that\r
+          can be used to send the system packet from.\r
+\r
+**/\r
+IP6_INTERFACE *\r
+Ip6SelectInterface (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Destination,\r
+  IN OUT EFI_IPv6_ADDRESS   *Source\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_IPv6_ADDRESS          SelectedSource;\r
+  IP6_INTERFACE             *IpIf;\r
+  BOOLEAN                   Exist;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+  ASSERT (Destination != NULL && Source != NULL);\r
+\r
+  if (NetIp6IsUnspecifiedAddr (Destination)) {\r
+    return NULL;\r
+  }\r
+\r
+  if (!NetIp6IsUnspecifiedAddr (Source)) {\r
+    Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL);\r
+    ASSERT (Exist);\r
+\r
+    return IpIf;\r
+  }\r
+\r
+  //\r
+  // If source is unspecified, select a source according to the destination.\r
+  //\r
+  Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource);\r
+  if (EFI_ERROR (Status)) {\r
+    return IpSb->DefaultInterface;\r
+  }\r
+\r
+  Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL);\r
+  IP6_COPY_ADDRESS (Source, &SelectedSource);\r
+\r
+  return IpIf;\r
+}\r
+\r
+/**\r
+  The default callback function for the system generated packet.\r
+  It will free the packet.\r
+\r
+  @param[in]  Packet        The packet that transmitted.\r
+  @param[in]  IoStatus      The result of the transmission, succeeded or failed.\r
+  @param[in]  LinkFlag      Not used when transmitted. Check IP6_FRAME_CALLBACK\r
+                            for reference.\r
+  @param[in]  Context       The context provided by us.\r
+\r
+**/\r
+VOID\r
+Ip6SysPacketSent (\r
+  NET_BUF                   *Packet,\r
+  EFI_STATUS                IoStatus,\r
+  UINT32                    LinkFlag,\r
+  VOID                      *Context\r
+  )\r
+{\r
+  NetbufFree (Packet);\r
+  Packet = NULL;\r
+}\r
+\r
+/**\r
+  Prefix an IP6 basic head and unfragmentable extension headers and a fragment header\r
+  to the Packet. Used for IP6 fragmentation.\r
+\r
+  @param[in]  IpSb             The IP6 service instance to transmit the packet.\r
+  @param[in]  Packet           The packet to prefix the IP6 header to.\r
+  @param[in]  Head             The caller supplied header.\r
+  @param[in]  FragmentOffset   The fragment offset of the data following the header.\r
+  @param[in]  ExtHdrs          The length of the original extension header.\r
+  @param[in]  ExtHdrsLen       The length of the extension headers.\r
+  @param[in]  LastHeader       The pointer of next header of last extension header.\r
+  @param[in]  HeadLen          The length of the unfragmented part of the IP6 header.\r
+\r
+  @retval EFI_BAD_BUFFER_SIZE  There is no enought room in the head space of\r
+                               Packet.\r
+  @retval EFI_SUCCESS          The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6PrependHead (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN UINT16                 FragmentOffset,\r
+  IN UINT8                  *ExtHdrs,\r
+  IN UINT32                 ExtHdrsLen,\r
+  IN UINT8                  LastHeader,\r
+  IN UINT32                 HeadLen\r
+  )\r
+{\r
+  UINT32                    Len;\r
+  UINT32                    UnFragExtHdrsLen;\r
+  EFI_IP6_HEADER            *PacketHead;\r
+  UINT8                     *UpdatedExtHdrs;\r
+  EFI_STATUS                Status;\r
+  UINT8                     NextHeader;\r
+\r
+  //\r
+  // HeadLen is the length of the fixed part of the sequences of fragments, i.e.\r
+  // the unfragment part.\r
+  //\r
+  PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);\r
+  if (PacketHead == NULL) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  //\r
+  // Set the head up, convert the host byte order to network byte order\r
+  //\r
+  CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));\r
+  PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER)));\r
+  Packet->Ip.Ip6            = PacketHead;\r
+\r
+  Len              = HeadLen - sizeof (EFI_IP6_HEADER);\r
+  UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER);\r
+\r
+  if (UnFragExtHdrsLen == 0) {\r
+    PacketHead->NextHeader = IP6_FRAGMENT;\r
+  }\r
+\r
+  //\r
+  // Append the extension headers: firstly copy the unfragmentable headers, then append\r
+  // fragmentation header.\r
+  //\r
+  if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {\r
+    NextHeader = Head->NextHeader;\r
+  } else {\r
+    NextHeader = PacketHead->NextHeader;\r
+  }\r
+\r
+  Status = Ip6FillFragmentHeader (\r
+             IpSb,\r
+             NextHeader,\r
+             LastHeader,\r
+             ExtHdrs,\r
+             ExtHdrsLen,\r
+             FragmentOffset,\r
+             &UpdatedExtHdrs\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  CopyMem (\r
+    (UINT8 *) (PacketHead + 1),\r
+    UpdatedExtHdrs,\r
+    UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER)\r
+    );\r
+\r
+  FreePool (UpdatedExtHdrs);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Transmit an IP6 packet. The packet comes either from the IP6\r
+  child's consumer (IpInstance != NULL) or the IP6 driver itself\r
+  (IpInstance == NULL). It will route the packet, fragment it,\r
+  then transmit all the fragments through an interface.\r
+\r
+  @param[in]  IpSb             The IP6 service instance to transmit the packet.\r
+  @param[in]  Interface        The IP6 interface to transmit the packet. Ignored\r
+                               if NULL.\r
+  @param[in]  IpInstance       The IP6 child that issues the transmission.  It is\r
+                               NULL if the packet is from the system.\r
+  @param[in]  Packet           The user data to send, excluding the IP header.\r
+  @param[in]  Head             The caller supplied header. The caller should set\r
+                               the  following header fields: NextHeader, HopLimit,\r
+                               Src, Dest, FlowLabel, PayloadLength. This function\r
+                               will fill in the Ver, TrafficClass.\r
+  @param[in]  ExtHdrs          The extension headers to append to the IPv6 basic\r
+                               header.\r
+  @param[in]  ExtHdrsLen       The length of the extension headers.\r
+  @param[in]  Callback         The callback function to issue when transmission\r
+                               completed.\r
+  @param[in]  Context          The opaque context for the callback.\r
+\r
+  @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.\r
+  @retval EFI_NO_MAPPING        There is no interface to the destination.\r
+  @retval EFI_NOT_FOUND         There is no route to the destination.\r
+  @retval EFI_SUCCESS           The packet successfully transmitted.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to finish the operation due to lack of\r
+                                resources.\r
+  @retval Others                Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Output (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_INTERFACE          *Interface   OPTIONAL,\r
+  IN IP6_PROTOCOL           *IpInstance  OPTIONAL,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN UINT8                  *ExtHdrs,\r
+  IN UINT32                 ExtHdrsLen,\r
+  IN IP6_FRAME_CALLBACK     Callback,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  IP6_INTERFACE             *IpIf;\r
+  EFI_IPv6_ADDRESS          NextHop;\r
+  IP6_NEIGHBOR_ENTRY        *NeighborCache;\r
+  IP6_ROUTE_CACHE_ENTRY     *RouteCache;\r
+  EFI_STATUS                Status;\r
+  UINT32                    Mtu;\r
+  UINT32                    HeadLen;\r
+  UINT16                    FragmentOffset;\r
+  UINT8                     *LastHeader;\r
+  UINT32                    UnFragmentLen;\r
+  UINT32                    UnFragmentHdrsLen;\r
+  UINT32                    FragmentHdrsLen;\r
+  UINT16                    *Checksum;\r
+  UINT16                    PacketChecksum;\r
+  UINT16                    PseudoChecksum;\r
+  UINT32                    Index;\r
+  UINT32                    PacketLen;\r
+  UINT32                    RealExtLen;\r
+  UINT32                    Offset;\r
+  NET_BUF                   *TmpPacket;\r
+  NET_BUF                   *Fragment;\r
+  UINT32                    Num;\r
+  UINT8                     *Buf;\r
+  EFI_IP6_HEADER            *PacketHead;\r
+  IP6_ICMP_HEAD             *IcmpHead;\r
+  IP6_TXTOKEN_WRAP          *Wrap;\r
+  IP6_ROUTE_ENTRY           *RouteEntry;\r
+  UINT8                     *UpdatedExtHdrs;\r
+  UINT8                     NextHeader;\r
+  UINT8                     LastHeaderBackup;\r
+  BOOLEAN                   FragmentHeadInserted;\r
+  UINT8                     *ExtHdrsBackup;\r
+  UINT8                     NextHeaderBackup;\r
+  EFI_IPv6_ADDRESS          Source;\r
+  EFI_IPv6_ADDRESS          Destination;\r
+\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  //\r
+  // RFC2460: Each extension header is an integer multiple of 8 octets long,\r
+  // in order to retain 8-octet alignment for subsequent headers.\r
+  //\r
+  if ((ExtHdrsLen & 0x7) != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  LastHeader = NULL;\r
+\r
+  Ip6IsExtsValid (\r
+    NULL,\r
+    NULL,\r
+    &Head->NextHeader,\r
+    ExtHdrs,\r
+    ExtHdrsLen,\r
+    FALSE,\r
+    NULL,\r
+    &LastHeader,\r
+    NULL,\r
+    NULL,\r
+    NULL\r
+    );\r
+\r
+  //\r
+  // Select an interface/source for system packet, application\r
+  // should select them itself.\r
+  //\r
+  IpIf = Interface;\r
+  if (IpIf == NULL) {\r
+    //\r
+    // IpInstance->Interface is NULL when IpInstance is configured with both stationaddress\r
+    // and destinationaddress is unspecified.\r
+    //\r
+    if (IpInstance == NULL || IpInstance->Interface == NULL) {\r
+      IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress);\r
+      if (IpInstance != NULL) {\r
+        IpInstance->Interface = IpIf;\r
+      }\r
+    } else {\r
+      IpIf = IpInstance->Interface;\r
+    }\r
+  }\r
+\r
+  if (IpIf == NULL) {\r
+    return EFI_NO_MAPPING;\r
+  }\r
+\r
+  //\r
+  // Update the common field in Head here.\r
+  //\r
+  Head->Version       = 6;\r
+  Head->TrafficClassL = 0;\r
+  Head->TrafficClassH = 0;\r
+\r
+  Checksum            = NULL;\r
+  NextHeader          = *LastHeader;\r
+\r
+  switch (NextHeader) {\r
+  case EFI_IP_PROTO_UDP:\r
+    Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+    ASSERT (Packet->Udp != NULL);\r
+    if (Packet->Udp->Checksum == 0) {\r
+      Checksum = &Packet->Udp->Checksum;\r
+    }\r
+    break;\r
+\r
+  case EFI_IP_PROTO_TCP:\r
+    Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+    ASSERT (Packet->Tcp != NULL);\r
+    if (Packet->Tcp->Checksum == 0) {\r
+      Checksum = &Packet->Tcp->Checksum;\r
+    }\r
+    break;\r
+\r
+  case IP6_ICMP:\r
+    //\r
+    // Don't send ICMP packet to an IPv6 anycast address.\r
+    //\r
+    if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+    ASSERT (IcmpHead != NULL);\r
+    if (IcmpHead->Checksum == 0) {\r
+      Checksum = &IcmpHead->Checksum;\r
+    }\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  if (Checksum != NULL) {\r
+    //\r
+    // Calculate the checksum for upper layer protocol if it is not calculated due to lack of\r
+    // IPv6 source address.\r
+    //\r
+    PacketChecksum = NetbufChecksum (Packet);\r
+    PseudoChecksum = NetIp6PseudoHeadChecksum (\r
+                      &Head->SourceAddress,\r
+                      &Head->DestinationAddress,\r
+                      NextHeader,\r
+                      Packet->TotalSize\r
+                      );\r
+    *Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum);\r
+  }\r
+\r
+  Status = Ip6IpSecProcessPacket (\r
+             IpSb,\r
+             Head,\r
+             LastHeader, // no need get the lasthead value for output\r
+             &Packet,\r
+             ExtHdrs,\r
+             ExtHdrsLen,\r
+             EfiIPsecOutBound,\r
+             Context\r
+             );\r
+\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  LastHeader = NULL;\r
+  //\r
+  // Check incoming parameters.\r
+  //\r
+  if (!Ip6IsExtsValid (\r
+         IpSb,\r
+         Packet,\r
+         &Head->NextHeader,\r
+         ExtHdrs,\r
+         ExtHdrsLen,\r
+         FALSE,\r
+         NULL,\r
+         &LastHeader,\r
+         &RealExtLen,\r
+         &UnFragmentHdrsLen,\r
+         NULL\r
+         )) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((RealExtLen & 0x7) != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  LastHeaderBackup = *LastHeader;\r
+\r
+  //\r
+  // Perform next hop determination:\r
+  // For multicast packets, the next-hop is always the destination address and\r
+  // is considered to be on-link.\r
+  //\r
+  if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
+    IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress);\r
+  } else {\r
+    //\r
+    // For unicast packets, use a combination of the Destination Cache, the Prefix List\r
+    // and the Default Router List to determine the IP address of the appropriate next hop.\r
+    //\r
+    RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress);\r
+    if (RouteCache == NULL) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+\r
+    IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop);\r
+    Ip6FreeRouteCacheEntry (RouteCache);\r
+  }\r
+\r
+  //\r
+  // Examines the Neighbor Cache for link-layer information about that neighbor.\r
+  // DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error.\r
+  //\r
+  if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) {\r
+    NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop);\r
+    if (NeighborCache == NULL) {\r
+      NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL);\r
+\r
+      if (NeighborCache == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      //\r
+      // Send out multicast neighbor solicitation for address resolution immediatly.\r
+      //\r
+      Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);\r
+      Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+\r
+      Status = Ip6SendNeighborSolicit (\r
+                 IpSb,\r
+                 &Source,\r
+                 &Destination,\r
+                 &NeighborCache->Neighbor,\r
+                 &IpSb->SnpMode.CurrentAddress\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+\r
+      --NeighborCache->Transmit;\r
+      NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1;\r
+    }\r
+\r
+    NeighborCache->Interface = IpIf;\r
+  }\r
+\r
+  UpdatedExtHdrs       = NULL;\r
+  ExtHdrsBackup        = NULL;\r
+  NextHeaderBackup     = 0;\r
+  FragmentHeadInserted = FALSE;\r
+\r
+  //\r
+  // Check whether we received Packet Too Big message for the packet sent to the\r
+  // Destination. If yes include a Fragment Header in the subsequent packets.\r
+  //\r
+  RouteEntry = Ip6FindRouteEntry (\r
+                 IpSb->RouteTable,\r
+                 &Head->DestinationAddress,\r
+                 NULL\r
+                 );\r
+  if (RouteEntry != NULL) {\r
+    if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) {\r
+\r
+      //\r
+      // FragmentHead is inserted after Hop-by-Hop Options header, Destination\r
+      // Options header (first occur), Routing header, and before Fragment header,\r
+      // Authentication header, Encapsulating Security Payload header, and\r
+      // Destination Options header (last occur), and upper-layer header.\r
+      //\r
+      Status = Ip6FillFragmentHeader (\r
+                 IpSb,\r
+                 Head->NextHeader,\r
+                 LastHeaderBackup,\r
+                 ExtHdrs,\r
+                 ExtHdrsLen,\r
+                 0,\r
+                 &UpdatedExtHdrs\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+\r
+      if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {\r
+        NextHeaderBackup = Head->NextHeader;\r
+        Head->NextHeader = IP6_FRAGMENT;\r
+      }\r
+\r
+      ExtHdrsBackup    = ExtHdrs;\r
+      ExtHdrs          = UpdatedExtHdrs;\r
+      ExtHdrsLen       = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);\r
+      RealExtLen       = RealExtLen + sizeof (IP6_FRAGMENT_HEADER);\r
+\r
+      mIp6Id++;\r
+\r
+      FragmentHeadInserted = TRUE;\r
+    }\r
+\r
+    Ip6FreeRouteEntry (RouteEntry);\r
+  }\r
+\r
+  //\r
+  // OK, selected the source and route, fragment the packet then send\r
+  // them. Tag each fragment other than the first one as spawn from it.\r
+  // Each extension header is an integar multiple of 8 octets long, in\r
+  // order to retain 8-octet alignment for subsequent headers.\r
+  //\r
+  Mtu     = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER);\r
+  HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen;\r
+\r
+  if (Packet->TotalSize + HeadLen > Mtu) {\r
+    //\r
+    // Remove the inserted Fragment Header since we need fragment the packet.\r
+    //\r
+    if (FragmentHeadInserted) {\r
+      ExtHdrs    = ExtHdrsBackup;\r
+      ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER);\r
+\r
+      if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {\r
+        Head->NextHeader = NextHeaderBackup;\r
+      }\r
+    }\r
+\r
+    FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen;\r
+\r
+    //\r
+    // The packet is beyond the maximum which can be described through the\r
+    // fragment offset field in Fragment header.\r
+    //\r
+    if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) {\r
+      Status = EFI_BAD_BUFFER_SIZE;\r
+      goto Error;\r
+    }\r
+\r
+    if (FragmentHdrsLen != 0) {\r
+      //\r
+      // Append the fragmentable extension hdrs before the upper layer payload\r
+      // to form a new NET_BUF. This NET_BUF contains all the buffer which will\r
+      // be fragmented below.\r
+      //\r
+      TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen);\r
+      ASSERT (TmpPacket != NULL);\r
+\r
+      //\r
+      // Allocate the space to contain the fragmentable hdrs and copy the data.\r
+      //\r
+      Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE);\r
+      ASSERT (Buf != NULL);\r
+      CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen);\r
+\r
+      //\r
+      // Free the old Packet.\r
+      //\r
+      NetbufFree (Packet);\r
+      Packet = TmpPacket;\r
+    }\r
+\r
+    //\r
+    // The unfragment part which appears in every fragmented IPv6 packet includes\r
+    // the IPv6 header, the unfragmentable extension hdrs and the fragment header.\r
+    //\r
+    UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER);\r
+\r
+    //\r
+    // Mtu now is the length of the fragment part in a full-length fragment.\r
+    //\r
+    Mtu = (Mtu - UnFragmentLen) & (~0x07);\r
+    Num = (Packet->TotalSize + Mtu - 1) / Mtu;\r
+\r
+    for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) {\r
+      //\r
+      // Get fragment from the Packet, append UnFragnmentLen spare buffer\r
+      // before the fragmented data, the corresponding data is filled in later.\r
+      //\r
+      Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen);\r
+      if (Fragment == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Error;\r
+      }\r
+\r
+      FragmentOffset = (UINT16) ((UINT16) Offset | 0x1);\r
+      if (Index == Num - 1){\r
+        //\r
+        // The last fragment, clear the M flag.\r
+        //\r
+        FragmentOffset &= (~0x1);\r
+      }\r
+\r
+      Status = Ip6PrependHead (\r
+                 IpSb,\r
+                 Fragment,\r
+                 Head,\r
+                 FragmentOffset,\r
+                 ExtHdrs,\r
+                 ExtHdrsLen,\r
+                 LastHeaderBackup,\r
+                 UnFragmentLen\r
+                 );\r
+      ASSERT (Status == EFI_SUCCESS);\r
+\r
+      Status = Ip6SendFrame (\r
+                 IpIf,\r
+                 IpInstance,\r
+                 Fragment,\r
+                 &NextHop,\r
+                 Ip6SysPacketSent,\r
+                 Packet\r
+                 );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error;\r
+      }\r
+\r
+      //\r
+      // The last fragment of upper layer packet, update the IP6 token status.\r
+      //\r
+      if ((Index == Num -1) && (Context != NULL)) {\r
+        Wrap                = (IP6_TXTOKEN_WRAP *) Context;\r
+        Wrap->Token->Status = Status;\r
+      }\r
+\r
+      Offset    += PacketLen;\r
+      PacketLen = Packet->TotalSize - Offset;\r
+      if (PacketLen > Mtu) {\r
+        PacketLen = Mtu;\r
+      }\r
+    }\r
+\r
+    NetbufFree (Packet);\r
+    mIp6Id++;\r
+\r
+    if (UpdatedExtHdrs != NULL) {\r
+      FreePool (UpdatedExtHdrs);\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Need not fragment the packet, send it in one frame.\r
+  //\r
+  PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);\r
+  if (PacketHead == NULL) {\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+    goto Error;\r
+  }\r
+\r
+  CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));\r
+  Packet->Ip.Ip6 = PacketHead;\r
+\r
+  if (ExtHdrs != NULL) {\r
+    Buf = (UINT8 *) (PacketHead + 1);\r
+    CopyMem (Buf, ExtHdrs, ExtHdrsLen);\r
+  }\r
+\r
+  if (UpdatedExtHdrs != NULL) {\r
+    //\r
+    // A Fragment Header is inserted to the packet, update the payload length.\r
+    //\r
+    PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) +\r
+                                sizeof (IP6_FRAGMENT_HEADER));\r
+    PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength);\r
+    FreePool (UpdatedExtHdrs);\r
+  }\r
+\r
+  return Ip6SendFrame (\r
+           IpIf,\r
+           IpInstance,\r
+           Packet,\r
+           &NextHop,\r
+           Callback,\r
+           Context\r
+           );\r
+\r
+Error:\r
+  if (UpdatedExtHdrs != NULL) {\r
+    FreePool (UpdatedExtHdrs);\r
+  }\r
+  Ip6CancelPacket (IpIf, Packet, Status);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The filter function to find a packet and all its fragments.\r
+  The packet's fragments have their Context set to the packet.\r
+\r
+  @param[in]  Frame            The frames hold by the low level interface.\r
+  @param[in]  Context          Context to the function, which is the packet.\r
+\r
+  @retval TRUE                 This is the packet to cancel or its fragments.\r
+  @retval FALSE                This is an unrelated packet.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6CancelPacketFragments (\r
+  IN IP6_LINK_TX_TOKEN   *Frame,\r
+  IN VOID                *Context\r
+  )\r
+{\r
+  if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Remove all the frames on the interface that pass the FrameToCancel,\r
+  either queued on ARP queues or that have already been delivered to\r
+  MNP and not yet recycled.\r
+\r
+  @param[in]  Interface     Interface to remove the frames from.\r
+  @param[in]  IoStatus      The transmit status returned to the frames' callback.\r
+  @param[in]  FrameToCancel Function to select the frame to cancel; NULL to select all.\r
+  @param[in]  Context       Opaque parameters passed to FrameToCancel. Ignored if\r
+                            FrameToCancel is NULL.\r
+\r
+**/\r
+VOID\r
+Ip6CancelFrames (\r
+  IN IP6_INTERFACE          *Interface,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN IP6_FRAME_TO_CANCEL    FrameToCancel   OPTIONAL,\r
+  IN VOID                   *Context        OPTIONAL\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_LINK_TX_TOKEN         *Token;\r
+  IP6_SERVICE               *IpSb;\r
+  IP6_NEIGHBOR_ENTRY        *ArpQue;\r
+  EFI_STATUS                Status;\r
+\r
+  IpSb = Interface->Service;\r
+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+  //\r
+  // Cancel all the pending frames on ARP requests\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {\r
+    ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);\r
+\r
+    Status = Ip6FreeNeighborEntry (\r
+               IpSb,\r
+               ArpQue,\r
+               FALSE,\r
+               FALSE,\r
+               IoStatus,\r
+               FrameToCancel,\r
+               Context\r
+               );\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+\r
+  //\r
+  // Cancel all the frames that have been delivered to MNP\r
+  // but not yet recycled.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {\r
+    Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);\r
+\r
+    if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {\r
+      IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken);\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Cancel the Packet and all its fragments.\r
+\r
+  @param[in]  IpIf                 The interface from which the Packet is sent.\r
+  @param[in]  Packet               The Packet to cancel.\r
+  @param[in]  IoStatus             The status returns to the sender.\r
+\r
+**/\r
+VOID\r
+Ip6CancelPacket (\r
+  IN IP6_INTERFACE    *IpIf,\r
+  IN NET_BUF          *Packet,\r
+  IN EFI_STATUS       IoStatus\r
+  )\r
+{\r
+  Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet);\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Output.h b/NetworkPkg/Ip6Dxe/Ip6Output.h
new file mode 100644 (file)
index 0000000..80abe85
--- /dev/null
@@ -0,0 +1,141 @@
+/** @file\r
+  The internal functions and routines to transmit the IP6 packet.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_OUTPUT_H__\r
+#define __EFI_IP6_OUTPUT_H__\r
+\r
+extern UINT32 mIp6Id;\r
+\r
+/**\r
+  Output all the available source addresses to the list entry head SourceList. The\r
+  number of source addresses are also returned.\r
+\r
+  @param[in]       IpSb             Points to a IP6 service binding instance.\r
+  @param[in]       Destination      The IPv6 destination address.\r
+  @param[out]      Source           The selected IPv6 source address according to\r
+                                    the Destination.\r
+\r
+  @retval EFI_SUCCESS           The source addresses were copied to the list entry\r
+                                head SourceList.\r
+  @retval EFI_NO_MAPPING        The IPv6 stack is not auto configured.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SelectSourceAddress (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Destination,\r
+  OUT EFI_IPv6_ADDRESS      *Source\r
+  );\r
+\r
+/**\r
+  The default callback function for system generated packet.\r
+  It will free the packet.\r
+\r
+  @param[in]  Packet        The packet that transmitted.\r
+  @param[in]  IoStatus      The result of the transmission: succeeded or failed.\r
+  @param[in]  LinkFlag      Not used when transmission. Check IP6_FRAME_CALLBACK\r
+                            for reference.\r
+  @param[in]  Context       The context provided by us.\r
+\r
+**/\r
+VOID\r
+Ip6SysPacketSent (\r
+  NET_BUF                   *Packet,\r
+  EFI_STATUS                IoStatus,\r
+  UINT32                    LinkFlag,\r
+  VOID                      *Context\r
+  );\r
+\r
+/**\r
+  Transmit an IP6 packet. The packet comes either from the IP6\r
+  child's consumer (IpInstance != NULL) or the IP6 driver itself\r
+  (IpInstance == NULL). It will route the packet, fragment it,\r
+  then transmit all the fragments through an interface.\r
+\r
+  @param[in]  IpSb             The IP6 service instance to transmit the packet.\r
+  @param[in]  Interface        The IP6 interface to transmit the packet. Ignored\r
+                               if NULL.\r
+  @param[in]  IpInstance       The IP6 child that issues the transmission.  It is\r
+                               NULL if the packet is from the system.\r
+  @param[in]  Packet           The user data to send, excluding the IP header.\r
+  @param[in]  Head             The caller supplied header. The caller should set\r
+                               the  following header fields: NextHeader, HopLimit,\r
+                               Src, Dest, FlowLabel, PayloadLength. This function\r
+                               will fill in the Ver, TrafficClass.\r
+  @param[in]  ExtHdrs          The extension headers to append to the IPv6 basic\r
+                               header.\r
+  @param[in]  ExtHdrsLen       The length of the extension headers.\r
+  @param[in]  Callback         The callback function to issue when transmission\r
+                               completed.\r
+  @param[in]  Context          The opaque context for the callback.\r
+\r
+  @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.\r
+  @retval EFI_NO_MAPPING        There is no interface to the destination.\r
+  @retval EFI_NOT_FOUND         There is no route to the destination.\r
+  @retval EFI_SUCCESS           The packet successfully transmitted.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to finish the operation due to lack of\r
+                                resources.\r
+  @retval Others                Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Output (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN IP6_INTERFACE          *Interface   OPTIONAL,\r
+  IN IP6_PROTOCOL           *IpInstance  OPTIONAL,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_IP6_HEADER         *Head,\r
+  IN UINT8                  *ExtHdrs,\r
+  IN UINT32                 ExtHdrsLen,\r
+  IN IP6_FRAME_CALLBACK     Callback,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+/**\r
+  Remove all the frames on the interface that pass the FrameToCancel,\r
+  either queued on ARP queues, or that have already been delivered to\r
+  MNP and not yet recycled.\r
+\r
+  @param[in]  Interface     Interface to remove the frames from.\r
+  @param[in]  IoStatus      The transmit status returned to the frames' callback.\r
+  @param[in]  FrameToCancel Function to select the frame to cancel; NULL to select all.\r
+  @param[in]  Context       Opaque parameters passed to FrameToCancel. Ignored if\r
+                            FrameToCancel is NULL.\r
+\r
+**/\r
+VOID\r
+Ip6CancelFrames (\r
+  IN IP6_INTERFACE          *Interface,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN IP6_FRAME_TO_CANCEL    FrameToCancel   OPTIONAL,\r
+  IN VOID                   *Context        OPTIONAL\r
+  );\r
+\r
+/**\r
+  Cancel the Packet and all its fragments.\r
+\r
+  @param[in]  IpIf                 The interface from which the Packet is sent.\r
+  @param[in]  Packet               The Packet to cancel.\r
+  @param[in]  IoStatus             The status returns to the sender.\r
+\r
+**/\r
+VOID\r
+Ip6CancelPacket (\r
+  IN IP6_INTERFACE    *IpIf,\r
+  IN NET_BUF          *Packet,\r
+  IN EFI_STATUS       IoStatus\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Route.c b/NetworkPkg/Ip6Dxe/Ip6Route.c
new file mode 100644 (file)
index 0000000..bba365c
--- /dev/null
@@ -0,0 +1,635 @@
+/** @file\r
+  The functions and routines to handle the route caches and route table.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+  This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value\r
+  as the index of the route cache bucket according to the prefix of two IPv6 addresses.\r
+\r
+  @param[in]  Ip1     The IPv6 address.\r
+  @param[in]  Ip2     The IPv6 address.\r
+\r
+  @return The hash value of the prefix of two IPv6 addresses.\r
+\r
+**/\r
+UINT32\r
+Ip6RouteCacheHash (\r
+  IN EFI_IPv6_ADDRESS       *Ip1,\r
+  IN EFI_IPv6_ADDRESS       *Ip2\r
+  )\r
+{\r
+  UINT32 Prefix1;\r
+  UINT32 Prefix2;\r
+\r
+  Prefix1 = *((UINT32 *) ((UINTN *) (Ip1)));\r
+  Prefix2 = *((UINT32 *) ((UINTN *) (Ip2)));\r
+\r
+  return ((UINT32) (Prefix1 ^ Prefix2) % IP6_ROUTE_CACHE_HASH_SIZE);\r
+}\r
+\r
+/**\r
+  Allocate a route entry then initialize it with the Destination/PrefixLength\r
+  and Gateway.\r
+\r
+  @param[in]  Destination     The IPv6 destination address. This is an optional\r
+                              parameter that may be NULL.\r
+  @param[in]  PrefixLength    The destination network's prefix length.\r
+  @param[in]  GatewayAddress  The next hop address. This is an optional parameter\r
+                              that may be NULL.\r
+\r
+  @return NULL if failed to allocate memeory; otherwise, the newly created route entry.\r
+\r
+**/\r
+IP6_ROUTE_ENTRY *\r
+Ip6CreateRouteEntry (\r
+  IN EFI_IPv6_ADDRESS       *Destination    OPTIONAL,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *GatewayAddress OPTIONAL\r
+  )\r
+{\r
+  IP6_ROUTE_ENTRY           *RtEntry;\r
+\r
+  RtEntry = AllocateZeroPool (sizeof (IP6_ROUTE_ENTRY));\r
+\r
+  if (RtEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  RtEntry->RefCnt       = 1;\r
+  RtEntry->Flag         = 0;\r
+  RtEntry->PrefixLength = PrefixLength;\r
+\r
+  if (Destination != NULL) {\r
+    IP6_COPY_ADDRESS (&RtEntry->Destination, Destination);\r
+  }\r
+\r
+  if (GatewayAddress != NULL) {\r
+    IP6_COPY_ADDRESS (&RtEntry->NextHop, GatewayAddress);\r
+  }\r
+\r
+  return RtEntry;\r
+}\r
+\r
+/**\r
+  Free the route table entry. It is reference counted.\r
+\r
+  @param[in, out]  RtEntry  The route entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeRouteEntry (\r
+  IN OUT IP6_ROUTE_ENTRY    *RtEntry\r
+  )\r
+{\r
+  ASSERT ((RtEntry != NULL) && (RtEntry->RefCnt > 0));\r
+\r
+  if (--RtEntry->RefCnt == 0) {\r
+    FreePool (RtEntry);\r
+  }\r
+}\r
+\r
+/**\r
+  Search the route table for a most specific match to the Dst. It searches\r
+  from the longest route area (prefix length == 128) to the shortest route area\r
+  (default routes). In each route area, it will first search the instance's\r
+  route table, then the default route table. This is required per the following\r
+  requirements:\r
+  1. IP search the route table for a most specific match.\r
+  2. The local route entries have precedence over the default route entry.\r
+\r
+  @param[in]  RtTable       The route table to search from.\r
+  @param[in]  Destination   The destionation address to search. If NULL, search\r
+                            the route table by NextHop.\r
+  @param[in]  NextHop       The next hop address. If NULL, search the route table\r
+                            by Destination.\r
+\r
+  @return NULL if no route matches the Dst. Otherwise, the point to the\r
+  @return most specific route to the Dst.\r
+\r
+**/\r
+IP6_ROUTE_ENTRY *\r
+Ip6FindRouteEntry (\r
+  IN IP6_ROUTE_TABLE        *RtTable,\r
+  IN EFI_IPv6_ADDRESS       *Destination OPTIONAL,\r
+  IN EFI_IPv6_ADDRESS       *NextHop     OPTIONAL\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  IP6_ROUTE_ENTRY           *RtEntry;\r
+  INTN                      Index;\r
+\r
+  ASSERT (Destination != NULL || NextHop != NULL);\r
+\r
+  RtEntry = NULL;\r
+\r
+  for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) {\r
+    NET_LIST_FOR_EACH (Entry, &RtTable->RouteArea[Index]) {\r
+      RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+\r
+      if (Destination != NULL) {\r
+        if (NetIp6IsNetEqual (Destination, &RtEntry->Destination, RtEntry->PrefixLength)) {\r
+          NET_GET_REF (RtEntry);\r
+          return RtEntry;\r
+        }\r
+      } else if (NextHop != NULL) {\r
+        if (NetIp6IsNetEqual (NextHop, &RtEntry->NextHop, RtEntry->PrefixLength)) {\r
+          NET_GET_REF (RtEntry);\r
+          return RtEntry;\r
+        }\r
+      }\r
+\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Allocate and initialize a IP6 route cache entry.\r
+\r
+  @param[in]  Dst           The destination address.\r
+  @param[in]  Src           The source address.\r
+  @param[in]  GateWay       The next hop address.\r
+  @param[in]  Tag           The tag from the caller. This marks all the cache entries\r
+                            spawned from one route table entry.\r
+\r
+  @return NULL if failed to allocate memory for the cache. Otherwise, point\r
+          to the created route cache entry.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6CreateRouteCacheEntry (\r
+  IN EFI_IPv6_ADDRESS       *Dst,\r
+  IN EFI_IPv6_ADDRESS       *Src,\r
+  IN EFI_IPv6_ADDRESS       *GateWay,\r
+  IN UINTN                  Tag\r
+  )\r
+{\r
+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+\r
+  RtCacheEntry = AllocatePool (sizeof (IP6_ROUTE_CACHE_ENTRY));\r
+\r
+  if (RtCacheEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  RtCacheEntry->RefCnt = 1;\r
+  RtCacheEntry->Tag    = Tag;\r
+\r
+  IP6_COPY_ADDRESS (&RtCacheEntry->Destination, Dst);\r
+  IP6_COPY_ADDRESS (&RtCacheEntry->Source, Src);\r
+  IP6_COPY_ADDRESS (&RtCacheEntry->NextHop, GateWay);\r
+\r
+  return RtCacheEntry;\r
+}\r
+\r
+/**\r
+  Free the route cache entry. It is reference counted.\r
+\r
+  @param[in, out]  RtCacheEntry  The route cache entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeRouteCacheEntry (\r
+  IN OUT IP6_ROUTE_CACHE_ENTRY  *RtCacheEntry\r
+  )\r
+{\r
+  ASSERT (RtCacheEntry->RefCnt > 0);\r
+\r
+  if (--RtCacheEntry->RefCnt == 0) {\r
+    FreePool (RtCacheEntry);\r
+  }\r
+}\r
+\r
+/**\r
+  Find a route cache with the destination and source address. This is\r
+  used by the ICMPv6 redirect messasge process.\r
+\r
+  @param[in]  RtTable       The route table to search the cache for.\r
+  @param[in]  Dest          The destination address.\r
+  @param[in]  Src           The source address.\r
+\r
+  @return NULL if no route entry to the (Dest, Src). Otherwise, the pointer\r
+          to the correct route cache entry.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6FindRouteCache (\r
+  IN IP6_ROUTE_TABLE        *RtTable,\r
+  IN EFI_IPv6_ADDRESS       *Dest,\r
+  IN EFI_IPv6_ADDRESS       *Src\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+  UINT32                    Index;\r
+\r
+  Index = IP6_ROUTE_CACHE_HASH (Dest, Src);\r
+\r
+  NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) {\r
+    RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);\r
+\r
+    if (EFI_IP6_EQUAL (Dest, &RtCacheEntry->Destination)&& EFI_IP6_EQUAL (Src, &RtCacheEntry->Source)) {\r
+      NET_GET_REF (RtCacheEntry);\r
+      return RtCacheEntry;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Build an array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number\r
+  of EFI_IP6_ROUTE_TABLE is also returned.\r
+\r
+  @param[in]  RouteTable        The pointer of IP6_ROUTE_TABLE internal used.\r
+  @param[out] EfiRouteCount     The number of returned route entries.\r
+  @param[out] EfiRouteTable     The pointer to the array of EFI_IP6_ROUTE_TABLE.\r
+                                If NULL, only the route entry count is returned.\r
+\r
+  @retval EFI_SUCCESS           The EFI_IP6_ROUTE_TABLE successfully built.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiRouteTable (\r
+  IN IP6_ROUTE_TABLE        *RouteTable,\r
+  OUT UINT32                *EfiRouteCount,\r
+  OUT EFI_IP6_ROUTE_TABLE   **EfiRouteTable OPTIONAL\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  IP6_ROUTE_ENTRY           *RtEntry;\r
+  EFI_IP6_ROUTE_TABLE       *EfiTable;\r
+  UINT32                    Count;\r
+  INT32                     Index;\r
+\r
+  ASSERT (EfiRouteCount != NULL);\r
+\r
+  Count          = RouteTable->TotalNum;\r
+  *EfiRouteCount = Count;\r
+\r
+  if ((EfiRouteTable == NULL) || (Count == 0)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (*EfiRouteTable == NULL) {\r
+    *EfiRouteTable = AllocatePool (sizeof (EFI_IP6_ROUTE_TABLE) * Count);\r
+    if (*EfiRouteTable == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+  EfiTable = *EfiRouteTable;\r
+\r
+  //\r
+  // Copy the route entry to EFI route table.\r
+  //\r
+  Count = 0;\r
+\r
+  for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) {\r
+\r
+    NET_LIST_FOR_EACH (Entry, &(RouteTable->RouteArea[Index])) {\r
+      RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+\r
+      Ip6CopyAddressByPrefix (\r
+        &EfiTable[Count].Destination,\r
+        &RtEntry->Destination,\r
+        RtEntry->PrefixLength\r
+        );\r
+\r
+      IP6_COPY_ADDRESS (&EfiTable[Count].Gateway, &RtEntry->NextHop);\r
+      EfiTable[Count].PrefixLength = RtEntry->PrefixLength;\r
+\r
+      Count++;\r
+    }\r
+  }\r
+\r
+  ASSERT (Count == RouteTable->TotalNum);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Create an empty route table. This includes its internal route cache.\r
+\r
+  @return NULL if failed to allocate memory for the route table. Otherwise,\r
+          the point to newly created route table.\r
+\r
+**/\r
+IP6_ROUTE_TABLE *\r
+Ip6CreateRouteTable (\r
+  VOID\r
+  )\r
+{\r
+  IP6_ROUTE_TABLE           *RtTable;\r
+  UINT32                    Index;\r
+\r
+  RtTable = AllocatePool (sizeof (IP6_ROUTE_TABLE));\r
+  if (RtTable == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  RtTable->RefCnt   = 1;\r
+  RtTable->TotalNum = 0;\r
+\r
+  for (Index = 0; Index < IP6_PREFIX_NUM; Index++) {\r
+    InitializeListHead (&RtTable->RouteArea[Index]);\r
+  }\r
+\r
+  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {\r
+    InitializeListHead (&RtTable->Cache.CacheBucket[Index]);\r
+    RtTable->Cache.CacheNum[Index] = 0;\r
+  }\r
+\r
+  return RtTable;\r
+}\r
+\r
+/**\r
+  Free the route table and its associated route cache. Route\r
+  table is reference counted.\r
+\r
+  @param[in, out]  RtTable      The route table to free.\r
+\r
+**/\r
+VOID\r
+Ip6CleanRouteTable (\r
+  IN OUT IP6_ROUTE_TABLE        *RtTable\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_ROUTE_ENTRY           *RtEntry;\r
+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+  UINT32                    Index;\r
+\r
+  ASSERT (RtTable->RefCnt > 0);\r
+\r
+  if (--RtTable->RefCnt > 0) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Free all the route table entry and its route cache.\r
+  //\r
+  for (Index = 0; Index < IP6_PREFIX_NUM; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->RouteArea[Index]) {\r
+      RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+      RemoveEntryList (Entry);\r
+      Ip6FreeRouteEntry (RtEntry);\r
+    }\r
+  }\r
+\r
+  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->Cache.CacheBucket[Index]) {\r
+      RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);\r
+      RemoveEntryList (Entry);\r
+      Ip6FreeRouteCacheEntry (RtCacheEntry);\r
+    }\r
+  }\r
+\r
+  FreePool (RtTable);\r
+}\r
+\r
+/**\r
+  Remove all the cache entries bearing the Tag. When a route cache\r
+  entry is created, it is tagged with the address of route entry\r
+  from which it is spawned. When a route entry is deleted, the cache\r
+  entries spawned from it are also deleted.\r
+\r
+  @param[in]  RtCache       Route cache to remove the entries from.\r
+  @param[in]  Tag           The Tag of the entries to remove.\r
+\r
+**/\r
+VOID\r
+Ip6PurgeRouteCache (\r
+  IN IP6_ROUTE_CACHE        *RtCache,\r
+  IN UINTN                  Tag\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+  UINT32                    Index;\r
+\r
+  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {\r
+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) {\r
+\r
+      RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);\r
+\r
+      if (RtCacheEntry->Tag == Tag) {\r
+        RemoveEntryList (Entry);\r
+        Ip6FreeRouteCacheEntry (RtCacheEntry);\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Add a route entry to the route table. It is the help function for EfiIp6Routes.\r
+\r
+  @param[in, out]  RtTable        Route table to add route to.\r
+  @param[in]       Destination    The destination of the network.\r
+  @param[in]       PrefixLength   The PrefixLength of the destination.\r
+  @param[in]       GatewayAddress The next hop address.\r
+\r
+  @retval EFI_ACCESS_DENIED     The same route already exists.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the entry.\r
+  @retval EFI_SUCCESS           The route was added successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6AddRoute (\r
+  IN OUT IP6_ROUTE_TABLE    *RtTable,\r
+  IN EFI_IPv6_ADDRESS       *Destination,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *GatewayAddress\r
+  )\r
+{\r
+  LIST_ENTRY                *ListHead;\r
+  LIST_ENTRY                *Entry;\r
+  IP6_ROUTE_ENTRY           *Route;\r
+\r
+  ListHead = &RtTable->RouteArea[PrefixLength];\r
+\r
+  //\r
+  // First check whether the route exists\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, ListHead) {\r
+    Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+\r
+    if (NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength) &&\r
+        EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {\r
+      return EFI_ACCESS_DENIED;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Create a route entry and insert it to the route area.\r
+  //\r
+  Route = Ip6CreateRouteEntry (Destination, PrefixLength, GatewayAddress);\r
+\r
+  if (Route == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  if (NetIp6IsUnspecifiedAddr (GatewayAddress)) {\r
+    Route->Flag = IP6_DIRECT_ROUTE;\r
+  }\r
+\r
+  InsertHeadList (ListHead, &Route->Link);\r
+  RtTable->TotalNum++;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Remove a route entry and all the route caches spawn from it.\r
+  It is the help function for EfiIp6Routes.\r
+\r
+  @param[in, out] RtTable           The route table to remove the route from.\r
+  @param[in]      Destination       The destination network.\r
+  @param[in]      PrefixLength      The PrefixLength of the Destination.\r
+  @param[in]      GatewayAddress    The next hop address.\r
+\r
+  @retval EFI_SUCCESS           The route entry was successfully removed.\r
+  @retval EFI_NOT_FOUND         There is no route entry in the table with that\r
+                                property.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6DelRoute (\r
+  IN OUT IP6_ROUTE_TABLE    *RtTable,\r
+  IN EFI_IPv6_ADDRESS       *Destination,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *GatewayAddress\r
+  )\r
+{\r
+  LIST_ENTRY                *ListHead;\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  IP6_ROUTE_ENTRY           *Route;\r
+  UINT32                    TotalNum;\r
+\r
+  ListHead = &RtTable->RouteArea[PrefixLength];\r
+  TotalNum = RtTable->TotalNum;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, ListHead) {\r
+    Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+\r
+    if (Destination != NULL && !NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength)) {\r
+      continue;\r
+    }\r
+    if (GatewayAddress != NULL && !EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {\r
+      continue;\r
+    }\r
+\r
+    Ip6PurgeRouteCache (&RtTable->Cache, (UINTN) Route);\r
+    RemoveEntryList (Entry);\r
+    Ip6FreeRouteEntry (Route);\r
+\r
+    ASSERT (RtTable->TotalNum > 0);\r
+    RtTable->TotalNum--;\r
+  }\r
+\r
+  return TotalNum == RtTable->TotalNum ? EFI_NOT_FOUND : EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Search the route table to route the packet. Return/create a route\r
+  cache if there is a route to the destination.\r
+\r
+  @param[in]  IpSb          The IP6 service data.\r
+  @param[in]  Dest          The destination address to search for.\r
+  @param[in]  Src           The source address to search for.\r
+\r
+  @return NULL if it failed to route the packet. Otherwise, a route cache\r
+          entry that can be used to route packets.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6Route (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Dest,\r
+  IN EFI_IPv6_ADDRESS       *Src\r
+  )\r
+{\r
+  IP6_ROUTE_TABLE           *RtTable;\r
+  LIST_ENTRY                *ListHead;\r
+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;\r
+  IP6_ROUTE_ENTRY           *RtEntry;\r
+  EFI_IPv6_ADDRESS          NextHop;\r
+  UINT32                    Index;\r
+\r
+  RtTable = IpSb->RouteTable;\r
+\r
+  ASSERT (RtTable != NULL);\r
+\r
+  //\r
+  // Search the destination cache in IP6_ROUTE_TABLE.\r
+  //\r
+  Index    = IP6_ROUTE_CACHE_HASH (Dest, Src);\r
+  ListHead = &RtTable->Cache.CacheBucket[Index];\r
+\r
+  RtCacheEntry = Ip6FindRouteCache (RtTable, Dest, Src);\r
+\r
+  //\r
+  // If found, promote the cache entry to the head of the hash bucket.\r
+  //\r
+  if (RtCacheEntry != NULL) {\r
+    RemoveEntryList (&RtCacheEntry->Link);\r
+    InsertHeadList (ListHead, &RtCacheEntry->Link);\r
+    return RtCacheEntry;\r
+  }\r
+\r
+  //\r
+  // Search the route table for the most specific route\r
+  //\r
+  RtEntry = Ip6FindRouteEntry (RtTable, Dest, NULL);\r
+  if (RtEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Found a route to the Dest, if it is a direct route, the packet\r
+  // will be send directly to the destination, such as for connected\r
+  // network. Otherwise, it is an indirect route, the packet will be\r
+  // send the next hop router.\r
+  //\r
+  if ((RtEntry->Flag & IP6_DIRECT_ROUTE) == IP6_DIRECT_ROUTE) {\r
+    IP6_COPY_ADDRESS (&NextHop, Dest);\r
+  } else {\r
+    IP6_COPY_ADDRESS (&NextHop, &RtEntry->NextHop);\r
+  }\r
+\r
+  Ip6FreeRouteEntry (RtEntry);\r
+\r
+  //\r
+  // Create a route cache entry, and tag it as spawned from this route entry\r
+  //\r
+  RtCacheEntry = Ip6CreateRouteCacheEntry (Dest, Src, &NextHop, (UINTN) RtEntry);\r
+\r
+  if (RtCacheEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  InsertHeadList (ListHead, &RtCacheEntry->Link);\r
+  NET_GET_REF (RtCacheEntry);\r
+  RtTable->Cache.CacheNum[Index]++;\r
+\r
+  return RtCacheEntry;\r
+}\r
+\r
diff --git a/NetworkPkg/Ip6Dxe/Ip6Route.h b/NetworkPkg/Ip6Dxe/Ip6Route.h
new file mode 100644 (file)
index 0000000..d81e07b
--- /dev/null
@@ -0,0 +1,299 @@
+/** @file\r
+  EFI IP6 route table and route cache table defintions.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_ROUTE_H__\r
+#define __EFI_IP6_ROUTE_H__\r
+\r
+#define IP6_DIRECT_ROUTE          0x00000001\r
+#define IP6_PACKET_TOO_BIG        0x00000010\r
+\r
+#define IP6_ROUTE_CACHE_HASH_SIZE 31\r
+///\r
+/// Max NO. of cache entry per hash bucket\r
+///\r
+#define IP6_ROUTE_CACHE_MAX       32\r
+\r
+#define IP6_ROUTE_CACHE_HASH(Ip1, Ip2) Ip6RouteCacheHash ((Ip1), (Ip2))\r
+\r
+typedef struct {\r
+  LIST_ENTRY                Link;\r
+  INTN                      RefCnt;\r
+  UINT32                    Flag;\r
+  UINT8                     PrefixLength;\r
+  EFI_IPv6_ADDRESS          Destination;\r
+  EFI_IPv6_ADDRESS          NextHop;\r
+} IP6_ROUTE_ENTRY;\r
+\r
+typedef struct {\r
+  LIST_ENTRY                Link;\r
+  INTN                      RefCnt;\r
+  UINTN                     Tag;\r
+  EFI_IPv6_ADDRESS          Destination;\r
+  EFI_IPv6_ADDRESS          Source;\r
+  EFI_IPv6_ADDRESS          NextHop;\r
+} IP6_ROUTE_CACHE_ENTRY;\r
+\r
+typedef struct {\r
+  LIST_ENTRY                CacheBucket[IP6_ROUTE_CACHE_HASH_SIZE];\r
+  UINT8                     CacheNum[IP6_ROUTE_CACHE_HASH_SIZE];\r
+} IP6_ROUTE_CACHE;\r
+\r
+//\r
+// Each IP6 instance has its own route table. Each ServiceBinding\r
+// instance has a default route table and default address.\r
+//\r
+// All the route table entries with the same prefix length are linked\r
+// together in one route area. For example, RouteArea[0] contains\r
+// the default routes. A route table also contains a route cache.\r
+//\r
+\r
+typedef struct _IP6_ROUTE_TABLE {\r
+  INTN                      RefCnt;\r
+  UINT32                    TotalNum;\r
+  LIST_ENTRY                RouteArea[IP6_PREFIX_NUM];\r
+  IP6_ROUTE_CACHE           Cache;\r
+} IP6_ROUTE_TABLE;\r
+\r
+/**\r
+  This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value\r
+  as the index of the route cache bucket according to the prefix of two IPv6 addresses.\r
+\r
+  @param[in]  Ip1     The IPv6 address.\r
+  @param[in]  Ip2     The IPv6 address.\r
+\r
+  @return The hash value of the prefix of two IPv6 addresses.\r
+\r
+**/\r
+UINT32\r
+Ip6RouteCacheHash (\r
+  IN EFI_IPv6_ADDRESS       *Ip1,\r
+  IN EFI_IPv6_ADDRESS       *Ip2\r
+  );\r
+\r
+/**\r
+  Allocate and initialize an IP6 route cache entry.\r
+\r
+  @param[in]  Dst           The destination address.\r
+  @param[in]  Src           The source address.\r
+  @param[in]  GateWay       The next hop address.\r
+  @param[in]  Tag           The tag from the caller. This marks all the cache entries\r
+                            spawned from one route table entry.\r
+\r
+  @return NULL if it failed to allocate memory for the cache. Otherwise, point\r
+          to the created route cache entry.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6CreateRouteCacheEntry (\r
+  IN EFI_IPv6_ADDRESS       *Dst,\r
+  IN EFI_IPv6_ADDRESS       *Src,\r
+  IN EFI_IPv6_ADDRESS       *GateWay,\r
+  IN UINTN                  Tag\r
+  );\r
+\r
+/**\r
+  Free the route cache entry. It is reference counted.\r
+\r
+  @param[in, out]  RtCacheEntry  The route cache entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeRouteCacheEntry (\r
+  IN OUT IP6_ROUTE_CACHE_ENTRY  *RtCacheEntry\r
+  );\r
+\r
+/**\r
+  Find a route cache with the destination and source address. This is\r
+  used by the ICMPv6 redirect messasge process.\r
+\r
+  @param[in]  RtTable       The route table to search the cache for.\r
+  @param[in]  Dest          The destination address.\r
+  @param[in]  Src           The source address.\r
+\r
+  @return NULL if no route entry to the (Dest, Src). Otherwise, point\r
+          to the correct route cache entry.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6FindRouteCache (\r
+  IN IP6_ROUTE_TABLE        *RtTable,\r
+  IN EFI_IPv6_ADDRESS       *Dest,\r
+  IN EFI_IPv6_ADDRESS       *Src\r
+  );\r
+\r
+/**\r
+  Build a array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number\r
+  of EFI_IP6_ROUTE_TABLE is also returned.\r
+\r
+  @param[in]  RouteTable        The pointer of IP6_ROUTE_TABLE internal used.\r
+  @param[out] EfiRouteCount     The number of returned route entries.\r
+  @param[out] EfiRouteTable     The pointer to the array of EFI_IP6_ROUTE_TABLE.\r
+                                If NULL, only the route entry count is returned.\r
+\r
+  @retval EFI_SUCCESS           The EFI_IP6_ROUTE_TABLE successfully built.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiRouteTable (\r
+  IN IP6_ROUTE_TABLE        *RouteTable,\r
+  OUT UINT32                *EfiRouteCount,\r
+  OUT EFI_IP6_ROUTE_TABLE   **EfiRouteTable OPTIONAL\r
+  );\r
+\r
+/**\r
+  Create an empty route table, includes its internal route cache.\r
+\r
+  @return NULL if failed to allocate memory for the route table. Otherwise,\r
+          the point to newly created route table.\r
+\r
+**/\r
+IP6_ROUTE_TABLE *\r
+Ip6CreateRouteTable (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Free the route table and its associated route cache. Route\r
+  table is reference counted.\r
+\r
+  @param[in, out]  RtTable      The route table to free.\r
+\r
+**/\r
+VOID\r
+Ip6CleanRouteTable (\r
+  IN OUT IP6_ROUTE_TABLE        *RtTable\r
+  );\r
+\r
+/**\r
+  Allocate a route entry then initialize it with the Destination/PrefixLength\r
+  and Gateway.\r
+\r
+  @param[in]  Destination     The IPv6 destination address. This is an optional\r
+                              parameter that may be NULL.\r
+  @param[in]  PrefixLength    The destination network's prefix length.\r
+  @param[in]  GatewayAddress  The next hop address. This is optional parameter\r
+                              that may be NULL.\r
+\r
+  @return NULL if it failed to allocate memeory. Otherwise, the newly created route entry.\r
+\r
+**/\r
+IP6_ROUTE_ENTRY *\r
+Ip6CreateRouteEntry (\r
+  IN EFI_IPv6_ADDRESS       *Destination    OPTIONAL,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *GatewayAddress OPTIONAL\r
+  );\r
+\r
+/**\r
+  Search the route table for a most specific match to the Dst. It searches\r
+  from the longest route area (prefix length == 128) to the shortest route area\r
+  (default routes). In each route area, it will first search the instance's\r
+  route table, then the default route table. This is required per the following\r
+  requirements:\r
+  1. IP search the route table for a most specific match.\r
+  2. The local route entries have precedence over the default route entry.\r
+\r
+  @param[in]  RtTable       The route table to search from.\r
+  @param[in]  Destination   The destionation address to search. If NULL, search\r
+                            the route table by NextHop.\r
+  @param[in]  NextHop       The next hop address. If NULL, search the route table\r
+                            by Destination.\r
+\r
+  @return NULL if no route matches the Dst. Otherwise the point to the\r
+          most specific route to the Dst.\r
+\r
+**/\r
+IP6_ROUTE_ENTRY *\r
+Ip6FindRouteEntry (\r
+  IN IP6_ROUTE_TABLE        *RtTable,\r
+  IN EFI_IPv6_ADDRESS       *Destination OPTIONAL,\r
+  IN EFI_IPv6_ADDRESS       *NextHop     OPTIONAL\r
+  );\r
+\r
+/**\r
+  Free the route table entry. It is reference counted.\r
+\r
+  @param[in, out]  RtEntry  The route entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeRouteEntry (\r
+  IN OUT IP6_ROUTE_ENTRY    *RtEntry\r
+  );\r
+\r
+/**\r
+  Add a route entry to the route table. It is the help function for EfiIp6Routes.\r
+\r
+  @param[in, out]  RtTable        Route table to add route to.\r
+  @param[in]       Destination    The destination of the network.\r
+  @param[in]       PrefixLength   The PrefixLength of the destination.\r
+  @param[in]       GatewayAddress The next hop address.\r
+\r
+  @retval EFI_ACCESS_DENIED     The same route already exists.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the entry.\r
+  @retval EFI_SUCCESS           The route was added successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6AddRoute (\r
+  IN OUT IP6_ROUTE_TABLE    *RtTable,\r
+  IN EFI_IPv6_ADDRESS       *Destination,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *GatewayAddress\r
+  );\r
+\r
+/**\r
+  Remove a route entry and all the route caches spawn from it.\r
+  It is the help function for EfiIp6Routes.\r
+\r
+  @param[in, out] RtTable           The route table to remove the route from.\r
+  @param[in]      Destination       The destination network.\r
+  @param[in]      PrefixLength      The PrefixLength of the Destination.\r
+  @param[in]      GatewayAddress    The next hop address.\r
+\r
+  @retval EFI_SUCCESS           Successfully removed the route entry.\r
+  @retval EFI_NOT_FOUND         There is no route entry in the table with that\r
+                                properity.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6DelRoute (\r
+  IN OUT IP6_ROUTE_TABLE    *RtTable,\r
+  IN EFI_IPv6_ADDRESS       *Destination,\r
+  IN UINT8                  PrefixLength,\r
+  IN EFI_IPv6_ADDRESS       *GatewayAddress\r
+  );\r
+\r
+/**\r
+  Search the route table to route the packet. Return/create a route\r
+  cache if there is a route to the destination.\r
+\r
+  @param[in]  IpSb          The IP6 service data.\r
+  @param[in]  Dest          The destination address to search for.\r
+  @param[in]  Src           The source address to search for.\r
+\r
+  @return NULL if failed to route packet. Otherwise, a route cache\r
+          entry that can be used to route packet.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6Route (\r
+  IN IP6_SERVICE            *IpSb,\r
+  IN EFI_IPv6_ADDRESS       *Dest,\r
+  IN EFI_IPv6_ADDRESS       *Src\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IpSecDxe/ComponentName.c b/NetworkPkg/IpSecDxe/ComponentName.c
new file mode 100644 (file)
index 0000000..22b3861
--- /dev/null
@@ -0,0 +1,310 @@
+/** @file\r
+  UEFI Component Name(2) protocol implementation for IPsec driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecImpl.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle,    OPTIONAL\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL    gIpSecComponentName = {\r
+  IpSecComponentNameGetDriverName,\r
+  IpSecComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL     gIpSecComponentName2 = {\r
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IpSecComponentNameGetDriverName,\r
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IpSecComponentNameGetControllerName,\r
+  "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIpSecDriverNameTable[] = {\r
+  {\r
+    "eng;en",\r
+    L"IpSec Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This, and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+{\r
+  return LookupUnicodeString2 (\r
+           Language,\r
+           This->SupportedLanguages,\r
+           mIpSecDriverNameTable,\r
+           DriverName,\r
+           (BOOLEAN) (This == &gIpSecComponentName)\r
+           );\r
+}\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle,        OPTIONAL\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/NetworkPkg/IpSecDxe/IpSecConfigImpl.c b/NetworkPkg/IpSecDxe/IpSecConfigImpl.c
new file mode 100644 (file)
index 0000000..e671e42
--- /dev/null
@@ -0,0 +1,2928 @@
+/** @file\r
+  The implementation of IPSEC_CONFIG_PROTOCOL.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecConfigImpl.h"\r
+#include "IpSecDebug.h"\r
+\r
+LIST_ENTRY                mConfigData[IPsecConfigDataTypeMaximum];\r
+BOOLEAN                   mSetBySelf = FALSE;\r
+\r
+//\r
+// Common CompareSelector routine entry for spd/sad/pad.\r
+//\r
+IPSEC_COMPARE_SELECTOR    mCompareSelector[] = {\r
+  (IPSEC_COMPARE_SELECTOR) CompareSpdSelector,\r
+  (IPSEC_COMPARE_SELECTOR) CompareSaId,\r
+  (IPSEC_COMPARE_SELECTOR) ComparePadId\r
+};\r
+\r
+//\r
+// Common IsZeroSelector routine entry for spd/sad/pad.\r
+//\r
+IPSEC_IS_ZERO_SELECTOR    mIsZeroSelector[] = {\r
+  (IPSEC_IS_ZERO_SELECTOR) IsZeroSpdSelector,\r
+  (IPSEC_IS_ZERO_SELECTOR) IsZeroSaId,\r
+  (IPSEC_IS_ZERO_SELECTOR) IsZeroPadId\r
+};\r
+\r
+//\r
+// Common DuplicateSelector routine entry for spd/sad/pad.\r
+//\r
+IPSEC_DUPLICATE_SELECTOR  mDuplicateSelector[] = {\r
+  (IPSEC_DUPLICATE_SELECTOR) DuplicateSpdSelector,\r
+  (IPSEC_DUPLICATE_SELECTOR) DuplicateSaId,\r
+  (IPSEC_DUPLICATE_SELECTOR) DuplicatePadId\r
+};\r
+\r
+//\r
+// Common FixPolicyEntry routine entry for spd/sad/pad.\r
+//\r
+IPSEC_FIX_POLICY_ENTRY    mFixPolicyEntry[] = {\r
+  (IPSEC_FIX_POLICY_ENTRY) FixSpdEntry,\r
+  (IPSEC_FIX_POLICY_ENTRY) FixSadEntry,\r
+  (IPSEC_FIX_POLICY_ENTRY) FixPadEntry\r
+};\r
+\r
+//\r
+// Common UnfixPolicyEntry routine entry for spd/sad/pad.\r
+//\r
+IPSEC_FIX_POLICY_ENTRY    mUnfixPolicyEntry[] = {\r
+  (IPSEC_FIX_POLICY_ENTRY) UnfixSpdEntry,\r
+  (IPSEC_FIX_POLICY_ENTRY) UnfixSadEntry,\r
+  (IPSEC_FIX_POLICY_ENTRY) UnfixPadEntry\r
+};\r
+\r
+//\r
+// Common SetPolicyEntry routine entry for spd/sad/pad.\r
+//\r
+IPSEC_SET_POLICY_ENTRY    mSetPolicyEntry[] = {\r
+  (IPSEC_SET_POLICY_ENTRY) SetSpdEntry,\r
+  (IPSEC_SET_POLICY_ENTRY) SetSadEntry,\r
+  (IPSEC_SET_POLICY_ENTRY) SetPadEntry\r
+};\r
+\r
+//\r
+// Common GetPolicyEntry routine entry for spd/sad/pad.\r
+//\r
+IPSEC_GET_POLICY_ENTRY    mGetPolicyEntry[] = {\r
+  (IPSEC_GET_POLICY_ENTRY) GetSpdEntry,\r
+  (IPSEC_GET_POLICY_ENTRY) GetSadEntry,\r
+  (IPSEC_GET_POLICY_ENTRY) GetPadEntry\r
+};\r
+\r
+//\r
+// Routine entry for IpSecConfig protocol.\r
+//\r
+EFI_IPSEC_CONFIG_PROTOCOL mIpSecConfigInstance = {\r
+  EfiIpSecConfigSetData,\r
+  EfiIpSecConfigGetData,\r
+  EfiIpSecConfigGetNextSelector,\r
+  EfiIpSecConfigRegisterNotify,\r
+  EfiIpSecConfigUnregisterNotify\r
+};\r
+\r
+/**\r
+  Get the all IPSec configuration variables and store those variables\r
+  to the internal data structure.\r
+\r
+  This founction is called by IpSecConfigInitialize() that is to intialize the\r
+  IPsecConfiguration Protocol.\r
+\r
+  @param[in]  Private            Point to IPSEC_PRIVATE_DATA.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS            Restore the IPsec Configuration successfully.\r
+  @retval  others                Other errors is found during the variable getting.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigRestore (\r
+  IN IPSEC_PRIVATE_DATA               *Private\r
+  );\r
+\r
+/**\r
+  Check if the specified EFI_IP_ADDRESS_INFO is in EFI_IP_ADDRESS_INFO list.\r
+\r
+  @param[in]   AddressInfo         Pointer of IP_ADDRESS_INFO to be search in AddressInfo list.\r
+  @param[in]   AddressInfoList     A list that contains IP_ADDRESS_INFOs.\r
+  @param[in]   AddressCount        Point out how many IP_ADDRESS_INFO in the list.\r
+\r
+  @retval  TRUE    The specified AddressInfo is in the AddressInfoList.\r
+  @retval  FALSE   The specified AddressInfo is not in the AddressInfoList.\r
+\r
+**/\r
+BOOLEAN\r
+IsInAddressInfoList(\r
+  IN EFI_IP_ADDRESS_INFO              *AddressInfo,\r
+  IN EFI_IP_ADDRESS_INFO              *AddressInfoList,\r
+  IN UINT32                           AddressCount\r
+  )\r
+{\r
+  UINT8  Index;\r
+\r
+  for (Index = 0; Index < AddressCount ; Index++) {\r
+    if (CompareMem (\r
+          AddressInfo,\r
+          &AddressInfoList[Index].Address,\r
+          sizeof (EFI_IP_ADDRESS)\r
+          ) == 0 &&\r
+          AddressInfo->PrefixLength == AddressInfoList[Index].PrefixLength\r
+          ) {\r
+       return TRUE;\r
+     }\r
+  }\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Compare two SPD Selectors.\r
+\r
+  Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/\r
+  NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the\r
+  Local Addresses and remote Addresses.\r
+\r
+  @param[in]   Selector1           Pointer of first SPD Selector.\r
+  @param[in]   Selector2           Pointer of second SPD Selector.\r
+\r
+  @retval  TRUE    This two Selector have the same value in above fields.\r
+  @retval  FALSE   Not all above fields have the same value in these two Selectors.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSpdSelector (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2\r
+  )\r
+{\r
+  EFI_IPSEC_SPD_SELECTOR  *SpdSel1;\r
+  EFI_IPSEC_SPD_SELECTOR  *SpdSel2;\r
+  BOOLEAN                 IsMatch;\r
+  UINTN                   Index;\r
+\r
+  SpdSel1 = &Selector1->SpdSelector;\r
+  SpdSel2 = &Selector2->SpdSelector;\r
+  IsMatch = TRUE;\r
+\r
+  //\r
+  // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/\r
+  // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the\r
+  // two Spdselectors. Since the SPD supports two directions, it needs to\r
+  // compare two directions.\r
+  //\r
+  if ((SpdSel1->LocalAddressCount != SpdSel2->LocalAddressCount &&\r
+       SpdSel1->LocalAddressCount != SpdSel2->RemoteAddressCount) ||\r
+      (SpdSel1->RemoteAddressCount != SpdSel2->RemoteAddressCount &&\r
+       SpdSel1->RemoteAddressCount != SpdSel2->LocalAddressCount) ||\r
+       SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol ||\r
+       SpdSel1->LocalPort != SpdSel2->LocalPort ||\r
+       SpdSel1->LocalPortRange != SpdSel2->LocalPortRange ||\r
+       SpdSel1->RemotePort != SpdSel2->RemotePort ||\r
+       SpdSel1->RemotePortRange != SpdSel2->RemotePortRange\r
+       ) {\r
+    IsMatch = FALSE;\r
+    return IsMatch;\r
+  }\r
+\r
+  //\r
+  // Compare the all LocalAddress fields in the two Spdselectors.\r
+  // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare\r
+  // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return\r
+  // TRUE.\r
+  //\r
+  for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {\r
+    if (!IsInAddressInfoList (\r
+          &SpdSel1->LocalAddress[Index],\r
+          SpdSel2->LocalAddress,\r
+          SpdSel2->LocalAddressCount\r
+          )) {\r
+      IsMatch = FALSE;\r
+      break;\r
+    }\r
+  }\r
+  if (IsMatch) {\r
+    for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {\r
+      if (!IsInAddressInfoList (\r
+            &SpdSel2->LocalAddress[Index],\r
+            SpdSel1->LocalAddress,\r
+            SpdSel1->LocalAddressCount\r
+            )) {\r
+        IsMatch = FALSE;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+  if (IsMatch) {\r
+    for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {\r
+      if (!IsInAddressInfoList (\r
+            &SpdSel1->RemoteAddress[Index],\r
+            SpdSel2->RemoteAddress,\r
+            SpdSel2->RemoteAddressCount\r
+            )) {\r
+        IsMatch = FALSE;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+  if (IsMatch) {\r
+    for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {\r
+      if (!IsInAddressInfoList (\r
+            &SpdSel2->RemoteAddress[Index],\r
+            SpdSel1->RemoteAddress,\r
+            SpdSel1->RemoteAddressCount\r
+            )) {\r
+        IsMatch = FALSE;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // Finish the one direction compare. If it is matched, return; otherwise,\r
+  // compare the other direction.\r
+  //\r
+  if (IsMatch) {\r
+    return IsMatch;\r
+  }\r
+  //\r
+  // Secondly, the SpdSel1->LocalAddress doesn't equal to  SpdSel2->LocalAddress and\r
+  // SpdSel1->RemoteAddress doesn't equal to SpdSel2->RemoteAddress. Try to compare\r
+  // the RemoteAddress to LocalAddress.\r
+  //\r
+  IsMatch = TRUE;\r
+  for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {\r
+    if (!IsInAddressInfoList (\r
+          &SpdSel1->RemoteAddress[Index],\r
+          SpdSel2->LocalAddress,\r
+          SpdSel2->LocalAddressCount\r
+          )) {\r
+      IsMatch = FALSE;\r
+      break;\r
+    }\r
+  }\r
+  if (IsMatch) {\r
+    for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {\r
+      if (!IsInAddressInfoList (\r
+            &SpdSel2->RemoteAddress[Index],\r
+            SpdSel1->LocalAddress,\r
+            SpdSel1->LocalAddressCount\r
+            )) {\r
+        IsMatch = FALSE;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+  if (IsMatch) {\r
+    for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {\r
+      if (!IsInAddressInfoList (\r
+            &SpdSel1->LocalAddress[Index],\r
+            SpdSel2->RemoteAddress,\r
+            SpdSel2->RemoteAddressCount\r
+            )) {\r
+        IsMatch = FALSE;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+  if (IsMatch) {\r
+    for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {\r
+      if (!IsInAddressInfoList (\r
+            &SpdSel2->LocalAddress[Index],\r
+            SpdSel1->RemoteAddress,\r
+            SpdSel1->RemoteAddressCount\r
+            )) {\r
+        IsMatch = FALSE;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+  return IsMatch;\r
+}\r
+\r
+/**\r
+  Compare two SA IDs.\r
+\r
+  @param[in]   Selector1           Pointer of first SA ID.\r
+  @param[in]   Selector2           Pointer of second SA ID.\r
+\r
+  @retval  TRUE    This two Selectors have the same SA ID.\r
+  @retval  FALSE   This two Selecotrs don't have the same SA ID.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSaId (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2\r
+  )\r
+{\r
+  EFI_IPSEC_SA_ID *SaId1;\r
+  EFI_IPSEC_SA_ID *SaId2;\r
+  BOOLEAN         IsMatch;\r
+\r
+  SaId1   = &Selector1->SaId;\r
+  SaId2   = &Selector2->SaId;\r
+  IsMatch = TRUE;\r
+\r
+  if (CompareMem (SaId1, SaId2, sizeof (EFI_IPSEC_SA_ID)) != 0) {\r
+    IsMatch = FALSE;\r
+  }\r
+\r
+  return IsMatch;\r
+}\r
+\r
+/**\r
+  Compare two PAD IDs.\r
+\r
+  @param[in]   Selector1           Pointer of first PAD ID.\r
+  @param[in]   Selector2           Pointer of second PAD ID.\r
+\r
+  @retval  TRUE    This two Selectors have the same PAD ID.\r
+  @retval  FALSE   This two Selecotrs don't have the same PAD ID.\r
+\r
+**/\r
+BOOLEAN\r
+ComparePadId (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2\r
+  )\r
+{\r
+  EFI_IPSEC_PAD_ID  *PadId1;\r
+  EFI_IPSEC_PAD_ID  *PadId2;\r
+  BOOLEAN           IsMatch;\r
+\r
+  PadId1  = &Selector1->PadId;\r
+  PadId2  = &Selector2->PadId;\r
+  IsMatch = TRUE;\r
+\r
+  //\r
+  // Compare the PeerIdValid fields in PadId.\r
+  //\r
+  if (PadId1->PeerIdValid != PadId2->PeerIdValid) {\r
+    IsMatch = FALSE;\r
+  }\r
+  //\r
+  // Compare the PeerId fields in PadId if PeerIdValid is true.\r
+  //\r
+  if (IsMatch &&\r
+      PadId1->PeerIdValid &&\r
+      AsciiStriCmp ((CONST CHAR8 *) PadId1->Id.PeerId, (CONST CHAR8 *) PadId2->Id.PeerId) != 0\r
+      ) {\r
+    IsMatch = FALSE;\r
+  }\r
+  //\r
+  // Compare the IpAddress fields in PadId if PeerIdValid is false.\r
+  //\r
+  if (IsMatch &&\r
+      !PadId1->PeerIdValid &&\r
+      (PadId1->Id.IpAddress.PrefixLength != PadId2->Id.IpAddress.PrefixLength ||\r
+       CompareMem (&PadId1->Id.IpAddress.Address, &PadId2->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0)\r
+      ) {\r
+    IsMatch = FALSE;\r
+  }\r
+\r
+  return IsMatch;\r
+}\r
+\r
+/**\r
+  Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount\r
+  fields.\r
+\r
+  @param[in]  Selector      Pointer of the SPD Selector.\r
+\r
+  @retval     TRUE          If the SPD Selector is Zero.\r
+  @retval     FALSE         If the SPD Selector is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroSpdSelector (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector\r
+  )\r
+{\r
+  EFI_IPSEC_SPD_SELECTOR  *SpdSel;\r
+  BOOLEAN                 IsZero;\r
+\r
+  SpdSel  = &Selector->SpdSelector;\r
+  IsZero  = FALSE;\r
+\r
+  if (SpdSel->LocalAddressCount == 0 && SpdSel->RemoteAddressCount == 0) {\r
+    IsZero = TRUE;\r
+  }\r
+\r
+  return IsZero;\r
+}\r
+\r
+/**\r
+  Check if the SA ID is Zero by its DestAddress.\r
+\r
+  @param[in]  Selector      Pointer of the SA ID.\r
+\r
+  @retval     TRUE          If the SA ID is Zero.\r
+  @retval     FALSE         If the SA ID is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroSaId (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector\r
+  )\r
+{\r
+  EFI_IP_ADDRESS  *DestAddr;\r
+  EFI_IP_ADDRESS  ZeroAddr;\r
+  BOOLEAN         IsZero;\r
+\r
+  DestAddr  = &Selector->SaId.DestAddress;\r
+  IsZero    = FALSE;\r
+\r
+  ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));\r
+\r
+  if (CompareMem (DestAddr, &ZeroAddr, sizeof (EFI_IP_ADDRESS)) == 0) {\r
+    IsZero = TRUE;\r
+  }\r
+\r
+  return IsZero;\r
+}\r
+\r
+/**\r
+  Check if the PAD ID is Zero.\r
+\r
+  @param[in]  Selector      Pointer of the PAD ID.\r
+\r
+  @retval     TRUE          If the PAD ID is Zero.\r
+  @retval     FALSE         If the PAD ID is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroPadId (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector\r
+  )\r
+{\r
+  EFI_IPSEC_PAD_ID  *PadId;\r
+  EFI_IPSEC_PAD_ID  ZeroId;\r
+  BOOLEAN           IsZero;\r
+\r
+  PadId   = &Selector->PadId;\r
+  IsZero  = FALSE;\r
+\r
+  ZeroMem (&ZeroId, sizeof (EFI_IPSEC_PAD_ID));\r
+\r
+  if (CompareMem (PadId, &ZeroId, sizeof (EFI_IPSEC_PAD_ID)) == 0) {\r
+    IsZero = TRUE;\r
+  }\r
+\r
+  return IsZero;\r
+}\r
+\r
+/**\r
+  Copy Source SPD Selector to the Destination SPD Selector.\r
+\r
+  @param[in, out] DstSel             Pointer of Destination SPD Selector.\r
+  @param[in]      SrcSel             Pointer of Source SPD Selector.\r
+  @param[in, out] Size               The size of the Destination SPD Selector. If it\r
+                                     not NULL and its value less than the size of\r
+                                     Source SPD Selector, the value of Source SPD\r
+                                     Selector's size will be passed to caller by this\r
+                                     parameter.\r
+\r
+  @retval EFI_INVALID_PARAMETER  If the Destination or Source SPD Selector is NULL\r
+  @retval EFI_BUFFER_TOO_SMALL   If the input Size is less than size of the Source SPD Selector.\r
+  @retval EFI_SUCCESS            Copy Source SPD Selector to the Destination SPD\r
+                                 Selector successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicateSpdSelector (\r
+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,\r
+  IN OUT UINTN                        *Size\r
+  )\r
+{\r
+  EFI_IPSEC_SPD_SELECTOR  *Dst;\r
+  EFI_IPSEC_SPD_SELECTOR  *Src;\r
+\r
+  Dst = &DstSel->SpdSelector;\r
+  Src = &SrcSel->SpdSelector;\r
+\r
+  if (Dst == NULL || Src == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Size != NULL && (*Size) < SIZE_OF_SPD_SELECTOR (Src)) {\r
+    *Size = SIZE_OF_SPD_SELECTOR (Src);\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+  //\r
+  // Copy the base structure of spd selector.\r
+  //\r
+  CopyMem (Dst, Src, sizeof (EFI_IPSEC_SPD_SELECTOR));\r
+\r
+  //\r
+  // Copy the local address array of spd selector.\r
+  //\r
+  Dst->LocalAddress = (EFI_IP_ADDRESS_INFO *) (Dst + 1);\r
+  CopyMem (\r
+    Dst->LocalAddress,\r
+    Src->LocalAddress,\r
+    sizeof (EFI_IP_ADDRESS_INFO) * Dst->LocalAddressCount\r
+    );\r
+\r
+  //\r
+  // Copy the remote address array of spd selector.\r
+  //\r
+  Dst->RemoteAddress = Dst->LocalAddress + Dst->LocalAddressCount;\r
+  CopyMem (\r
+    Dst->RemoteAddress,\r
+    Src->RemoteAddress,\r
+    sizeof (EFI_IP_ADDRESS_INFO) * Dst->RemoteAddressCount\r
+    );\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Copy Source SA ID to the Destination SA ID.\r
+\r
+  @param[in, out] DstSel             Pointer of Destination SA ID.\r
+  @param[in]      SrcSel             Pointer of Source SA ID.\r
+  @param[in, out] Size               The size of the Destination SA ID. If it\r
+                                     not NULL and its value less than the size of\r
+                                     Source SA ID, the value of Source SA ID's size\r
+                                     will be passed to caller by this parameter.\r
+\r
+  @retval EFI_INVALID_PARAMETER  If the Destination or Source SA ID is NULL.\r
+  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source SA ID.\r
+  @retval EFI_SUCCESS            Copy Source SA ID  to the Destination SA ID successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicateSaId (\r
+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,\r
+  IN OUT UINTN                        *Size\r
+  )\r
+{\r
+  EFI_IPSEC_SA_ID *Dst;\r
+  EFI_IPSEC_SA_ID *Src;\r
+\r
+  Dst = &DstSel->SaId;\r
+  Src = &SrcSel->SaId;\r
+\r
+  if (Dst == NULL || Src == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Size != NULL && *Size < sizeof (EFI_IPSEC_SA_ID)) {\r
+    *Size = sizeof (EFI_IPSEC_SA_ID);\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  CopyMem (Dst, Src, sizeof (EFI_IPSEC_SA_ID));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Copy Source PAD ID to the Destination PAD ID.\r
+\r
+  @param[in, out] DstSel             Pointer of Destination PAD ID.\r
+  @param[in]      SrcSel             Pointer of Source PAD ID.\r
+  @param[in, out] Size               The size of the Destination PAD ID. If it\r
+                                     not NULL and its value less than the size of\r
+                                     Source PAD ID, the value of Source PAD ID's size\r
+                                     will be passed to caller by this parameter.\r
+\r
+  @retval EFI_INVALID_PARAMETER  If the Destination or Source PAD ID is NULL.\r
+  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source PAD ID .\r
+  @retval EFI_SUCCESS            Copy Source PAD ID  to the Destination PAD ID successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicatePadId (\r
+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,\r
+  IN OUT UINTN                        *Size\r
+  )\r
+{\r
+  EFI_IPSEC_PAD_ID  *Dst;\r
+  EFI_IPSEC_PAD_ID  *Src;\r
+\r
+  Dst = &DstSel->PadId;\r
+  Src = &SrcSel->PadId;\r
+\r
+  if (Dst == NULL || Src == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Size != NULL && *Size < sizeof (EFI_IPSEC_PAD_ID)) {\r
+    *Size = sizeof (EFI_IPSEC_PAD_ID);\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  CopyMem (Dst, Src, sizeof (EFI_IPSEC_PAD_ID));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Fix the value of some members of SPD Selector.\r
+\r
+  This function is called by IpSecCopyPolicyEntry()which copy the Policy\r
+  Entry into the Variable. Since some members in SPD Selector are pointers,\r
+  a physical address to relative address convertion is required before copying\r
+  this SPD entry into the variable.\r
+\r
+  @param[in]       Selector              Pointer of SPD Selector.\r
+  @param[in, out]  Data                  Pointer of SPD Data.\r
+\r
+**/\r
+VOID\r
+FixSpdEntry (\r
+  IN     EFI_IPSEC_SPD_SELECTOR            *Selector,\r
+  IN OUT EFI_IPSEC_SPD_DATA                *Data\r
+  )\r
+{\r
+  //\r
+  // It assumes that all ref buffers in spd selector and data are\r
+  // stored in the continous memory and close to the base structure.\r
+  //\r
+  FIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);\r
+  FIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);\r
+\r
+  if (Data->ProcessingPolicy != NULL) {\r
+    if (Data->ProcessingPolicy->TunnelOption != NULL) {\r
+      FIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);\r
+    }\r
+\r
+    FIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Fix the value of some members of SA ID.\r
+\r
+  This function is called by IpSecCopyPolicyEntry()which copy the Policy\r
+  Entry into the Variable. Since some members in SA ID are pointers,\r
+  a physical address to relative address conversion is required before copying\r
+  this SAD into the variable.\r
+\r
+  @param[in]       SaId                  Pointer of SA ID\r
+  @param[in, out]  Data                  Pointer of SA Data.\r
+\r
+**/\r
+VOID\r
+FixSadEntry (\r
+  IN     EFI_IPSEC_SA_ID                  *SaId,\r
+  IN OUT EFI_IPSEC_SA_DATA                *Data\r
+  )\r
+{\r
+  //\r
+  // It assumes that all ref buffers in sad selector and data are\r
+  // stored in the continous memory and close to the base structure.\r
+  //\r
+  if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+    FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);\r
+  }\r
+\r
+  if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+    FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);\r
+  }\r
+\r
+  if (Data->SpdSelector != NULL) {\r
+    if (Data->SpdSelector->LocalAddress != NULL) {\r
+      FIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);\r
+    }\r
+\r
+    FIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);\r
+    FIX_REF_BUF_ADDR (Data->SpdSelector, Data);\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Fix the value of some members of PAD ID.\r
+\r
+  This function is called by IpSecCopyPolicyEntry()which copy the Policy\r
+  Entry into the Variable. Since some members in PAD ID are pointers,\r
+  a physical address to relative address conversion is required before copying\r
+  this PAD into the variable.\r
+\r
+  @param[in]       PadId              Pointer of PAD ID.\r
+  @param[in, out]  Data               Pointer of PAD Data.\r
+\r
+**/\r
+VOID\r
+FixPadEntry (\r
+  IN     EFI_IPSEC_PAD_ID                  *PadId,\r
+  IN OUT EFI_IPSEC_PAD_DATA                *Data\r
+  )\r
+{\r
+  //\r
+  // It assumes that all ref buffers in pad selector and data are\r
+  // stored in the continous memory and close to the base structure.\r
+  //\r
+  if (Data->AuthData != NULL) {\r
+    FIX_REF_BUF_ADDR (Data->AuthData, Data);\r
+  }\r
+\r
+  if (Data->RevocationData != NULL) {\r
+    FIX_REF_BUF_ADDR (Data->RevocationData, Data);\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Recover the value of some members of SPD Selector.\r
+\r
+  This function is corresponding to FixSpdEntry(). It recovers the value of members\r
+  of SPD Selector that are fixed by FixSpdEntry().\r
+\r
+  @param[in, out]  Selector              Pointer of SPD Selector.\r
+  @param[in, out]  Data                  Pointer of SPD Data.\r
+\r
+**/\r
+VOID\r
+UnfixSpdEntry (\r
+  IN OUT EFI_IPSEC_SPD_SELECTOR           *Selector,\r
+  IN OUT EFI_IPSEC_SPD_DATA               *Data\r
+  )\r
+{\r
+  //\r
+  // It assumes that all ref buffers in spd selector and data are\r
+  // stored in the continous memory and close to the base structure.\r
+  //\r
+  UNFIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);\r
+  UNFIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);\r
+\r
+  if (Data->ProcessingPolicy != NULL) {\r
+    UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);\r
+    if (Data->ProcessingPolicy->TunnelOption != NULL) {\r
+      UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);\r
+    }\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Recover the value of some members of SA ID.\r
+\r
+  This function is corresponding to FixSadEntry(). It recovers the value of members\r
+  of SAD ID that are fixed by FixSadEntry().\r
+\r
+  @param[in, out]  SaId              Pointer of SAD ID.\r
+  @param[in, out]  Data              Pointer of SAD Data.\r
+\r
+**/\r
+VOID\r
+UnfixSadEntry (\r
+  IN OUT EFI_IPSEC_SA_ID                     *SaId,\r
+  IN OUT EFI_IPSEC_SA_DATA                   *Data\r
+  )\r
+{\r
+  //\r
+  // It assumes that all ref buffers in sad selector and data are\r
+  // stored in the continous memory and close to the base structure.\r
+  //\r
+  if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+    UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);\r
+  }\r
+\r
+  if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+    UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);\r
+  }\r
+\r
+  if (Data->SpdSelector != NULL) {\r
+    UNFIX_REF_BUF_ADDR (Data->SpdSelector, Data);\r
+    if (Data->SpdSelector->LocalAddress != NULL) {\r
+      UNFIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);\r
+    }\r
+\r
+    UNFIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Recover the value of some members of PAD ID.\r
+\r
+  This function is corresponding to FixPadEntry(). It recovers the value of members\r
+  of PAD ID that are fixed by FixPadEntry().\r
+\r
+  @param[in]       PadId              Pointer of PAD ID.\r
+  @param[in, out]  Data               Pointer of PAD Data.\r
+\r
+**/\r
+VOID\r
+UnfixPadEntry (\r
+  IN     EFI_IPSEC_PAD_ID                 *PadId,\r
+  IN OUT EFI_IPSEC_PAD_DATA               *Data\r
+  )\r
+{\r
+  //\r
+  // It assumes that all ref buffers in pad selector and data are\r
+  // stored in the continous memory and close to the base structure.\r
+  //\r
+  if (Data->AuthData != NULL) {\r
+    UNFIX_REF_BUF_ADDR (Data->AuthData, Data);\r
+  }\r
+\r
+  if (Data->RevocationData != NULL) {\r
+    UNFIX_REF_BUF_ADDR (Data->RevocationData, Data);\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Set the security policy information for the EFI IPsec driver.\r
+\r
+  The IPsec configuration data has a unique selector/identifier separately to\r
+  identify a data entry.\r
+\r
+  @param[in]  Selector           Pointer to an entry selector on operated\r
+                                 configuration data specified by DataType.\r
+                                 A NULL Selector causes the entire specified-type\r
+                                 configuration information to be flushed.\r
+  @param[in]  Data               The data buffer to be set. The structure\r
+                                 of the data buffer should be EFI_IPSEC_SPD_DATA.\r
+  @param[in]  Context            Pointer to one entry selector that describes\r
+                                 the expected position the new data entry will\r
+                                 be added. If Context is NULL, the new entry will\r
+                                 be appended the end of database.\r
+\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                   - Selector is not NULL and its LocalAddress\r
+                                     is NULL or its RemoteAddress is NULL.\r
+                                   - Data is not NULL and its Action is Protected\r
+                                     and its plolicy is NULL.\r
+                                   - Data is not NULL, its Action is not protected,\r
+                                     and its policy is not NULL.\r
+                                   - The Action of Data is Protected, its policy\r
+                                     mode is Tunnel, and its tunnel option is NULL.\r
+                                   - The Action of Data is protected and its policy\r
+                                     mode is not Tunnel and it tunnel option is not NULL.\r
+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetSpdEntry (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,\r
+  IN VOID                            *Data,\r
+  IN VOID                            *Context OPTIONAL\r
+  )\r
+{\r
+  EFI_IPSEC_SPD_SELECTOR  *SpdSel;\r
+  EFI_IPSEC_SPD_DATA      *SpdData;\r
+  EFI_IPSEC_SPD_SELECTOR  *InsertBefore;\r
+  LIST_ENTRY              *SpdList;\r
+  LIST_ENTRY              *SadList;\r
+  LIST_ENTRY              *SpdSas;\r
+  LIST_ENTRY              *EntryInsertBefore;\r
+  LIST_ENTRY              *Entry;\r
+  LIST_ENTRY              *NextEntry;\r
+  LIST_ENTRY              *Entry2;\r
+  IPSEC_SPD_ENTRY         *SpdEntry;\r
+  IPSEC_SAD_ENTRY         *SadEntry;\r
+  UINTN                   SpdEntrySize;\r
+  UINTN                   Index;\r
+\r
+  SpdSel        = (Selector == NULL) ? NULL : &Selector->SpdSelector;\r
+  SpdData       = (Data == NULL) ? NULL : (EFI_IPSEC_SPD_DATA *) Data;\r
+  InsertBefore  = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SpdSelector;\r
+  SpdList       = &mConfigData[IPsecConfigDataTypeSpd];\r
+\r
+  if (SpdSel != NULL) {\r
+    if (SpdSel->LocalAddress == NULL || SpdSel->RemoteAddress == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  if (SpdData != NULL) {\r
+    if ((SpdData->Action == EfiIPsecActionProtect && SpdData->ProcessingPolicy == NULL) ||\r
+        (SpdData->Action != EfiIPsecActionProtect && SpdData->ProcessingPolicy != NULL)\r
+        ) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (SpdData->Action == EfiIPsecActionProtect) {\r
+      if ((SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption == NULL) ||\r
+          (SpdData->ProcessingPolicy->Mode != EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption != NULL)\r
+          ) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // The default behavior is to insert the node ahead of the header.\r
+  //\r
+  EntryInsertBefore = SpdList;\r
+\r
+  //\r
+  // Remove the existed spd entry.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SpdList) {\r
+\r
+    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
+\r
+    if (SpdSel == NULL ||\r
+        CompareSpdSelector ((EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel)\r
+        ) {\r
+      //\r
+      // Record the existed entry position to keep the original order.\r
+      //\r
+      EntryInsertBefore = SpdEntry->List.ForwardLink;\r
+      RemoveEntryList (&SpdEntry->List);\r
+\r
+      //\r
+      // Update the reverse ref of sad entry in the spd.sas list.\r
+      //\r
+      SpdSas = &SpdEntry->Data->Sas;\r
+      NET_LIST_FOR_EACH (Entry2, SpdSas) {\r
+        SadEntry                  = IPSEC_SAD_ENTRY_FROM_SPD (Entry2);\r
+        SadEntry->Data->SpdEntry  = NULL;\r
+      }\r
+      //\r
+      // Free the existed spd entry\r
+      //\r
+      FreePool (SpdEntry);\r
+    }\r
+  }\r
+  //\r
+  // Return success here if only want to remove the spd entry.\r
+  //\r
+  if (SpdData == NULL || SpdSel == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Search the appointed entry position if InsertBefore is not NULL.\r
+  //\r
+  if (InsertBefore != NULL) {\r
+\r
+    NET_LIST_FOR_EACH (Entry, SpdList) {\r
+      SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
+\r
+      if (CompareSpdSelector (\r
+            (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,\r
+            (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore\r
+            )) {\r
+        EntryInsertBefore = Entry;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Do Padding for the different Arch.\r
+  //\r
+  SpdEntrySize  = ALIGN_VARIABLE (sizeof (IPSEC_SPD_ENTRY));\r
+  SpdEntrySize  = ALIGN_VARIABLE (SpdEntrySize + (UINTN)SIZE_OF_SPD_SELECTOR (SpdSel));\r
+  SpdEntrySize += IpSecGetSizeOfEfiSpdData (SpdData);\r
+\r
+  SpdEntry = AllocateZeroPool (SpdEntrySize);\r
+\r
+  if (SpdEntry == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Fix the address of Selector and Data buffer and copy them, which is\r
+  // continous memory and close to the base structure of spd entry.\r
+  //\r
+  SpdEntry->Selector  = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER ((SpdEntry + 1), sizeof (UINTN));\r
+  SpdEntry->Data      = (IPSEC_SPD_DATA *) ALIGN_POINTER (\r
+                                            ((UINT8 *) SpdEntry->Selector + SIZE_OF_SPD_SELECTOR (SpdSel)),\r
+                                            sizeof (UINTN)\r
+                                            );\r
+\r
+  DuplicateSpdSelector (\r
+    (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,\r
+    (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,\r
+    NULL\r
+    );\r
+\r
+  CopyMem (\r
+    SpdEntry->Data->Name,\r
+    SpdData->Name,\r
+    sizeof (SpdData->Name)\r
+    );\r
+  SpdEntry->Data->PackageFlag = SpdData->PackageFlag;\r
+  SpdEntry->Data->Action      = SpdData->Action;\r
+\r
+  //\r
+  // Fix the address of ProcessingPolicy and copy it if need, which is continous\r
+  // memory and close to the base structure of sad data.\r
+  //\r
+  if (SpdData->Action != EfiIPsecActionProtect) {\r
+    SpdEntry->Data->ProcessingPolicy = NULL;\r
+  } else {\r
+    SpdEntry->Data->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (\r
+                                                                      SpdEntry->Data + 1,\r
+                                                                      sizeof (UINTN)\r
+                                                                      );\r
+    IpSecDuplicateProcessPolicy (SpdEntry->Data->ProcessingPolicy, SpdData->ProcessingPolicy);\r
+  }\r
+  //\r
+  // Update the sas list of the new spd entry.\r
+  //\r
+  InitializeListHead (&SpdEntry->Data->Sas);\r
+\r
+  SadList = &mConfigData[IPsecConfigDataTypeSad];\r
+\r
+  NET_LIST_FOR_EACH (Entry, SadList) {\r
+    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+\r
+    for (Index = 0; Index < SpdData->SaIdCount; Index++) {\r
+\r
+      if (CompareSaId (\r
+            (EFI_IPSEC_CONFIG_SELECTOR *) &SpdData->SaId[Index],\r
+            (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id\r
+            )) {\r
+        InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);\r
+        SadEntry->Data->SpdEntry = SpdEntry;\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // Insert the new spd entry.\r
+  //\r
+  InsertTailList (EntryInsertBefore, &SpdEntry->List);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Set the security association information for the EFI IPsec driver.\r
+\r
+  The IPsec configuration data has a unique selector/identifier separately to\r
+  identify a data entry.\r
+\r
+  @param[in]  Selector           Pointer to an entry selector on operated\r
+                                 configuration data specified by DataType.\r
+                                 A NULL Selector causes the entire specified-type\r
+                                 configuration information to be flushed.\r
+  @param[in]  Data               The data buffer to be set. The structure\r
+                                 of the data buffer should be EFI_IPSEC_SA_DATA.\r
+  @param[in]  Context            Pointer to one entry selector which describes\r
+                                 the expected position the new data entry will\r
+                                 be added. If Context is NULL,the new entry will\r
+                                 be appended the end of database.\r
+\r
+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetSadEntry (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,\r
+  IN VOID                            *Data,\r
+  IN VOID                            *Context OPTIONAL\r
+  )\r
+{\r
+  IPSEC_SAD_ENTRY   *SadEntry;\r
+  IPSEC_SPD_ENTRY   *SpdEntry;\r
+  LIST_ENTRY        *Entry;\r
+  LIST_ENTRY        *NextEntry;\r
+  LIST_ENTRY        *SadList;\r
+  LIST_ENTRY        *SpdList;\r
+  EFI_IPSEC_SA_ID   *SaId;\r
+  EFI_IPSEC_SA_DATA *SaData;\r
+  EFI_IPSEC_SA_ID   *InsertBefore;\r
+  LIST_ENTRY        *EntryInsertBefore;\r
+  UINTN             SadEntrySize;\r
+\r
+  SaId          = (Selector == NULL) ? NULL : &Selector->SaId;\r
+  SaData        = (Data == NULL) ? NULL : (EFI_IPSEC_SA_DATA *) Data;\r
+  InsertBefore  = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SaId;\r
+  SadList       = &mConfigData[IPsecConfigDataTypeSad];\r
+\r
+  //\r
+  // The default behavior is to insert the node ahead of the header.\r
+  //\r
+  EntryInsertBefore = SadList;\r
+\r
+  //\r
+  // Remove the existed sad entry.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SadList) {\r
+\r
+    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+\r
+    if (SaId == NULL ||\r
+        CompareSaId (\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) SaId\r
+          )) {\r
+      //\r
+      // Record the existed entry position to keep the original order.\r
+      //\r
+      EntryInsertBefore = SadEntry->List.ForwardLink;\r
+\r
+      //\r
+      // Update the related sad.byspd field.\r
+      //\r
+      if (SadEntry->Data->SpdEntry != NULL) {\r
+        RemoveEntryList (&SadEntry->BySpd);\r
+      }\r
+\r
+      RemoveEntryList (&SadEntry->List);\r
+      FreePool (SadEntry);\r
+    }\r
+  }\r
+  //\r
+  // Return success here if only want to remove the sad entry\r
+  //\r
+  if (SaData == NULL || SaId == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Search the appointed entry position if InsertBefore is not NULL.\r
+  //\r
+  if (InsertBefore != NULL) {\r
+\r
+    NET_LIST_FOR_EACH (Entry, SadList) {\r
+      SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+\r
+      if (CompareSaId (\r
+           (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,\r
+           (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore\r
+           )) {\r
+        EntryInsertBefore = Entry;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Do Padding for different Arch.\r
+  //\r
+  SadEntrySize  = ALIGN_VARIABLE (sizeof (IPSEC_SAD_ENTRY));\r
+  SadEntrySize  = ALIGN_VARIABLE (SadEntrySize + sizeof (EFI_IPSEC_SA_DATA));\r
+  SadEntrySize  = ALIGN_VARIABLE (SadEntrySize + sizeof (IPSEC_SAD_DATA));\r
+\r
+  if (SaId->Proto == EfiIPsecAH) {\r
+    SadEntrySize += SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength;\r
+  } else {\r
+    SadEntrySize  = ALIGN_VARIABLE (SadEntrySize + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength);\r
+    SadEntrySize += SaData->AlgoInfo.EspAlgoInfo.EncKeyLength;\r
+  }\r
+\r
+  SadEntry      = AllocateZeroPool (SadEntrySize);\r
+\r
+  if (SadEntry == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Fix the address of Id and Data buffer and copy them, which is\r
+  // continous memory and close to the base structure of sad entry.\r
+  //\r
+  SadEntry->Id    = (EFI_IPSEC_SA_ID *) ALIGN_POINTER ((SadEntry + 1), sizeof (UINTN));\r
+  SadEntry->Data  = (IPSEC_SAD_DATA *) ALIGN_POINTER ((SadEntry->Id + 1), sizeof (UINTN));\r
+\r
+  CopyMem (SadEntry->Id, SaId, sizeof (EFI_IPSEC_SA_ID));\r
+\r
+  SadEntry->Data->Mode                  = SaData->Mode;\r
+  SadEntry->Data->SequenceNumber        = SaData->SNCount;\r
+  SadEntry->Data->AntiReplayWindowSize  = SaData->AntiReplayWindows;\r
+\r
+  ZeroMem (\r
+    &SadEntry->Data->AntiReplayBitmap,\r
+    sizeof (SadEntry->Data->AntiReplayBitmap)\r
+    );\r
+\r
+  ZeroMem (\r
+    &SadEntry->Data->AlgoInfo,\r
+    sizeof (EFI_IPSEC_ALGO_INFO)\r
+    );\r
+\r
+  SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId     = SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId;\r
+  SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength  = SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength;\r
+\r
+  if (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {\r
+    SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SadEntry->Data + 1), sizeof (UINTN));\r
+    CopyMem (\r
+      SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+      SaData->AlgoInfo.EspAlgoInfo.AuthKey,\r
+      SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength\r
+      );\r
+  }\r
+\r
+  if (SaId->Proto == EfiIPsecESP) {\r
+    SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId    = SaData->AlgoInfo.EspAlgoInfo.EncAlgoId;\r
+    SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength = SaData->AlgoInfo.EspAlgoInfo.EncKeyLength;\r
+\r
+    if (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {\r
+      SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (\r
+                                                               ((UINT8 *) (SadEntry->Data + 1) +\r
+                                                                 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength),\r
+                                                                 sizeof (UINTN)\r
+                                                                 );\r
+      CopyMem (\r
+        SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,\r
+        SaData->AlgoInfo.EspAlgoInfo.EncKey,\r
+        SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength\r
+        );\r
+    }\r
+  }\r
+\r
+  CopyMem (\r
+    &SadEntry->Data->SaLifetime,\r
+    &SaData->SaLifetime,\r
+    sizeof (EFI_IPSEC_SA_LIFETIME)\r
+    );\r
+\r
+  SadEntry->Data->PathMTU     = SaData->PathMTU;\r
+  SadEntry->Data->SpdEntry    = NULL;\r
+  SadEntry->Data->ESNEnabled  = FALSE;\r
+  SadEntry->Data->ManualSet   = SaData->ManualSet;\r
+\r
+  //\r
+  // Update the spd.sas list of the spd entry specified by sad.selector\r
+  //\r
+  SpdList = &mConfigData[IPsecConfigDataTypeSpd];\r
+\r
+  for (Entry = SpdList->ForwardLink; Entry != SpdList && SaData->SpdSelector != NULL; Entry = Entry->ForwardLink) {\r
+\r
+    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
+    if (CompareSpdSelector (\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector\r
+          ) && SpdEntry->Data->Action == EfiIPsecActionProtect) {\r
+      SadEntry->Data->SpdEntry = SpdEntry;\r
+      InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);\r
+    }\r
+  }\r
+  //\r
+  // Insert the new sad entry.\r
+  //\r
+  InsertTailList (EntryInsertBefore, &SadEntry->List);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Set the peer authorization configuration information for the EFI IPsec driver.\r
+\r
+  The IPsec configuration data has a unique selector/identifier separately to\r
+  identify a data entry.\r
+\r
+  @param[in]  Selector           Pointer to an entry selector on operated\r
+                                 configuration data specified by DataType.\r
+                                 A NULL Selector causes the entire specified-type\r
+                                 configuration information to be flushed.\r
+  @param[in]  Data               The data buffer to be set. The structure\r
+                                 of the data buffer should be EFI_IPSEC_PAD_DATA.\r
+  @param[in]  Context            Pointer to one entry selector that describes\r
+                                 the expected position the new data entry will\r
+                                 be added. If Context is NULL, the new entry will\r
+                                 be appended the end of database.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  The required system resources could not be allocated.\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetPadEntry (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,\r
+  IN VOID                            *Data,\r
+  IN VOID                            *Context OPTIONAL\r
+  )\r
+{\r
+  IPSEC_PAD_ENTRY     *PadEntry;\r
+  EFI_IPSEC_PAD_ID    *PadId;\r
+  EFI_IPSEC_PAD_DATA  *PadData;\r
+  LIST_ENTRY          *PadList;\r
+  LIST_ENTRY          *Entry;\r
+  LIST_ENTRY          *NextEntry;\r
+  EFI_IPSEC_PAD_ID    *InsertBefore;\r
+  LIST_ENTRY          *EntryInsertBefore;\r
+  UINTN               PadEntrySize;\r
+\r
+  PadId         = (Selector == NULL) ? NULL : &Selector->PadId;\r
+  PadData       = (Data == NULL) ? NULL : (EFI_IPSEC_PAD_DATA *) Data;\r
+  InsertBefore  = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->PadId;\r
+  PadList       = &mConfigData[IPsecConfigDataTypePad];\r
+\r
+  //\r
+  // The default behavior is to insert the node ahead of the header.\r
+  //\r
+  EntryInsertBefore = PadList;\r
+\r
+  //\r
+  // Remove the existed pad entry.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, PadList) {\r
+\r
+    PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
+\r
+    if (PadId == NULL ||\r
+        ComparePadId ((EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id, (EFI_IPSEC_CONFIG_SELECTOR *) PadId)\r
+        ) {\r
+      //\r
+      // Record the existed entry position to keep the original order.\r
+      //\r
+      EntryInsertBefore = PadEntry->List.ForwardLink;\r
+      RemoveEntryList (&PadEntry->List);\r
+\r
+      FreePool (PadEntry);\r
+    }\r
+  }\r
+  //\r
+  // Return success here if only want to remove the pad entry\r
+  //\r
+  if (PadData == NULL || PadId == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Search the appointed entry position if InsertBefore is not NULL.\r
+  //\r
+  if (InsertBefore != NULL) {\r
+\r
+    NET_LIST_FOR_EACH (Entry, PadList) {\r
+      PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
+\r
+      if (ComparePadId (\r
+            (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id,\r
+            (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore\r
+            )) {\r
+        EntryInsertBefore = Entry;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Do PADDING for different arch.\r
+  //\r
+  PadEntrySize  = ALIGN_VARIABLE (sizeof (IPSEC_PAD_ENTRY));\r
+  PadEntrySize  = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_ID));\r
+  PadEntrySize  = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_DATA));\r
+  PadEntrySize  = ALIGN_VARIABLE (PadEntrySize + (PadData->AuthData != NULL ? PadData->AuthDataSize : 0));\r
+  PadEntrySize += PadData->RevocationData != NULL ? PadData->RevocationDataSize : 0;\r
+\r
+  PadEntry      = AllocateZeroPool (PadEntrySize);\r
+\r
+  if (PadEntry == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Fix the address of Id and Data buffer and copy them, which is\r
+  // continous memory and close to the base structure of pad entry.\r
+  //\r
+  PadEntry->Id    = (EFI_IPSEC_PAD_ID *) ALIGN_POINTER ((PadEntry + 1), sizeof (UINTN));\r
+  PadEntry->Data  = (EFI_IPSEC_PAD_DATA *) ALIGN_POINTER ((PadEntry->Id + 1), sizeof (UINTN));\r
+\r
+  CopyMem (PadEntry->Id, PadId, sizeof (EFI_IPSEC_PAD_ID));\r
+\r
+  PadEntry->Data->AuthProtocol  = PadData->AuthProtocol;\r
+  PadEntry->Data->AuthMethod    = PadData->AuthMethod;\r
+  PadEntry->Data->IkeIdFlag     = PadData->IkeIdFlag;\r
+\r
+  if (PadData->AuthData != NULL) {\r
+    PadEntry->Data->AuthDataSize  = PadData->AuthDataSize;\r
+    PadEntry->Data->AuthData      = (VOID *) ALIGN_POINTER (PadEntry->Data + 1, sizeof (UINTN));\r
+    CopyMem (\r
+      PadEntry->Data->AuthData,\r
+      PadData->AuthData,\r
+      PadData->AuthDataSize\r
+      );\r
+  } else {\r
+    PadEntry->Data->AuthDataSize  = 0;\r
+    PadEntry->Data->AuthData      = NULL;\r
+  }\r
+\r
+  if (PadData->RevocationData != NULL) {\r
+    PadEntry->Data->RevocationDataSize  = PadData->RevocationDataSize;\r
+    PadEntry->Data->RevocationData      = (VOID *) ALIGN_POINTER (\r
+                                                    ((UINT8 *) (PadEntry->Data + 1) + PadData->AuthDataSize),\r
+                                                    sizeof (UINTN)\r
+                                                    );\r
+    CopyMem (\r
+      PadEntry->Data->RevocationData,\r
+      PadData->RevocationData,\r
+      PadData->RevocationDataSize\r
+      );\r
+  } else {\r
+    PadEntry->Data->RevocationDataSize  = 0;\r
+    PadEntry->Data->RevocationData      = NULL;\r
+  }\r
+  //\r
+  // Insert the new pad entry.\r
+  //\r
+  InsertTailList (EntryInsertBefore, &PadEntry->List);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function lookup the data entry from IPsec SPD. Return the configuration\r
+  value of the specified SPD Entry.\r
+\r
+  @param[in]      Selector      Pointer to an entry selector which is an identifier\r
+                                of the SPD entry.\r
+  @param[in, out] DataSize      On output the size of data returned in Data.\r
+  @param[out]     Data          The buffer to return the contents of the IPsec\r
+                                configuration data. The type of the data buffer\r
+                                is associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.\r
+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.\r
+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been\r
+                                updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetSpdEntry (\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR       *Selector,\r
+  IN OUT UINTN                           *DataSize,\r
+     OUT VOID                            *Data\r
+  )\r
+{\r
+  IPSEC_SPD_ENTRY         *SpdEntry;\r
+  IPSEC_SAD_ENTRY         *SadEntry;\r
+  EFI_IPSEC_SPD_SELECTOR  *SpdSel;\r
+  EFI_IPSEC_SPD_DATA      *SpdData;\r
+  LIST_ENTRY              *SpdList;\r
+  LIST_ENTRY              *SpdSas;\r
+  LIST_ENTRY              *Entry;\r
+  UINTN                   RequiredSize;\r
+\r
+  SpdSel  = &Selector->SpdSelector;\r
+  SpdData = (EFI_IPSEC_SPD_DATA *) Data;\r
+  SpdList = &mConfigData[IPsecConfigDataTypeSpd];\r
+\r
+  NET_LIST_FOR_EACH (Entry, SpdList) {\r
+    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
+\r
+    //\r
+    // Find the required spd entry\r
+    //\r
+    if (CompareSpdSelector (\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector\r
+          )) {\r
+\r
+      RequiredSize = IpSecGetSizeOfSpdData (SpdEntry->Data);\r
+      if (*DataSize < RequiredSize) {\r
+        *DataSize = RequiredSize;\r
+        return EFI_BUFFER_TOO_SMALL;\r
+      }\r
+\r
+      if (SpdData == NULL) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      *DataSize = RequiredSize;\r
+\r
+      //\r
+      // Extract and fill all SaId array from the spd.sas list\r
+      //\r
+      SpdSas              = &SpdEntry->Data->Sas;\r
+      SpdData->SaIdCount  = 0;\r
+\r
+      NET_LIST_FOR_EACH (Entry, SpdSas) {\r
+        SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);\r
+        CopyMem (\r
+          &SpdData->SaId[SpdData->SaIdCount++],\r
+          SadEntry->Id,\r
+          sizeof (EFI_IPSEC_SA_ID)\r
+          );\r
+      }\r
+      //\r
+      // Fill the other fields in spd data.\r
+      //\r
+      CopyMem (SpdData->Name, SpdEntry->Data->Name, sizeof (SpdData->Name));\r
+\r
+      SpdData->PackageFlag  = SpdEntry->Data->PackageFlag;\r
+      SpdData->Action       = SpdEntry->Data->Action;\r
+\r
+      if (SpdData->Action != EfiIPsecActionProtect) {\r
+        SpdData->ProcessingPolicy = NULL;\r
+      } else {\r
+        SpdData->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ((UINT8 *) SpdData + sizeof (EFI_IPSEC_SPD_DATA) + (SpdData->SaIdCount - 1) * sizeof (EFI_IPSEC_SA_ID));\r
+\r
+        IpSecDuplicateProcessPolicy (\r
+          SpdData->ProcessingPolicy,\r
+          SpdEntry->Data->ProcessingPolicy\r
+          );\r
+      }\r
+\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  This function lookup the data entry from IPsec SAD. Return the configuration\r
+  value of the specified SAD Entry.\r
+\r
+  @param[in]      Selector      Pointer to an entry selector which is an identifier\r
+                                of the SAD entry.\r
+  @param[in, out] DataSize      On output, the size of data returned in Data.\r
+  @param[out]     Data          The buffer to return the contents of the IPsec\r
+                                configuration data. The type of the data buffer\r
+                                is associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.\r
+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been\r
+                                updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetSadEntry (\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR     *Selector,\r
+  IN OUT UINTN                         *DataSize,\r
+     OUT VOID                          *Data\r
+  )\r
+{\r
+  IPSEC_SAD_ENTRY   *SadEntry;\r
+  LIST_ENTRY        *Entry;\r
+  LIST_ENTRY        *SadList;\r
+  EFI_IPSEC_SA_ID   *SaId;\r
+  EFI_IPSEC_SA_DATA *SaData;\r
+  UINTN             RequiredSize;\r
+\r
+  SaId    = &Selector->SaId;\r
+  SaData  = (EFI_IPSEC_SA_DATA *) Data;\r
+  SadList = &mConfigData[IPsecConfigDataTypeSad];\r
+\r
+  NET_LIST_FOR_EACH (Entry, SadList) {\r
+    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+\r
+    //\r
+    // Find the required sad entry.\r
+    //\r
+    if (CompareSaId (\r
+         (EFI_IPSEC_CONFIG_SELECTOR *) SaId,\r
+         (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id\r
+         )) {\r
+      //\r
+      // Calculate the required size of the sad entry.\r
+      // Data Layout is follows:\r
+      // |EFI_IPSEC_SA_DATA\r
+      // |AuthKey\r
+      // |EncryptKey  (Optional)\r
+      // |SpdSelector (Optional)\r
+      //\r
+      RequiredSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA));\r
+\r
+      if (SaId->Proto == EfiIPsecAH) {\r
+        RequiredSize  = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength);\r
+      } else {\r
+        RequiredSize  = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength);\r
+        RequiredSize  = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength);\r
+      }\r
+\r
+      if (SadEntry->Data->SpdEntry != NULL) {\r
+        RequiredSize += SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector);\r
+      }\r
+\r
+\r
+\r
+      if (*DataSize < RequiredSize) {\r
+        *DataSize = RequiredSize;\r
+        return EFI_BUFFER_TOO_SMALL;\r
+      }\r
+      //\r
+      // Fill the data fields of sad entry.\r
+      //\r
+      *DataSize                 = RequiredSize;\r
+      SaData->Mode              = SadEntry->Data->Mode;\r
+      SaData->SNCount           = SadEntry->Data->SequenceNumber;\r
+      SaData->AntiReplayWindows = SadEntry->Data->AntiReplayWindowSize;\r
+\r
+      CopyMem (\r
+        &SaData->SaLifetime,\r
+        &SadEntry->Data->SaLifetime,\r
+        sizeof (EFI_IPSEC_SA_LIFETIME)\r
+        );\r
+\r
+      ZeroMem (\r
+        &SaData->AlgoInfo,\r
+        sizeof (EFI_IPSEC_ALGO_INFO)\r
+        );\r
+\r
+      if (SaId->Proto == EfiIPsecAH) {\r
+        //\r
+        // Copy AH alogrithm INFO to SaData\r
+        //\r
+        SaData->AlgoInfo.AhAlgoInfo.AuthAlgoId    = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthAlgoId;\r
+        SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength;\r
+        if (SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength != 0) {\r
+          SaData->AlgoInfo.AhAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));\r
+          CopyMem (\r
+            SaData->AlgoInfo.AhAlgoInfo.AuthKey,\r
+            SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKey,\r
+            SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength\r
+            );\r
+        }\r
+      } else if (SaId->Proto == EfiIPsecESP) {\r
+        //\r
+        // Copy ESP alogrithem INFO to SaData\r
+        //\r
+        SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId     = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId;\r
+        SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength  = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength;\r
+        if (SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {\r
+          SaData->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));\r
+          CopyMem (\r
+            SaData->AlgoInfo.EspAlgoInfo.AuthKey,\r
+            SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+            SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength\r
+            );\r
+        }\r
+\r
+        SaData->AlgoInfo.EspAlgoInfo.EncAlgoId    = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId;\r
+        SaData->AlgoInfo.EspAlgoInfo.EncKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength;\r
+\r
+        if (SaData->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {\r
+          SaData->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (\r
+                                                          ((UINT8 *) (SaData + 1) +\r
+                                                            SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength),\r
+                                                            sizeof (UINTN)\r
+                                                            );\r
+          CopyMem (\r
+            SaData->AlgoInfo.EspAlgoInfo.EncKey,\r
+            SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,\r
+            SaData->AlgoInfo.EspAlgoInfo.EncKeyLength\r
+            );\r
+        }\r
+      }\r
+\r
+      SaData->PathMTU = SadEntry->Data->PathMTU;\r
+\r
+      //\r
+      // Fill the spd selector field of sad data\r
+      //\r
+      if (SadEntry->Data->SpdEntry != NULL) {\r
+\r
+        SaData->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) (\r
+                                (UINT8 *)SaData +\r
+                                RequiredSize -\r
+                                SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector)\r
+                                );\r
+\r
+        DuplicateSpdSelector (\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector,\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdEntry->Selector,\r
+          NULL\r
+          );\r
+\r
+      } else {\r
+\r
+        SaData->SpdSelector = NULL;\r
+      }\r
+\r
+      SaData->ManualSet = SadEntry->Data->ManualSet;\r
+\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  This function lookup the data entry from IPsec PAD. Return the configuration\r
+  value of the specified PAD Entry.\r
+\r
+  @param[in]      Selector      Pointer to an entry selector which is an identifier\r
+                                of the PAD entry.\r
+  @param[in, out] DataSize      On output the size of data returned in Data.\r
+  @param[out]     Data          The buffer to return the contents of the IPsec\r
+                                configuration data. The type of the data buffer\r
+                                is associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.\r
+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been\r
+                                updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetPadEntry (\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR   *Selector,\r
+  IN OUT UINTN                       *DataSize,\r
+     OUT VOID                        *Data\r
+  )\r
+{\r
+  IPSEC_PAD_ENTRY     *PadEntry;\r
+  LIST_ENTRY          *PadList;\r
+  LIST_ENTRY          *Entry;\r
+  EFI_IPSEC_PAD_ID    *PadId;\r
+  EFI_IPSEC_PAD_DATA  *PadData;\r
+  UINTN               RequiredSize;\r
+\r
+  PadId   = &Selector->PadId;\r
+  PadData = (EFI_IPSEC_PAD_DATA *) Data;\r
+  PadList = &mConfigData[IPsecConfigDataTypePad];\r
+\r
+  NET_LIST_FOR_EACH (Entry, PadList) {\r
+    PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
+\r
+    //\r
+    // Find the required pad entry.\r
+    //\r
+    if (ComparePadId (\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) PadId,\r
+          (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id\r
+          )) {\r
+      //\r
+      // Calculate the required size of the pad entry.\r
+      //\r
+      RequiredSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));\r
+      RequiredSize  = ALIGN_VARIABLE (RequiredSize + PadEntry->Data->AuthDataSize);\r
+      RequiredSize += PadEntry->Data->RevocationDataSize;\r
+\r
+      if (*DataSize < RequiredSize) {\r
+        *DataSize = RequiredSize;\r
+        return EFI_BUFFER_TOO_SMALL;\r
+      }\r
+      //\r
+      // Fill the data fields of pad entry\r
+      //\r
+      *DataSize             = RequiredSize;\r
+      PadData->AuthProtocol = PadEntry->Data->AuthProtocol;\r
+      PadData->AuthMethod   = PadEntry->Data->AuthMethod;\r
+      PadData->IkeIdFlag    = PadEntry->Data->IkeIdFlag;\r
+\r
+      //\r
+      // Copy Authentication data.\r
+      //\r
+      if (PadEntry->Data->AuthData != NULL) {\r
+\r
+        PadData->AuthDataSize = PadEntry->Data->AuthDataSize;\r
+        PadData->AuthData     = (VOID *) ALIGN_POINTER ((PadData + 1), sizeof (UINTN));\r
+        CopyMem (\r
+          PadData->AuthData,\r
+          PadEntry->Data->AuthData,\r
+          PadData->AuthDataSize\r
+          );\r
+      } else {\r
+\r
+        PadData->AuthDataSize = 0;\r
+        PadData->AuthData     = NULL;\r
+      }\r
+      //\r
+      // Copy Revocation Data.\r
+      //\r
+      if (PadEntry->Data->RevocationData != NULL) {\r
+\r
+        PadData->RevocationDataSize = PadEntry->Data->RevocationDataSize;\r
+        PadData->RevocationData     = (VOID *) ALIGN_POINTER (\r
+                                                 ((UINT8 *) (PadData + 1) + PadData->AuthDataSize),\r
+                                                  sizeof (UINTN)\r
+                                                  );\r
+        CopyMem (\r
+          PadData->RevocationData,\r
+          PadEntry->Data->RevocationData,\r
+          PadData->RevocationDataSize\r
+          );\r
+      } else {\r
+\r
+        PadData->RevocationDataSize = 0;\r
+        PadData->RevocationData     = NULL;\r
+      }\r
+\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  Copy Source Process Policy to the Destination Process Policy.\r
+\r
+  @param[in]  Dst                  Pointer to the Source Process Policy.\r
+  @param[in]  Src                  Pointer to the Destination Process Policy.\r
+\r
+**/\r
+VOID\r
+IpSecDuplicateProcessPolicy (\r
+  IN EFI_IPSEC_PROCESS_POLICY            *Dst,\r
+  IN EFI_IPSEC_PROCESS_POLICY            *Src\r
+  )\r
+{\r
+  //\r
+  // Firstly copy the structure content itself.\r
+  //\r
+  CopyMem (Dst, Src, sizeof (EFI_IPSEC_PROCESS_POLICY));\r
+\r
+  //\r
+  // Recursively copy the tunnel option if needed.\r
+  //\r
+  if (Dst->Mode != EfiIPsecTunnel) {\r
+    ASSERT (Dst->TunnelOption == NULL);\r
+  } else {\r
+    Dst->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER ((Dst + 1), sizeof (UINTN));\r
+    CopyMem (\r
+      Dst->TunnelOption,\r
+      Src->TunnelOption,\r
+      sizeof (EFI_IPSEC_TUNNEL_OPTION)\r
+      );\r
+  }\r
+}\r
+\r
+/**\r
+  Calculate the a whole size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed\r
+  to by the pointer members.\r
+\r
+  @param[in]  SpdData             Pointer to a specified EFI_IPSEC_SPD_DATA.\r
+\r
+  @return the whole size the specified EFI_IPSEC_SPD_DATA.\r
+\r
+**/\r
+UINTN\r
+IpSecGetSizeOfEfiSpdData (\r
+  IN EFI_IPSEC_SPD_DATA               *SpdData\r
+  )\r
+{\r
+  UINTN Size;\r
+\r
+  Size = ALIGN_VARIABLE (sizeof (IPSEC_SPD_DATA));\r
+\r
+  if (SpdData->Action == EfiIPsecActionProtect) {\r
+    Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_PROCESS_POLICY));\r
+\r
+    if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {\r
+      Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_TUNNEL_OPTION));\r
+    }\r
+  }\r
+\r
+  return Size;\r
+}\r
+\r
+/**\r
+  Calculate the a whole size of IPSEC_SPD_DATA which includes the buffer size pointed\r
+  to by the pointer members and the buffer size used by the Sa List.\r
+\r
+  @param[in]  SpdData       Pointer to the specified IPSEC_SPD_DATA.\r
+\r
+  @return the whole size of IPSEC_SPD_DATA.\r
+\r
+**/\r
+UINTN\r
+IpSecGetSizeOfSpdData (\r
+  IN IPSEC_SPD_DATA                   *SpdData\r
+  )\r
+{\r
+  UINTN       Size;\r
+  LIST_ENTRY  *Link;\r
+\r
+  Size = sizeof (EFI_IPSEC_SPD_DATA) - sizeof (EFI_IPSEC_SA_ID);\r
+\r
+  if (SpdData->Action == EfiIPsecActionProtect) {\r
+    Size += sizeof (EFI_IPSEC_PROCESS_POLICY);\r
+\r
+    if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {\r
+      Size += sizeof (EFI_IPSEC_TUNNEL_OPTION);\r
+    }\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Link, &SpdData->Sas) {\r
+    Size += sizeof (EFI_IPSEC_SA_ID);\r
+  }\r
+\r
+  return Size;\r
+}\r
+\r
+/**\r
+  Get the IPsec Variable.\r
+\r
+  Get the all variables which start with the string contained in VaraiableName.\r
+  Since all IPsec related variable store in continual space, those kinds of\r
+  variable can be searched by the EfiGetNextVariableName. Those variables also are\r
+  returned in a continual buffer.\r
+\r
+  @param[in]      VariableName          Pointer to a specified Variable Name.\r
+  @param[in]      VendorGuid            Pointer to a specified Vendor Guid.\r
+  @param[in]      Attributes            Point to memory location to return the attributes\r
+                                        of variable. If the point is NULL, the parameter\r
+                                        would be ignored.\r
+  @param[in, out] DataSize              As input, point to the maximum size of return\r
+                                        Data-Buffer. As output, point to the actual\r
+                                        size of the returned Data-Buffer.\r
+  @param[in]      Data                  Point to return Data-Buffer.\r
+\r
+  @retval  EFI_ABORTED           If the Variable size which contained in the variable\r
+                                 structure doesn't match the variable size obtained\r
+                                 from the EFIGetVariable.\r
+  @retval  EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has\r
+                                 been updated with the size needed to complete the request.\r
+  @retval  EFI_SUCCESS           The function completed successfully.\r
+  @retval  others                Other errors found during the variable getting.\r
+**/\r
+EFI_STATUS\r
+IpSecGetVariable (\r
+  IN     CHAR16                       *VariableName,\r
+  IN     EFI_GUID                     *VendorGuid,\r
+  IN     UINT32                       *Attributes, OPTIONAL\r
+  IN OUT UINTN                        *DataSize,\r
+  IN     VOID                         *Data\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  EFI_GUID              VendorGuidI;\r
+  UINTN                 VariableNameLength;\r
+  CHAR16                *VariableNameI;\r
+  UINTN                 VariableNameISize;\r
+  UINTN                 VariableNameISizeNew;\r
+  UINTN                 VariableIndex;\r
+  UINTN                 VariableCount;\r
+  IP_SEC_VARIABLE_INFO  IpSecVariableInfo;\r
+  UINTN                 DataSizeI;\r
+\r
+  //\r
+  // The variable name constructor is "VariableName + Info/0001/0002/... + NULL".\r
+  // So the varialbe name is like "VariableNameInfo", "VariableName0001", ...\r
+  // "VariableNameNULL".\r
+  //\r
+  VariableNameLength  = StrLen (VariableName);\r
+  VariableNameISize   = (VariableNameLength + 5) * sizeof (CHAR16);\r
+  VariableNameI       = AllocateZeroPool (VariableNameISize);\r
+  ASSERT (VariableNameI != NULL);\r
+\r
+  //\r
+  // Construct the varible name of ipsecconfig meta data.\r
+  //\r
+  UnicodeSPrint (VariableNameI, VariableNameISize, L"%s%s", VariableName, L"Info");\r
+\r
+  DataSizeI = sizeof (IpSecVariableInfo);\r
+\r
+  Status = gRT->GetVariable (\r
+                  VariableNameI,\r
+                  VendorGuid,\r
+                  Attributes,\r
+                  &DataSizeI,\r
+                  &IpSecVariableInfo\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (*DataSize < IpSecVariableInfo.VariableSize) {\r
+    *DataSize = IpSecVariableInfo.VariableSize;\r
+    Status    = EFI_BUFFER_TOO_SMALL;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  VariableCount     = IpSecVariableInfo.VariableCount;\r
+  VariableNameI[0]  = L'\0';\r
+\r
+  while (VariableCount != 0) {\r
+    //\r
+    // Get the variable name one by one in the variable database.\r
+    //\r
+    VariableNameISizeNew = VariableNameISize;\r
+    Status = gRT->GetNextVariableName (\r
+                    &VariableNameISizeNew,\r
+                    VariableNameI,\r
+                    &VendorGuidI\r
+                    );\r
+    if (Status == EFI_BUFFER_TOO_SMALL) {\r
+      VariableNameI = ReallocatePool (\r
+                        VariableNameISize,\r
+                        VariableNameISizeNew,\r
+                        VariableNameI\r
+                        );\r
+      VariableNameISize = VariableNameISizeNew;\r
+\r
+      Status = gRT->GetNextVariableName (\r
+                      &VariableNameISizeNew,\r
+                      VariableNameI,\r
+                      &VendorGuidI\r
+                      );\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+    //\r
+    // Check whether the current variable is the required "ipsecconfig".\r
+    //\r
+    if (StrnCmp (VariableNameI, VariableName, VariableNameLength) == 0 ||\r
+        CompareGuid (VendorGuid, &VendorGuidI)\r
+        ) {\r
+      //\r
+      // Parse the variable count of the current ipsecconfig data.\r
+      //\r
+      VariableIndex = StrDecimalToUintn (VariableNameI + VariableNameLength);\r
+      if (VariableIndex!= 0 && VariableIndex <= IpSecVariableInfo.VariableCount) {\r
+        //\r
+        // Get the variable size of the current ipsecconfig data.\r
+        //\r
+        DataSizeI = 0;\r
+        Status = gRT->GetVariable (\r
+                        VariableNameI,\r
+                        VendorGuid,\r
+                        Attributes,\r
+                        &DataSizeI,\r
+                        NULL\r
+                        );\r
+        ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+        //\r
+        // Validate the variable count and variable size.\r
+        //\r
+        if (VariableIndex != IpSecVariableInfo.VariableCount) {\r
+          //\r
+          // If the varaibe is not the last one, its size should be the max\r
+          // size of the single variable.\r
+          //\r
+          if (DataSizeI != IpSecVariableInfo.SingleVariableSize) {\r
+            return EFI_ABORTED;\r
+          }\r
+        } else {\r
+          if (DataSizeI != IpSecVariableInfo.VariableSize % IpSecVariableInfo.SingleVariableSize) {\r
+            return EFI_ABORTED;\r
+          }\r
+        }\r
+        //\r
+        // Get the variable data of the current ipsecconfig data and\r
+        // store it into user buffer continously.\r
+        //\r
+        Status = gRT->GetVariable (\r
+                        VariableNameI,\r
+                        VendorGuid,\r
+                        Attributes,\r
+                        &DataSizeI,\r
+                        (UINT8 *) Data + (VariableIndex - 1) * IpSecVariableInfo.SingleVariableSize\r
+                        );\r
+        ASSERT_EFI_ERROR (Status);\r
+        VariableCount--;\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // The VariableCount in "VariableNameInfo" varaible should have the correct\r
+  // numbers of variables which name starts with VariableName.\r
+  //\r
+  if (VariableCount != 0) {\r
+    Status = EFI_ABORTED;\r
+  }\r
+\r
+ON_EXIT:\r
+  FreePool (VariableNameI);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Set the IPsec variables.\r
+\r
+  Set all IPsec variables which start with the specified variable name. Those variables\r
+  are set one by one.\r
+\r
+  @param[in]  VariableName  The name of the vendor's variable. It is a\r
+                            Null-Terminated Unicode String.\r
+  @param[in]  VendorGuid    Unify identifier for vendor.\r
+  @param[in]  Attributes    Point to memory location to return the attributes of\r
+                            variable. If the point is NULL, the parameter would be ignored.\r
+  @param[in]  DataSize      The size in bytes of Data-Buffer.\r
+  @param[in]  Data          Points to the content of the variable.\r
+\r
+  @retval  EFI_SUCCESS      The firmware successfully stored the variable and its data, as\r
+                            defined by the Attributes.\r
+  @retval  others           Storing the variables failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecSetVariable (\r
+  IN CHAR16                           *VariableName,\r
+  IN EFI_GUID                         *VendorGuid,\r
+  IN UINT32                           Attributes,\r
+  IN UINTN                            DataSize,\r
+  IN VOID                             *Data\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  CHAR16                *VariableNameI;\r
+  UINTN                 VariableNameSize;\r
+  UINTN                 VariableIndex;\r
+  IP_SEC_VARIABLE_INFO  IpSecVariableInfo;\r
+  UINT64                MaximumVariableStorageSize;\r
+  UINT64                RemainingVariableStorageSize;\r
+  UINT64                MaximumVariableSize;\r
+\r
+  Status = gRT->QueryVariableInfo (\r
+                  Attributes,\r
+                  &MaximumVariableStorageSize,\r
+                  &RemainingVariableStorageSize,\r
+                  &MaximumVariableSize\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // "VariableName + Info/0001/0002/... + NULL"\r
+  //\r
+  VariableNameSize  = (StrLen (VariableName) + 5) * sizeof (CHAR16);\r
+  VariableNameI     = AllocateZeroPool (VariableNameSize);\r
+\r
+  if (VariableNameI == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Construct the variable of ipsecconfig general information. Like the total\r
+  // numbers of the Ipsecconfig variables, the total size of all ipsecconfig variables.\r
+  //\r
+  UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%s", VariableName, L"Info");\r
+  MaximumVariableSize -= VariableNameSize;\r
+\r
+  IpSecVariableInfo.VariableCount       = (UINT32) ((DataSize + (UINTN) MaximumVariableSize - 1) / (UINTN) MaximumVariableSize);\r
+  IpSecVariableInfo.VariableSize        = (UINT32) DataSize;\r
+  IpSecVariableInfo.SingleVariableSize  = (UINT32) MaximumVariableSize;\r
+\r
+  //\r
+  // Set the variable of ipsecconfig general information.\r
+  //\r
+  Status = gRT->SetVariable (\r
+                  VariableNameI,\r
+                  VendorGuid,\r
+                  Attributes,\r
+                  sizeof (IpSecVariableInfo),\r
+                  &IpSecVariableInfo\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "Error set ipsecconfig meta data with %r\n", Status));\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  for (VariableIndex = 0; VariableIndex < IpSecVariableInfo.VariableCount; VariableIndex++) {\r
+    //\r
+    // Construct and set the variable of ipsecconfig data one by one.\r
+    // The index of variable name begin from 0001, and the varaible name\r
+    // likes "VariableName0001", "VaraiableName0002"....\r
+    //\r
+    UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%04d", VariableName, VariableIndex + 1);\r
+    Status = gRT->SetVariable (\r
+                    VariableNameI,\r
+                    VendorGuid,\r
+                    Attributes,\r
+                    (VariableIndex == IpSecVariableInfo.VariableCount - 1) ?\r
+                    (DataSize % (UINTN) MaximumVariableSize) :\r
+                    (UINTN) MaximumVariableSize,\r
+                    (UINT8 *) Data + VariableIndex * (UINTN) MaximumVariableSize\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "Error set ipsecconfig variable data with %r\n", Status));\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+  if (VariableNameI != NULL) {\r
+    FreePool (VariableNameI);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Return the configuration value for the EFI IPsec driver.\r
+\r
+  This function lookup the data entry from IPsec database or IKEv2 configuration\r
+  information. The expected data type and unique identification are described in\r
+  DataType and Selector parameters.\r
+\r
+  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in]      DataType      The type of data to retrieve.\r
+  @param[in]      Selector      Pointer to an entry selector that is an identifier of the IPsec\r
+                                configuration data entry.\r
+  @param[in, out] DataSize      On output the size of data returned in Data.\r
+  @param[out]     Data          The buffer to return the contents of the IPsec configuration data.\r
+                                The type of the data buffer associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+                                - This is NULL.\r
+                                - Selector is NULL.\r
+                                - DataSize is NULL.\r
+                                - Data is NULL and *DataSize is not zero\r
+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.\r
+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.\r
+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been\r
+                                updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigGetData (\r
+  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,\r
+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,\r
+  IN OUT UINTN                        *DataSize,\r
+     OUT VOID                         *Data\r
+  )\r
+{\r
+  if (This == NULL || Selector == NULL || DataSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (*DataSize != 0 && Data == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DataType >= IPsecConfigDataTypeMaximum) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return mGetPolicyEntry[DataType](Selector, DataSize, Data);\r
+}\r
+\r
+/**\r
+  Set the security association, security policy and peer authorization configuration\r
+  information for the EFI IPsec driver.\r
+\r
+  This function is used to set the IPsec configuration information of type DataType for\r
+  the EFI IPsec driver.\r
+  The IPsec configuration data has a unique selector/identifier separately to identify\r
+  a data entry. The selector structure depends on DataType's definition.\r
+  Using SetData() with a Data of NULL causes the IPsec configuration data entry identified\r
+  by DataType and Selector to be deleted.\r
+\r
+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in] DataType           The type of data to be set.\r
+  @param[in] Selector           Pointer to an entry selector on operated configuration data\r
+                                specified by DataType. A NULL Selector causes the entire\r
+                                specified-type configuration information to be flushed.\r
+  @param[in] Data               The data buffer to be set. The structure of the data buffer is\r
+                                associated with the DataType.\r
+  @param[in] InsertBefore       Pointer to one entry selector which describes the expected\r
+                                position the new data entry will be added. If InsertBefore is NULL,\r
+                                the new entry will be appended to the end of the database.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration entry data was set successfully.\r
+  @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+                                - This is NULL.\r
+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.\r
+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigSetData (\r
+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector,\r
+  IN VOID                             *Data,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *InsertBefore OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DataType >= IPsecConfigDataTypeMaximum) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Status = mSetPolicyEntry[DataType](Selector, Data, InsertBefore);\r
+\r
+  if (!EFI_ERROR (Status) && !mSetBySelf) {\r
+    //\r
+    // Save the updated config data into variable.\r
+    //\r
+    IpSecConfigSave ();\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Enumerates the current selector for IPsec configuration data entry.\r
+\r
+  This function is called multiple times to retrieve the entry Selector in IPsec\r
+  configuration database. On each call to GetNextSelector(), the next entry\r
+  Selector are retrieved into the output interface.\r
+\r
+  If the entire IPsec configuration database has been iterated, the error\r
+  EFI_NOT_FOUND is returned.\r
+  If the Selector buffer is too small for the next Selector copy, an\r
+  EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect\r
+  the size of buffer needed.\r
+\r
+  On the initial call to GetNextSelector() to start the IPsec configuration database\r
+  search, a pointer to the buffer with all zero value is passed in Selector. Calls\r
+  to SetData() between calls to GetNextSelector may produce unpredictable results.\r
+\r
+  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in]      DataType      The type of IPsec configuration data to retrieve.\r
+  @param[in, out] SelectorSize  The size of the Selector buffer.\r
+  @param[in, out] Selector      On input, supplies the pointer to last Selector that was\r
+                                returned by GetNextSelector().\r
+                                On output, returns one copy of the current entry Selector\r
+                                of a given DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+                                - This is NULL.\r
+                                - SelectorSize is NULL.\r
+                                - Selector is NULL.\r
+  @retval EFI_NOT_FOUND         The next configuration data entry was not found.\r
+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.\r
+  @retval EFI_BUFFER_TOO_SMALL  The SelectorSize is too small for the result. This parameter\r
+                                has been updated with the size needed to complete the search\r
+                                request.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigGetNextSelector (\r
+  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,\r
+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,\r
+  IN OUT UINTN                        *SelectorSize,\r
+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *Selector\r
+  )\r
+{\r
+  LIST_ENTRY                *Link;\r
+  IPSEC_COMMON_POLICY_ENTRY *CommonEntry;\r
+  BOOLEAN                   IsFound;\r
+\r
+  if (This == NULL || Selector == NULL || SelectorSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (DataType >= IPsecConfigDataTypeMaximum) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  IsFound = FALSE;\r
+\r
+  NET_LIST_FOR_EACH (Link, &mConfigData[DataType]) {\r
+    CommonEntry = BASE_CR (Link, IPSEC_COMMON_POLICY_ENTRY, List);\r
+\r
+    if (IsFound || mIsZeroSelector[DataType](Selector)) {\r
+      //\r
+      // If found the appointed entry, then duplicate the next one and return,\r
+      // or if the appointed entry is zero, then return the first one directly.\r
+      //\r
+      return mDuplicateSelector[DataType](Selector, CommonEntry->Selector, SelectorSize);\r
+    } else {\r
+      //\r
+      // Set the flag if find the appointed entry.\r
+      //\r
+      IsFound = mCompareSelector[DataType](Selector, CommonEntry->Selector);\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  Register an event that is to be signaled whenever a configuration process on the\r
+  specified IPsec configuration information is done.\r
+\r
+  The register function is not surpport now and always returns EFI_UNSUPPORTED.\r
+\r
+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in] DataType           The type of data to be registered the event for.\r
+  @param[in] Event              The event to be registered.\r
+\r
+  @retval EFI_SUCCESS           The event is registered successfully.\r
+  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.\r
+  @retval EFI_ACCESS_DENIED     The Event is already registered for the DataType.\r
+  @retval EFI_UNSUPPORTED       The notify registration is unsupported, or the specified\r
+                                DataType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigRegisterNotify (\r
+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,\r
+  IN EFI_EVENT                        Event\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Remove the specified event that was previously registered on the specified IPsec\r
+  configuration data.\r
+\r
+  This function is not support now and alwasy return EFI_UNSUPPORTED.\r
+\r
+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in] DataType           The configuration data type to remove the registered event for.\r
+  @param[in] Event              The event to be unregistered.\r
+\r
+  @retval EFI_SUCCESS           The event was removed successfully.\r
+  @retval EFI_NOT_FOUND         The Event specified by DataType could not be found in the\r
+                                database.\r
+  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.\r
+  @retval EFI_UNSUPPORTED       The notify registration is unsupported, or the specified\r
+                                DataType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigUnregisterNotify (\r
+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,\r
+  IN EFI_EVENT                        Event\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Copy whole data in specified EFI_SIPEC_CONFIG_SELECTOR and the Data to a buffer.\r
+\r
+  This function is a caller defined function, and it is called by the IpSecVisitConfigData().\r
+  The orignal caller is IpSecConfigSave(), which calls the IpsecVisitConfigData() to\r
+  copy all types of IPsec Config datas into one buffer and store this buffer into firmware in\r
+  the form of several variables.\r
+\r
+  @param[in]      Type              A specified IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in]      Selector          Points to a EFI_IPSEC_CONFIG_SELECTOR to be copied\r
+                                    to the buffer.\r
+  @param[in]      Data              Points to data to be copied to the buffer. The\r
+                                    Data type is related to the Type.\r
+  @param[in]      SelectorSize      The size of the Selector.\r
+  @param[in]      DataSize          The size of the Data.\r
+  @param[in, out] Buffer            The buffer to store the Selector and Data.\r
+\r
+  @retval EFI_SUCCESS            Copy the Selector and Data to a buffer successfully.\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecCopyPolicyEntry (\r
+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   Type,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,\r
+  IN     VOID                         *Data,\r
+  IN     UINTN                        SelectorSize,\r
+  IN     UINTN                        DataSize,\r
+  IN OUT IPSEC_VARIABLE_BUFFER        *Buffer\r
+  )\r
+{\r
+  IPSEC_VAR_ITEM_HEADER SelectorHeader;\r
+  IPSEC_VAR_ITEM_HEADER DataHeader;\r
+  UINTN                 EntrySize;\r
+  UINT8                 *TempPoint;\r
+\r
+  if (Type == IPsecConfigDataTypeSad) {\r
+    //\r
+    // Don't save automatically-generated sa entry into variable.\r
+    //\r
+    if (((EFI_IPSEC_SA_DATA *) Data)->ManualSet == FALSE) {\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+  //\r
+  // Increase the capacity size of the buffer if needed.\r
+  //\r
+  EntrySize  = ALIGN_VARIABLE (sizeof (SelectorHeader));\r
+  EntrySize  = ALIGN_VARIABLE (EntrySize + SelectorSize);\r
+  EntrySize  = ALIGN_VARIABLE (EntrySize + sizeof (SelectorHeader));\r
+  EntrySize  = ALIGN_VARIABLE (EntrySize + DataSize);\r
+\r
+  //EntrySize = SelectorSize + DataSize + 2 * sizeof (SelectorHeader);\r
+  if (Buffer->Capacity - Buffer->Size < EntrySize) {\r
+    //\r
+    // Calculate the required buffer\r
+    //\r
+    Buffer->Capacity += EntrySize;\r
+    TempPoint         = AllocatePool (Buffer->Capacity);\r
+\r
+    if (Buffer->Ptr == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    //\r
+    // Copy the old Buffer to new buffer and free the old one.\r
+    //\r
+    CopyMem (TempPoint, Buffer->Ptr, Buffer->Size);\r
+    FreePool (Buffer->Ptr);\r
+\r
+    Buffer->Ptr       =  TempPoint;\r
+  }\r
+\r
+  mFixPolicyEntry[Type](Selector, Data);\r
+\r
+  //\r
+  // Fill the selector header and copy it into buffer.\r
+  //\r
+  SelectorHeader.Type = (UINT8) (Type | IPSEC_VAR_ITEM_HEADER_LOGO_BIT);\r
+  SelectorHeader.Size = (UINT16) SelectorSize;\r
+\r
+  CopyMem (\r
+    Buffer->Ptr + Buffer->Size,\r
+    &SelectorHeader,\r
+    sizeof (SelectorHeader)\r
+    );\r
+  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + sizeof (SelectorHeader));\r
+\r
+  //\r
+  // Copy the selector into buffer.\r
+  //\r
+  CopyMem (\r
+    Buffer->Ptr + Buffer->Size,\r
+    Selector,\r
+    SelectorSize\r
+    );\r
+  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + SelectorSize);\r
+\r
+  //\r
+  // Fill the data header and copy it into buffer.\r
+  //\r
+  DataHeader.Type = (UINT8) Type;\r
+  DataHeader.Size = (UINT16) DataSize;\r
+\r
+  CopyMem (\r
+    Buffer->Ptr + Buffer->Size,\r
+    &DataHeader,\r
+    sizeof (DataHeader)\r
+    );\r
+  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + sizeof (DataHeader));\r
+  //\r
+  // Copy the data into buffer.\r
+  //\r
+  CopyMem (\r
+    Buffer->Ptr + Buffer->Size,\r
+    Data,\r
+    DataSize\r
+    );\r
+  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + DataSize);\r
+\r
+  mUnfixPolicyEntry[Type](Selector, Data);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Visit all IPsec Configurations of specified Type and call the caller defined\r
+  interface.\r
+\r
+  @param[in]  DataType          The specified IPsec Config Data Type.\r
+  @param[in]  Routine           The function defined by the caller.\r
+  @param[in]  Context           The data passed to the Routine.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated\r
+  @retval EFI_SUCCESS            This function completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecVisitConfigData (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+  IN IPSEC_COPY_POLICY_ENTRY    Routine,\r
+  IN VOID                       *Context\r
+  )\r
+{\r
+  EFI_STATUS                GetNextStatus;\r
+  EFI_STATUS                GetDataStatus;\r
+  EFI_STATUS                RoutineStatus;\r
+  EFI_IPSEC_CONFIG_SELECTOR *Selector;\r
+  VOID                      *Data;\r
+  UINTN                     SelectorSize;\r
+  UINTN                     DataSize;\r
+  UINTN                     SelectorBufferSize;\r
+  UINTN                     DataBufferSize;\r
+  BOOLEAN                   FirstGetNext;\r
+\r
+  FirstGetNext        = TRUE;\r
+  DataBufferSize      = 0;\r
+  Data                = NULL;\r
+  SelectorBufferSize  = sizeof (EFI_IPSEC_CONFIG_SELECTOR);\r
+  Selector            = AllocateZeroPool (SelectorBufferSize);\r
+\r
+  if (Selector == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  while (TRUE) {\r
+    //\r
+    // Get the real size of the selector.\r
+    //\r
+    SelectorSize = SelectorBufferSize;\r
+    GetNextStatus = EfiIpSecConfigGetNextSelector (\r
+                      &mIpSecConfigInstance,\r
+                      DataType,\r
+                      &SelectorSize,\r
+                      Selector\r
+                      );\r
+    if (GetNextStatus == EFI_BUFFER_TOO_SMALL) {\r
+      FreePool (Selector);\r
+      SelectorBufferSize = SelectorSize;\r
+      //\r
+      // Allocate zero pool for the first selector, while store the last\r
+      // selector content for the other selectors.\r
+      //\r
+      if (FirstGetNext) {\r
+        Selector = AllocateZeroPool (SelectorBufferSize);\r
+      } else {\r
+        Selector = AllocateCopyPool (SelectorBufferSize, Selector);\r
+      }\r
+\r
+      if (Selector == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      //\r
+      // Get the content of the selector.\r
+      //\r
+      GetNextStatus = EfiIpSecConfigGetNextSelector (\r
+                        &mIpSecConfigInstance,\r
+                        DataType,\r
+                        &SelectorSize,\r
+                        Selector\r
+                        );\r
+    }\r
+\r
+    if (EFI_ERROR (GetNextStatus)) {\r
+      break;\r
+    }\r
+\r
+    FirstGetNext = FALSE;\r
+\r
+    //\r
+    // Get the real size of the policy entry according to the selector.\r
+    //\r
+    DataSize = DataBufferSize;\r
+    GetDataStatus = EfiIpSecConfigGetData (\r
+                      &mIpSecConfigInstance,\r
+                      DataType,\r
+                      Selector,\r
+                      &DataSize,\r
+                      Data\r
+                      );\r
+    if (GetDataStatus == EFI_BUFFER_TOO_SMALL) {\r
+      if (Data != NULL) {\r
+        FreePool (Data);\r
+      }\r
+\r
+      DataBufferSize  = DataSize;\r
+      Data            = AllocateZeroPool (DataBufferSize);\r
+\r
+      if (Data == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      //\r
+      // Get the content of the policy entry according to the selector.\r
+      //\r
+      GetDataStatus = EfiIpSecConfigGetData (\r
+                        &mIpSecConfigInstance,\r
+                        DataType,\r
+                        Selector,\r
+                        &DataSize,\r
+                        Data\r
+                        );\r
+    }\r
+\r
+    if (EFI_ERROR (GetDataStatus)) {\r
+      break;\r
+    }\r
+    //\r
+    // Prepare the buffer of updated policy entry, which is stored in\r
+    // the continous memory, and then save into variable later.\r
+    //\r
+    RoutineStatus = Routine (\r
+                      DataType,\r
+                      Selector,\r
+                      Data,\r
+                      SelectorSize,\r
+                      DataSize,\r
+                      Context\r
+                      );\r
+    if (EFI_ERROR (RoutineStatus)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (Data != NULL) {\r
+    FreePool (Data);\r
+  }\r
+\r
+  if (Selector != NULL) {\r
+    FreePool (Selector);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function is the subfunction of  EFIIpSecConfigSetData.\r
+\r
+  This function call IpSecSetVaraible to set the IPsec Configuration into the firmware.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS            Saved the configration successfully.\r
+  @retval Others                 Other errors were found while obtaining the variable.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigSave (\r
+  VOID\r
+  )\r
+{\r
+  IPSEC_VARIABLE_BUFFER       Buffer;\r
+  EFI_STATUS                  Status;\r
+  EFI_IPSEC_CONFIG_DATA_TYPE  Type;\r
+\r
+  Buffer.Size     = 0;\r
+  Buffer.Capacity = IPSEC_DEFAULT_VARIABLE_SIZE;\r
+  Buffer.Ptr      = AllocateZeroPool (Buffer.Capacity);\r
+\r
+  if (Buffer.Ptr == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // For each policy database, prepare the contious buffer to save into variable.\r
+  //\r
+  for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {\r
+    IpSecVisitConfigData (\r
+      Type,\r
+      (IPSEC_COPY_POLICY_ENTRY) IpSecCopyPolicyEntry,\r
+      &Buffer\r
+      );\r
+  }\r
+  //\r
+  // Save the updated policy database into variable.\r
+  //\r
+  Status = IpSecSetVariable (\r
+             IPSECCONFIG_VARIABLE_NAME,\r
+             &gEfiIpSecConfigProtocolGuid,\r
+             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+             Buffer.Size,\r
+             Buffer.Ptr\r
+             );\r
+\r
+  FreePool (Buffer.Ptr);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get the all IPSec configuration variables and store those variables\r
+  to the internal data structure.\r
+\r
+  This founction is called by IpSecConfigInitialize() which is to intialize the\r
+  IPsecConfiguration Protocol.\r
+\r
+  @param[in]  Private            Point to IPSEC_PRIVATE_DATA.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated\r
+  @retval EFI_SUCCESS            Restore the IPsec Configuration successfully.\r
+  @retval  others                Other errors is found while obtaining the variable.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigRestore (\r
+  IN IPSEC_PRIVATE_DATA           *Private\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINTN                       BufferSize;\r
+  UINT8                       *Buffer;\r
+  IPSEC_VAR_ITEM_HEADER       *Header;\r
+  UINT8                       *Ptr;\r
+  EFI_IPSEC_CONFIG_SELECTOR   *Selector;\r
+  EFI_IPSEC_CONFIG_DATA_TYPE  Type;\r
+  VOID                        *Data;\r
+  UINT8                       Value;\r
+  UINTN                       Size;\r
+\r
+  Value       = 0;\r
+  Size        = sizeof (Value);\r
+  BufferSize  = 0;\r
+  Buffer      = NULL;\r
+\r
+  Status = gRT->GetVariable (\r
+                  IPSECCONFIG_STATUS_NAME,\r
+                  &gEfiIpSecConfigProtocolGuid,\r
+                  NULL,\r
+                  &Size,\r
+                  &Value\r
+             );\r
+\r
+  if (!EFI_ERROR (Status) && Value == IPSEC_STATUS_ENABLED) {\r
+    Private->IpSec.DisabledFlag = FALSE;\r
+  }\r
+  //\r
+  // Get the real size of policy database in variable.\r
+  //\r
+  Status = IpSecGetVariable (\r
+             IPSECCONFIG_VARIABLE_NAME,\r
+             &gEfiIpSecConfigProtocolGuid,\r
+             NULL,\r
+             &BufferSize,\r
+             Buffer\r
+             );\r
+  if (Status == EFI_BUFFER_TOO_SMALL) {\r
+\r
+    Buffer = AllocateZeroPool (BufferSize);\r
+    if (Buffer == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    //\r
+    // Get the content of policy database in variable.\r
+    //\r
+    Status = IpSecGetVariable (\r
+               IPSECCONFIG_VARIABLE_NAME,\r
+               &gEfiIpSecConfigProtocolGuid,\r
+               NULL,\r
+               &BufferSize,\r
+               Buffer\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (Buffer);\r
+      return Status;\r
+    }\r
+\r
+    for (Ptr = Buffer; Ptr < Buffer + BufferSize;) {\r
+\r
+      Header  = (IPSEC_VAR_ITEM_HEADER *) Ptr;\r
+      Type    = (EFI_IPSEC_CONFIG_DATA_TYPE) (Header->Type & IPSEC_VAR_ITEM_HEADER_CONTENT_BIT);\r
+      ASSERT (((Header->Type & 0x80) == IPSEC_VAR_ITEM_HEADER_LOGO_BIT) && (Type < IPsecConfigDataTypeMaximum));\r
+\r
+      Selector  = (EFI_IPSEC_CONFIG_SELECTOR *) ALIGN_POINTER (Header + 1, sizeof (UINTN));\r
+      Header    = (IPSEC_VAR_ITEM_HEADER *) ALIGN_POINTER (\r
+                                              (UINT8 *) Selector + Header->Size,\r
+                                              sizeof (UINTN)\r
+                                              );\r
+      ASSERT (Header->Type == Type);\r
+\r
+      Data = ALIGN_POINTER (Header + 1, sizeof (UINTN));\r
+\r
+      mUnfixPolicyEntry[Type](Selector, Data);\r
+\r
+      //\r
+      // Update each policy entry according to the content in variable.\r
+      //\r
+      mSetBySelf = TRUE;\r
+      Status = EfiIpSecConfigSetData (\r
+                 &Private->IpSecConfig,\r
+                 Type,\r
+                 Selector,\r
+                 Data,\r
+                 NULL\r
+                 );\r
+      mSetBySelf = FALSE;\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        FreePool (Buffer);\r
+        return Status;\r
+      }\r
+\r
+      Ptr =  ALIGN_POINTER ((UINT8 *) Data + Header->Size, sizeof (UINTN));\r
+    }\r
+\r
+    FreePool (Buffer);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Install and Initialize IPsecConfig protocol\r
+\r
+  @param[in, out]  Private   Pointer to IPSEC_PRIVATE_DATA. After this function finish,\r
+                             the pointer of IPsecConfig Protocol implementation will copy\r
+                             into its IPsecConfig member.\r
+\r
+  @retval     EFI_SUCCESS    Initialized the IPsecConfig Protocol successfully.\r
+  @retval     Others         Initializing the IPsecConfig Protocol failed.\r
+**/\r
+EFI_STATUS\r
+IpSecConfigInitialize (\r
+  IN OUT IPSEC_PRIVATE_DATA        *Private\r
+  )\r
+{\r
+  EFI_IPSEC_CONFIG_DATA_TYPE  Type;\r
+\r
+  CopyMem (\r
+    &Private->IpSecConfig,\r
+    &mIpSecConfigInstance,\r
+    sizeof (EFI_IPSEC_CONFIG_PROTOCOL)\r
+    );\r
+\r
+  //\r
+  // Initialize the list head of policy database.\r
+  //\r
+  for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {\r
+    InitializeListHead (&mConfigData[Type]);\r
+  }\r
+  //\r
+  // Restore the content of policy database according to the variable.\r
+  //\r
+  IpSecConfigRestore (Private);\r
+\r
+  return gBS->InstallMultipleProtocolInterfaces (\r
+                &Private->Handle,\r
+                &gEfiIpSecConfigProtocolGuid,\r
+                &Private->IpSecConfig,\r
+                NULL\r
+                );\r
+}\r
diff --git a/NetworkPkg/IpSecDxe/IpSecConfigImpl.h b/NetworkPkg/IpSecDxe/IpSecConfigImpl.h
new file mode 100644 (file)
index 0000000..54d43bd
--- /dev/null
@@ -0,0 +1,952 @@
+/** @file\r
+  Definitions related to IPSEC_CONFIG_PROTOCOL implementations.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _IPSEC_CONFIG_IMPL_H_\r
+#define _IPSEC_CONFIG_IMPL_H_\r
+\r
+#include <Protocol/IpSec.h>\r
+#include <Protocol/IpSecConfig.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/DebugLib.h>\r
+\r
+#include "IpSecImpl.h"\r
+\r
+#define EFI_IPSEC_ANY_PROTOCOL    0xFFFF\r
+#define EFI_IPSEC_ANY_PORT        0\r
+\r
+#define IPSEC_VAR_ITEM_HEADER_LOGO_BIT     0x80\r
+#define IPSEC_VAR_ITEM_HEADER_CONTENT_BIT  0x7F\r
+\r
+#define IPSECCONFIG_VARIABLE_NAME       L"IpSecConfig"\r
+#define IPSECCONFIG_STATUS_NAME         L"IpSecStatus"\r
+\r
+#define SIZE_OF_SPD_SELECTOR(x) (UINTN) (sizeof (EFI_IPSEC_SPD_SELECTOR) \\r
+       + sizeof (EFI_IP_ADDRESS_INFO) * ((x)->LocalAddressCount + (x)->RemoteAddressCount))\r
+\r
+#define FIX_REF_BUF_ADDR(addr, base)    addr = (VOID *) ((UINTN) (addr) - (UINTN) (base))\r
+#define UNFIX_REF_BUF_ADDR(addr, base)  addr = (VOID *) ((UINTN) (addr) + (UINTN) (base))\r
+\r
+//\r
+// The data structure used to store the genernall information of IPsec configuration.\r
+//\r
+typedef struct {\r
+  UINT32 VariableCount;      // the total number of the IPsecConfig variables.\r
+  UINT32 VariableSize;       // The total size of all IpsecConfig variables.\r
+  UINT32 SingleVariableSize; // The max size of single variable\r
+} IP_SEC_VARIABLE_INFO;\r
+\r
+typedef struct {\r
+  EFI_IPSEC_CONFIG_SELECTOR *Selector;\r
+  VOID                      *Data;\r
+  LIST_ENTRY                List;\r
+} IPSEC_COMMON_POLICY_ENTRY;\r
+\r
+typedef struct {\r
+  UINT8 *Ptr;\r
+  UINTN Size;\r
+  UINTN Capacity;\r
+} IPSEC_VARIABLE_BUFFER;\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+  UINT8   Type;\r
+  UINT16  Size;\r
+} IPSEC_VAR_ITEM_HEADER;\r
+#pragma pack()\r
+\r
+/**\r
+  The prototype of Copy Source Selector to the Destination Selector.\r
+\r
+  @param[in out]  DstSel             Pointer of Destination Selector. It would be\r
+                                     SPD Selector, or SAD Selector or PAD Selector.\r
+  @param[in]      SrcSel             Pointer of Source  Selector. It would be\r
+                                     SPD Selector, or SAD Selector or PAD Selector.\r
+  @param[in out]  Size               The size of the Destination Selector. If it\r
+                                     is not NULL and its value is less than the size of\r
+                                     Source Selector, the value of Source Selector's\r
+                                     size will be passed to the caller by this parameter.\r
+\r
+  @retval EFI_INVALID_PARAMETER  If the Destination or Source Selector is NULL.\r
+  @retval EFI_BUFFER_TOO_SMALL   If the input Size is less than size of Source Selector.\r
+  @retval EFI_SUCCESS            Copy Source Selector to the Destination\r
+                                 Selector successfully.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IPSEC_DUPLICATE_SELECTOR) (\r
+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,\r
+  IN OUT UINTN                        *Size\r
+  );\r
+\r
+/**\r
+  It is prototype of compare two Selectors. The Selector would be SPD Selector,\r
+  or SAD Selector, or PAD selector.\r
+\r
+  @param[in]   Selector1           Pointer of the first  Selector.\r
+  @param[in]   Selector2           Pointer of the second Selector.\r
+\r
+  @retval  TRUE    These two Selectors have the same value in certain fields.\r
+  @retval  FALSE   Not all fields have the same value in these two Selectors.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(*IPSEC_COMPARE_SELECTOR) (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2\r
+  );\r
+\r
+/**\r
+  The prototype of a function to check if the Selector is Zero by its certain fields.\r
+\r
+  @param[in]  Selector      Pointer of the Selector.\r
+\r
+  @retval     TRUE          If the Selector is Zero.\r
+  @retval     FALSE         If the Selector is not Zero.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(*IPSEC_IS_ZERO_SELECTOR) (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector\r
+  );\r
+\r
+/**\r
+  The prototype of a function to fix the value of particular members of the Selector.\r
+\r
+  @param[in]  Selector              Pointer of Selector.\r
+  @param[in]  Data                  Pointer of Data.\r
+\r
+**/\r
+typedef\r
+VOID\r
+(*IPSEC_FIX_POLICY_ENTRY) (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR           *Selector,\r
+  IN VOID                                *Data\r
+  );\r
+\r
+/**\r
+  It is prototype function to define a routine function by the caller of IpSecVisitConfigData().\r
+\r
+  @param[in]      Type              A specified IPSEC_CONFIG_DATA_TYPE.\r
+  @param[in]      Selector          Points to EFI_IPSEC_CONFIG_SELECTOR to be copied\r
+                                    to the buffer.\r
+  @param[in]      Data              Points to data to be copied to the buffer. The\r
+                                    Data type is related to the Type.\r
+  @param[in]      SelectorSize      The size of the Selector.\r
+  @param[in]      DataSize          The size of the Data.\r
+  @param[in out]  Buffer            The buffer to store the Selector and Data.\r
+\r
+  @retval EFI_SUCCESS            Copied the Selector and Data to a buffer successfully.\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IPSEC_COPY_POLICY_ENTRY) (\r
+  IN     EFI_IPSEC_CONFIG_DATA_TYPE          Type,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR           *Selector,\r
+  IN     VOID                                *Data,\r
+  IN     UINTN                               SelectorSize,\r
+  IN     UINTN                               DataSize,\r
+  IN OUT VOID                                *Context\r
+  );\r
+\r
+/**\r
+  Set the security policy information for the EFI IPsec driver.\r
+\r
+  The IPsec configuration data has a unique selector/identifier separately to\r
+  identify a data entry.\r
+\r
+  @param[in]  Selector           Pointer to an entry selector on operated\r
+                                 configuration data specified by DataType.\r
+                                 A NULL Selector causes the entire specified-type\r
+                                 configuration information to be flushed.\r
+  @param[in]  Data               The data buffer to be set.\r
+  @param[in]  Context            Pointer to one entry selector that describes\r
+                                 the expected position the new data entry will\r
+                                 be added. If Context is NULL, the new entry will\r
+                                 be appended to the end of the database.\r
+\r
+  @retval EFI_INVALID_PARAMETER Certain Parameters are not correct. The Parameter\r
+                                requiring a check depends on the Selector type.\r
+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IPSEC_SET_POLICY_ENTRY) (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector,\r
+  IN VOID                             *Data,\r
+  IN VOID                             *Context OPTIONAL\r
+  );\r
+\r
+/**\r
+  A prototype function definition to lookup the data entry from IPsec. Return the configuration\r
+  value of the specified Entry.\r
+\r
+  @param[in]      Selector      Pointer to an entry selector that is an identifier\r
+                                of the  entry.\r
+  @param[in, out] DataSize      On output, the size of data returned in Data.\r
+  @param[out]     Data          The buffer to return the contents of the IPsec\r
+                                configuration data. The type of the data buffer\r
+                                is associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.\r
+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.\r
+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been\r
+                                updated with the size needed to complete the request.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IPSEC_GET_POLICY_ENTRY) (\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,\r
+  IN OUT UINTN                        *DataSize,\r
+  IN     VOID                         *Data\r
+  );\r
+\r
+/**\r
+  Compare two SPD Selectors.\r
+\r
+  Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/\r
+  NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the\r
+  Local Addresses and remote Addresses.\r
+\r
+  @param[in]   Selector1           Pointer of the first SPD Selector.\r
+  @param[in]   Selector2           Pointer of the second SPD Selector.\r
+\r
+  @retval  TRUE    These two Selectors have the same value in above fields.\r
+  @retval  FALSE   Not all of the above fields have the same value in these two Selectors.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSpdSelector (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2\r
+  );\r
+\r
+\r
+/**\r
+  Visit all IPsec Configurations of specified Type and call the caller defined\r
+  interface.\r
+\r
+  @param[in]  DataType          The specified IPsec Config Data Type.\r
+  @param[in]  Routine           The function caller defined.\r
+  @param[in]  Context           The data passed to the Routine.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS            This function complete successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecVisitConfigData (\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,\r
+  IN IPSEC_COPY_POLICY_ENTRY          Routine,\r
+  IN VOID                             *Context\r
+  );\r
+\r
+\r
+/**\r
+  This function is the subfunction of the EFIIpSecConfigSetData.\r
+\r
+  This function call IpSecSetVaraible to set the IPsec Configuration into the firmware.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS            Saved the configration successfully.\r
+  @retval Others                 Other errors were found while obtaining the variable.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigSave (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Initialize IPsecConfig protocol\r
+\r
+  @param[in, out]  Private   Pointer to IPSEC_PRIVATE_DATA. After this function finish,\r
+                             the pointer of IPsecConfig Protocol implementation will copy\r
+                             into its IPsecConfig member.\r
+\r
+  @retval     EFI_SUCCESS    Initialized the IPsecConfig Protocol successfully.\r
+  @retval     Others         Initializing the IPsecConfig Protocol failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigInitialize (\r
+  IN OUT IPSEC_PRIVATE_DATA               *Private\r
+  );\r
+\r
+/**\r
+  Calculate the entire size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed\r
+  by the pointer members.\r
+\r
+  @param[in]  SpdData             Pointer to a specified EFI_IPSEC_SPD_DATA.\r
+\r
+  @return The entire size of the specified EFI_IPSEC_SPD_DATA.\r
+\r
+**/\r
+UINTN\r
+IpSecGetSizeOfEfiSpdData (\r
+  IN EFI_IPSEC_SPD_DATA               *SpdData\r
+  );\r
+\r
+/**\r
+  Calculate the a entire size of IPSEC_SPD_DATA, which includes the buffer size pointed\r
+  by the pointer members and the buffer size used by Sa List.\r
+\r
+  @param[in]  SpdData       Pointer to the specified IPSEC_SPD_DATA.\r
+\r
+  @return The entire size of IPSEC_SPD_DATA.\r
+\r
+**/\r
+UINTN\r
+IpSecGetSizeOfSpdData (\r
+  IN IPSEC_SPD_DATA                   *SpdData\r
+  );\r
+\r
+/**\r
+  Copy Source Process Policy to the Destination Process Policy.\r
+\r
+  @param[in]  Dst                  Pointer to the Source Process Policy.\r
+  @param[in]  Src                  Pointer to the Destination Process Policy.\r
+\r
+**/\r
+VOID\r
+IpSecDuplicateProcessPolicy (\r
+  IN EFI_IPSEC_PROCESS_POLICY            *Dst,\r
+  IN EFI_IPSEC_PROCESS_POLICY            *Src\r
+  );\r
+\r
+/**\r
+  Compare two SPD Selectors.\r
+\r
+  Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/\r
+  NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the\r
+  Local Addresses and remote Addresses.\r
+\r
+  @param[in]   Selector1           Pointer of the first SPD Selector.\r
+  @param[in]   Selector2           Pointer of the second SPD Selector.\r
+\r
+  @retval  TRUE    This two Selector have the same value in above fields.\r
+  @retval  FALSE   Not all of the above fields have the same value in these two Selectors.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSpdSelector (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2\r
+  );\r
+\r
+/**\r
+  Compare two SA IDs.\r
+\r
+  @param[in]   Selector1           Pointer of the first SA ID.\r
+  @param[in]   Selector2           Pointer of the second SA ID.\r
+\r
+  @retval  TRUE    This two Selectors have the same SA ID.\r
+  @retval  FALSE   This two Selecotrs don't have the same SA ID.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSaId (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2\r
+  );\r
+\r
+/**\r
+  Compare two PAD IDs.\r
+\r
+  @param[in]   Selector1           Pointer of the first PAD ID.\r
+  @param[in]   Selector2           Pointer of the second PAD ID.\r
+\r
+  @retval  TRUE    This two Selectors have the same PAD ID.\r
+  @retval  FALSE   This two Selecotrs don't have the same PAD ID.\r
+\r
+**/\r
+BOOLEAN\r
+ComparePadId (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2\r
+  );\r
+\r
+/**\r
+  Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount\r
+  fields.\r
+\r
+  @param[in]  Selector      Pointer of the SPD Selector.\r
+\r
+  @retval     TRUE          If the SPD Selector is Zero.\r
+  @retval     FALSE         If the SPD Selector is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroSpdSelector (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector\r
+  );\r
+\r
+/**\r
+  Check if the SA ID is Zero by its DestAddress.\r
+\r
+  @param[in]  Selector      Pointer of the SA ID.\r
+\r
+  @retval     TRUE          If the SA ID is Zero.\r
+  @retval     FALSE         If the SA ID is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroSaId (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector\r
+  );\r
+\r
+/**\r
+  Check if the PAD ID is Zero.\r
+\r
+  @param[in]  Selector      Pointer of the PAD ID.\r
+\r
+  @retval     TRUE          If the PAD ID is Zero.\r
+  @retval     FALSE         If the PAD ID is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroPadId (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector\r
+  );\r
+\r
+/**\r
+  Copy Source SPD Selector to the Destination SPD Selector.\r
+\r
+  @param[in, out] DstSel             Pointer of Destination SPD Selector.\r
+  @param[in]      SrcSel             Pointer of Source SPD Selector.\r
+  @param[in, out] Size               The size of the Destination SPD Selector. If\r
+                                     it is not NULL and its value is less than the\r
+                                     size of Source SPD Selector, the value of\r
+                                     Source SPD Selector's size will be passed to\r
+                                     the caller by this parameter.\r
+\r
+  @retval EFI_INVALID_PARAMETER  If the Destination or Source SPD Selector is NULL.\r
+  @retval EFI_BUFFER_TOO_SMALL   If the input Size is less than size of Source SPD Selector.\r
+  @retval EFI_SUCCESS            Copy Source SPD Selector to the Destination SPD\r
+                                 Selector successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicateSpdSelector (\r
+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,\r
+  IN OUT UINTN                        *Size\r
+  );\r
+\r
+/**\r
+  Copy Source SA ID to the Destination SA ID.\r
+\r
+  @param[in, out] DstSel             Pointer of the Destination SA ID.\r
+  @param[in]      SrcSel             Pointer of the Source SA ID.\r
+  @param[in, out] Size               The size of the Destination SA ID. If it\r
+                                     not NULL, and its value is less than the size of\r
+                                     Source SA ID, the value of Source SA ID's size\r
+                                     will be passed to the caller by this parameter.\r
+\r
+  @retval EFI_INVALID_PARAMETER  If the Destination or Source SA ID is NULL.\r
+  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source SA ID.\r
+  @retval EFI_SUCCESS            Copied Source SA ID to the Destination SA ID successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicateSaId (\r
+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,\r
+  IN OUT UINTN                        *Size\r
+  );\r
+\r
+/**\r
+  Copy Source PAD ID to the Destination PAD ID.\r
+\r
+  @param[in, out] DstSel             Pointer of Destination PAD ID.\r
+  @param[in]      SrcSel             Pointer of Source PAD ID.\r
+  @param[in, out] Size               The size of the Destination PAD ID. If it\r
+                                     not NULL, and its value less than the size of\r
+                                     Source PAD ID, the value of Source PAD ID's size\r
+                                     will be passed to the caller by this parameter.\r
+\r
+  @retval EFI_INVALID_PARAMETER  If the Destination or Source PAD ID is NULL.\r
+  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source PAD ID.\r
+  @retval EFI_SUCCESS            Copied Source PAD ID to the Destination PAD ID successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicatePadId (\r
+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,\r
+  IN OUT UINTN                        *Size\r
+  );\r
+\r
+/**\r
+  Fix the value of some members of the  SPD Selector.\r
+\r
+  This function is called by IpSecCopyPolicyEntry(), which copies the Policy\r
+  Entry into the Variable. Since some members in SPD Selector are pointers,\r
+  a physical address to relative address conversion is required before copying\r
+  this SPD entry into the variable.\r
+\r
+  @param[in]       Selector              Pointer of SPD Selector.\r
+  @param[in, out]  Data                  Pointer of SPD Data.\r
+\r
+**/\r
+VOID\r
+FixSpdEntry (\r
+  IN     EFI_IPSEC_SPD_SELECTOR            *Selector,\r
+  IN OUT EFI_IPSEC_SPD_DATA                *Data\r
+  );\r
+\r
+/**\r
+  Fix the value of some members of SA ID.\r
+\r
+  This function is called by IpSecCopyPolicyEntry(), which copies the Policy\r
+  Entry into the Variable. Since some members in SA ID are pointers,\r
+  a physical address to relative address conversion is required before copying\r
+  this SAD into the variable.\r
+\r
+  @param[in]       SaId              Pointer of SA ID.\r
+  @param[in, out]  Data              Pointer of SA Data.\r
+\r
+**/\r
+VOID\r
+FixSadEntry (\r
+  IN     EFI_IPSEC_SA_ID                  *SaId,\r
+  IN OUT EFI_IPSEC_SA_DATA                *Data\r
+  );\r
+\r
+/**\r
+  Fix the value of some members of PAD ID.\r
+\r
+  This function is called by IpSecCopyPolicyEntry(), which copy the Policy\r
+  Entry into the Variable. Since some members in PAD ID are pointers,\r
+  a physical address to relative address conversion is required before copying\r
+  this PAD into the variable.\r
+\r
+  @param[in]       PadId              Pointer of PAD ID.\r
+  @param[in, out]  Data               Pointer of PAD Data.\r
+\r
+**/\r
+VOID\r
+FixPadEntry (\r
+  IN     EFI_IPSEC_PAD_ID                  *PadId,\r
+  IN OUT EFI_IPSEC_PAD_DATA                *Data\r
+  );\r
+\r
+/**\r
+  Recover the value of some members of SPD Selector.\r
+\r
+  This function is corresponding to FixSpdEntry(). It recovers the value of members\r
+  of SPD Selector which fix by the FixSpdEntry().\r
+\r
+  @param[in, out]  Selector              Pointer of SPD Selector.\r
+  @param[in, out]  Data                  Pointer of SPD Data.\r
+\r
+**/\r
+VOID\r
+UnfixSpdEntry (\r
+  IN OUT EFI_IPSEC_SPD_SELECTOR           *Selector,\r
+  IN OUT EFI_IPSEC_SPD_DATA               *Data\r
+  );\r
+\r
+\r
+/**\r
+  Recover the value of some members of SA ID.\r
+\r
+  This function is corresponding to FixSadEntry(). It recovers the value of members\r
+  of SAD ID which fix by the FixSadEntry().\r
+\r
+  @param[in, out]       SaId              Pointer of SAD ID\r
+  @param[in, out]  Data              Pointer of SAD Data.\r
+\r
+**/\r
+VOID\r
+UnfixSadEntry (\r
+  IN OUT EFI_IPSEC_SA_ID                     *SaId,\r
+  IN OUT EFI_IPSEC_SA_DATA                   *Data\r
+  );\r
+\r
+/**\r
+  Recover the value of some members of PAD ID.\r
+\r
+  This function is corresponding to FixPadEntry(). It recovers the value of members\r
+  of PAD ID which fix by the FixPadEntry().\r
+\r
+  @param[in]       PadId              Pointer of PAD ID\r
+  @param[in, out]  Data               Pointer of PAD Data.\r
+\r
+**/\r
+VOID\r
+UnfixPadEntry (\r
+  IN     EFI_IPSEC_PAD_ID                 *PadId,\r
+  IN OUT EFI_IPSEC_PAD_DATA               *Data\r
+  );\r
+\r
+/**\r
+  Set the security policy information for the EFI IPsec driver.\r
+\r
+  The IPsec configuration data has a unique selector/identifier separately to\r
+  identify a data entry.\r
+\r
+  @param[in]  Selector           Pointer to an entry selector on operated\r
+                                 configuration data specified by DataType.\r
+                                 A NULL Selector causes the entire specified-type\r
+                                 configuration information to be flushed.\r
+  @param[in]  Data               The data buffer to be set. The structure\r
+                                 of the data buffer should be EFI_IPSEC_SPD_DATA.\r
+  @param[in]  Context            Pointer to one entry selector that describes\r
+                                 the expected position the new data entry will\r
+                                 be added. If Context is NULL,the new entry will\r
+                                 be appended the end of database.\r
+\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                   - Selector is not NULL and its LocalAddress\r
+                                     is NULL or its RemoteAddress is NULL.\r
+                                   - Data is not NULL, its Action is Protected,\r
+                                     and its policy is NULL.\r
+                                   - Data is not NULL and its Action is not protected\r
+                                     and its policy is not NULL.\r
+                                   - The Action of Data is Protected, its policy\r
+                                     mode is Tunnel, and its tunnel option is NULL.\r
+                                   - The Action of Data is protected, its policy\r
+                                     mode is not Tunnel, and it tunnel option is not NULL.\r
+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetSpdEntry (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,\r
+  IN VOID                            *Data,\r
+  IN VOID                            *Context OPTIONAL\r
+  );\r
+\r
+/**\r
+  Set the security association information for the EFI IPsec driver.\r
+\r
+  The IPsec configuration data has a unique selector/identifier separately to\r
+  identify a data entry.\r
+\r
+  @param[in]  Selector           Pointer to an entry selector on operated\r
+                                 configuration data specified by DataType.\r
+                                 A NULL Selector causes the entire specified-type\r
+                                 configuration information to be flushed.\r
+  @param[in]  Data               The data buffer to be set. The structure\r
+                                 of the data buffer should be EFI_IPSEC_SA_DATA.\r
+  @param[in]  Context            Pointer to one entry selector which describes\r
+                                 the expected position the new data entry will\r
+                                 be added. If Context is NULL,the new entry will\r
+                                 be appended to the end of database.\r
+\r
+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetSadEntry (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,\r
+  IN VOID                            *Data,\r
+  IN VOID                            *Context OPTIONAL\r
+  );\r
+\r
+/**\r
+  Set the peer authorization configuration information for the EFI IPsec driver.\r
+\r
+  The IPsec configuration data has a unique selector/identifier separately to\r
+  identify a data entry.\r
+\r
+  @param[in]  Selector           Pointer to an entry selector on operated\r
+                                 configuration data specified by DataType.\r
+                                 A NULL Selector causes the entire specified-type\r
+                                 configuration information to be flushed.\r
+  @param[in]  Data               The data buffer to be set. The structure\r
+                                 of the data buffer should be EFI_IPSEC_PAD_DATA.\r
+  @param[in]  Context            Pointer to one entry selector that describes\r
+                                 the expected position where the new data entry will\r
+                                 be added. If Context is NULL, the new entry will\r
+                                 be appended the end of database.\r
+\r
+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetPadEntry (\r
+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,\r
+  IN VOID                            *Data,\r
+  IN VOID                            *Context OPTIONAL\r
+  );\r
+\r
+/**\r
+  This function looks up the data entry from IPsec SPD, and returns the configuration\r
+  value of the specified SPD Entry.\r
+\r
+  @param[in]      Selector      Pointer to an entry selector which is an identifier\r
+                                of the SPD entry.\r
+  @param[in, out] DataSize      On output the size of data returned in Data.\r
+  @param[out]     Data          The buffer to return the contents of the IPsec\r
+                                configuration data. The type of the data buffer\r
+                                is associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.\r
+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.\r
+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been\r
+                                updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetSpdEntry (\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,\r
+  IN OUT UINTN                        *DataSize,\r
+     OUT VOID                         *Data\r
+  );\r
+\r
+/**\r
+  This function looks up the data entry from IPsec SAD and returns the configuration\r
+  value of the specified SAD Entry.\r
+\r
+  @param[in]      Selector      Pointer to an entry selector that is an identifier\r
+                                of the SAD entry.\r
+  @param[in, out] DataSize      On output, the size of data returned in Data.\r
+  @param[out]     Data          The buffer to return the contents of the IPsec\r
+                                configuration data. This type of the data buffer\r
+                                is associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.\r
+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been\r
+                                updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetSadEntry (\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR   *Selector,\r
+  IN OUT UINTN                       *DataSize,\r
+     OUT VOID                        *Data\r
+  );\r
+\r
+/**\r
+  This function looks up the data entry from IPsec PADand returns the configuration\r
+  value of the specified PAD Entry.\r
+\r
+  @param[in]      Selector      Pointer to an entry selector that  is an identifier\r
+                                of the PAD entry.\r
+  @param[in, out] DataSize      On output the size of data returned in Data.\r
+  @param[out]     Data          The buffer to return the contents of the IPsec\r
+                                configuration data. This type of the data buffer\r
+                                is associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.\r
+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been\r
+                                updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetPadEntry (\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR   *Selector,\r
+  IN OUT UINTN                       *DataSize,\r
+     OUT VOID                        *Data\r
+  );\r
+\r
+/**\r
+  Return the configuration value for the EFI IPsec driver.\r
+\r
+  This function lookup the data entry from IPsec database or IKEv2 configuration\r
+  information. The expected data type and unique identification are described in\r
+  DataType and Selector parameters.\r
+\r
+  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in]      DataType      The type of data to retrieve.\r
+  @param[in]      Selector      Pointer to an entry selector that is an identifier of the IPsec\r
+                                configuration data entry.\r
+  @param[in, out] DataSize      On output the size of data returned in Data.\r
+  @param[out]     Data          The buffer to return the contents of the IPsec configuration data.\r
+                                The type of the data buffer is associated with the DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+                                - This is NULL.\r
+                                - Selector is NULL.\r
+                                - DataSize is NULL.\r
+                                - Data is NULL and *DataSize is not zero\r
+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.\r
+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.\r
+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been\r
+                                updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigGetData (\r
+  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,\r
+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,\r
+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,\r
+  IN OUT UINTN                        *DataSize,\r
+     OUT VOID                         *Data\r
+  );\r
+\r
+/**\r
+  Set the security association, security policy and peer authorization configuration\r
+  information for the EFI IPsec driver.\r
+\r
+  This function is used to set the IPsec configuration information of type DataType for\r
+  the EFI IPsec driver.\r
+  The IPsec configuration data has a unique selector/identifier separately to identify\r
+  a data entry. The selector structure depends on DataType's definition.\r
+  Using SetData() with a Data of NULL causes the IPsec configuration data entry identified\r
+  by DataType and Selector to be deleted.\r
+\r
+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in] DataType           The type of data to be set.\r
+  @param[in] Selector           Pointer to an entry selector on operated configuration data\r
+                                specified by DataType. A NULL Selector causes the entire\r
+                                specified-type configuration information to be flushed.\r
+  @param[in] Data               The data buffer to be set. The structure of the data buffer is\r
+                                associated with the DataType.\r
+  @param[in] InsertBefore       Pointer to one entry selector which describes the expected\r
+                                position the new data entry will be added. If InsertBefore is NULL,\r
+                                the new entry will be appended the end of database.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration entry data was set successfully.\r
+  @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+                                - This is NULL.\r
+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.\r
+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigSetData (\r
+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector,\r
+  IN VOID                             *Data,\r
+  IN EFI_IPSEC_CONFIG_SELECTOR        *InsertBefore OPTIONAL\r
+  );\r
+\r
+/**\r
+  Enumerates the current selector for IPsec configuration data entry.\r
+\r
+  This function is called multiple times to retrieve the entry Selector in IPsec\r
+  configuration database. On each call to GetNextSelector(), the next entry\r
+  Selector are retrieved into the output interface.\r
+\r
+  If the entire IPsec configuration database has been iterated, the error\r
+  EFI_NOT_FOUND is returned.\r
+  If the Selector buffer is too small for the next Selector copy, an\r
+  EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect\r
+  the size of buffer needed.\r
+\r
+  On the initial call to GetNextSelector() to start the IPsec configuration database\r
+  search, a pointer to the buffer with all zero value is passed in Selector. Calls\r
+  to SetData() between calls to GetNextSelector may produce unpredictable results.\r
+\r
+  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in]      DataType      The type of IPsec configuration data to retrieve.\r
+  @param[in, out] SelectorSize  The size of the Selector buffer.\r
+  @param[in, out] Selector      On input, supplies the pointer to last Selector that was\r
+                                returned by GetNextSelector().\r
+                                On output, returns one copy of the current entry Selector\r
+                                of a given DataType.\r
+\r
+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.\r
+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+                                - This is NULL.\r
+                                - SelectorSize is NULL.\r
+                                - Selector is NULL.\r
+  @retval EFI_NOT_FOUND         The next configuration data entry was not found.\r
+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.\r
+  @retval EFI_BUFFER_TOO_SMALL  The SelectorSize is too small for the result. This parameter\r
+                                has been updated with the size needed to complete the search\r
+                                request.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigGetNextSelector (\r
+  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,\r
+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,\r
+  IN OUT UINTN                        *SelectorSize,\r
+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *Selector\r
+  );\r
+\r
+/**\r
+  Register an event that is to be signaled whenever a configuration process on the\r
+  specified IPsec configuration information is done.\r
+\r
+  The register function is not surpport now and always returns EFI_UNSUPPORTED.\r
+\r
+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in] DataType           The type of data to be registered the event for.\r
+  @param[in] Event              The event to be registered.\r
+\r
+  @retval EFI_SUCCESS           The event is registered successfully.\r
+  @retval EFI_INVALID_PARAMETER This is NULL, or Event is NULL.\r
+  @retval EFI_ACCESS_DENIED     The Event is already registered for the DataType.\r
+  @retval EFI_UNSUPPORTED       The notify registration unsupported, or the specified\r
+                                DataType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigRegisterNotify (\r
+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,\r
+  IN EFI_EVENT                        Event\r
+  );\r
+\r
+\r
+/**\r
+  Remove the specified event that was previously registered on the specified IPsec\r
+  configuration data.\r
+\r
+  This function is not supported now and always returns EFI_UNSUPPORTED.\r
+\r
+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+  @param[in] DataType           The configuration data type to remove the registered event for.\r
+  @param[in] Event              The event to be unregistered.\r
+\r
+  @retval EFI_SUCCESS           The event was removed successfully.\r
+  @retval EFI_NOT_FOUND         The Event specified by DataType could not be found in the\r
+                                database.\r
+  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.\r
+  @retval EFI_UNSUPPORTED       The notify registration unsupported or the specified\r
+                                DataType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigUnregisterNotify (\r
+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,\r
+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,\r
+  IN EFI_EVENT                        Event\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IpSecDxe/IpSecCryptIo.c b/NetworkPkg/IpSecDxe/IpSecCryptIo.c
new file mode 100644 (file)
index 0000000..7011f98
--- /dev/null
@@ -0,0 +1,133 @@
+/** @file\r
+  Common operation for Security.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecCryptIo.h"\r
+//\r
+// Alogrithm's informations for the Encrypt/Decrpt Alogrithm.\r
+//\r
+ENCRYPT_ALGORITHM mIpsecEncryptAlgorithmList[IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE] = {\r
+  {EFI_IPSEC_EALG_NULL, 0, 0, 1, NULL, NULL, NULL, NULL},\r
+  {(UINT8)-1,           0, 0, 0, NULL, NULL, NULL, NULL}\r
+};\r
+//\r
+// Alogrithm's informations for the Authentication algorithm\r
+//\r
+AUTH_ALGORITHM mIpsecAuthAlgorithmList[IPSEC_AUTH_ALGORITHM_LIST_SIZE] = {\r
+  {EFI_IPSEC_AALG_NONE, 0, 0, 0, NULL, NULL, NULL, NULL},\r
+  {EFI_IPSEC_AALG_NULL, 0, 0, 0, NULL, NULL, NULL, NULL},\r
+  {(UINT8)-1,           0, 0, 0, NULL, NULL, NULL, NULL}\r
+};\r
+\r
+\r
+/**\r
+  Get the block size of encrypt alogrithm. The block size is based on the algorithm used.\r
+\r
+  @param[in]  AlgorithmId          The encrypt algorithm ID.\r
+\r
+  @return The value of block size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetEncryptBlockSize (\r
+  IN UINT8   AlgorithmId\r
+  )\r
+{\r
+  UINT8 Index;\r
+\r
+  for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) {\r
+    if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) {\r
+      //\r
+      // The BlockSize is same with IvSize.\r
+      //\r
+      return mIpsecEncryptAlgorithmList[Index].BlockSize;\r
+    }\r
+  }\r
+\r
+  return (UINTN) -1;\r
+}\r
+\r
+/**\r
+  Get the IV size of encrypt alogrithm. The IV size is based on the algorithm used.\r
+\r
+  @param[in]  AlgorithmId          The encrypt algorithm ID.\r
+\r
+  @return The value of IV size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetEncryptIvLength (\r
+  IN UINT8 AlgorithmId\r
+  )\r
+{\r
+  UINT8 Index;\r
+\r
+  for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) {\r
+    if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) {\r
+      //\r
+      // The BlockSize is same with IvSize.\r
+      //\r
+      return mIpsecEncryptAlgorithmList[Index].IvLength;\r
+    }\r
+  }\r
+\r
+  return (UINTN) -1;\r
+}\r
+\r
+/**\r
+  Get the ICV size of Authenticaion alogrithm. The ICV size is based on the algorithm used.\r
+\r
+  @param[in]  AuthAlgorithmId          The Authentication algorithm ID.\r
+\r
+  @return The value of ICV size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetIcvLength (\r
+  IN UINT8  AuthAlgorithmId\r
+  )\r
+{\r
+  UINT8 Index;\r
+  for (Index = 0; Index < IPSEC_AUTH_ALGORITHM_LIST_SIZE; Index++) {\r
+    if (AuthAlgorithmId == mIpsecAuthAlgorithmList[Index].AlgorithmId) {\r
+      return mIpsecAuthAlgorithmList[Index].IcvLength;\r
+    }\r
+  }\r
+  return (UINTN) -1;\r
+}\r
+\r
+/**\r
+  Generate a random data for IV. If the IvSize is zero, not needed to create\r
+  IV and return EFI_SUCCESS.\r
+\r
+  @param[in]  IvBuffer  The pointer of the IV buffer.\r
+  @param[in]  IvSize    The IV size.\r
+\r
+  @retval     EFI_SUCCESS  Create a random data for IV.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecGenerateIv (\r
+  IN UINT8                           *IvBuffer,\r
+  IN UINTN                           IvSize\r
+  )\r
+{\r
+  if (IvSize != 0) {\r
+    //\r
+    //TODO: return CryptGenerateRandom (IvBuffer, IvSize);\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/NetworkPkg/IpSecDxe/IpSecCryptIo.h b/NetworkPkg/IpSecDxe/IpSecCryptIo.h
new file mode 100644 (file)
index 0000000..d883a2e
--- /dev/null
@@ -0,0 +1,322 @@
+/** @file\r
+  Definition related to the Security operation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _EFI_IPSEC_CRYPTIO_H_\r
+#define _EFI_IPSEC_CRYPTIO_H_\r
+\r
+#include <Protocol/IpSecConfig.h>\r
+#include <Library/DebugLib.h>\r
+\r
+#define IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE 2\r
+#define IPSEC_AUTH_ALGORITHM_LIST_SIZE    3\r
+\r
+/**\r
+  Prototype of Hash GetContextSize.\r
+\r
+  Retrieves the size, in bytes, of the context buffer required.\r
+\r
+  @return  The size, in bytes, of the context buffer required.\r
+\r
+**/\r
+typedef\r
+UINTN\r
+(EFIAPI *CPL_HASH_GETCONTEXTSIZE) (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Prototype of Hash Operation Initiating.\r
+\r
+  Initialization with a new context.\r
+\r
+\r
+  @param[in,out]  Context  Input Context.\r
+\r
+  @retval TRUE  Initialization Successfully.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *CPL_HASH_INIT) (\r
+  IN OUT  VOID     *Context\r
+  );\r
+\r
+/**\r
+  Prototype of HASH update.\r
+  Hash update operation. Continue an Hash message digest operation, processing\r
+  another message block, and updating the Hash context.\r
+\r
+  If Context is NULL, then ASSERT().\r
+  If Data is NULL, then ASSERT().\r
+\r
+  @param[in,out]  Context     The Specified Context.\r
+  @param[in,out]  Data        The Input Data to hash.\r
+  @param[in]      DataLength  The length, in bytes, of Data.\r
+\r
+  @retval TRUE   Update data successfully.\r
+  @retval FALSE  The Context has been finalized.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_HASH_UPDATE) (\r
+  IN OUT       VOID  *Context,\r
+  IN     CONST VOID  *Data,\r
+  IN           UINTN DataLength\r
+  );\r
+\r
+/**\r
+  Prototype of Hash finallization.\r
+  Terminate a Hash message digest operation and output the message digest.\r
+\r
+  If Context is NULL, then ASSERT().\r
+  If HashValue is NULL, then ASSERT().\r
+\r
+  @param[in,out]  Context     The specified Context.\r
+  @param[out]     HashValue   Pointer to a 16-byte message digest output buffer.\r
+\r
+  @retval TRUE  Finalized successfully.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_HASH_FINAL) (\r
+  IN OUT  VOID   *Context,\r
+     OUT  UINT8  *HashValue\r
+  );\r
+\r
+/**\r
+  Prototype of Cipher GetContextSize.\r
+\r
+  Retrieves the size, in bytes, of the context buffer required.\r
+\r
+  @return  The size, in bytes, of the context buffer required.\r
+\r
+**/\r
+typedef\r
+UINTN\r
+(EFIAPI *CPL_CIPHER_GETCONTEXTSIZE) (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Prototype of Cipher initiation.\r
+  Intializes the user-supplied key as the specifed context (key materials) for both\r
+  encryption and decryption operations.\r
+\r
+  If Context is NULL, then ASSERT().\r
+  If Key is NULL, then generate random key for usage.\r
+\r
+  @param[in,out]  Context      The specified Context.\r
+  @param[in]      Key          User-supplied TDES key (64/128/192 bits).\r
+  @param[in]      KeyBits      Key length in bits.\r
+\r
+  @retval TRUE  TDES Initialization was successful.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_CIPHER_INIT) (\r
+  IN OUT        VOID   *Context,\r
+  IN      CONST UINT8  *Key,\r
+  IN      CONST UINTN  KeyBits\r
+  );\r
+\r
+\r
+/**\r
+  Prototype of Cipher encryption.\r
+  Encrypts plaintext message with the specified cipher.\r
+\r
+  If Context is NULL, then ASSERT().\r
+  if InData is NULL, then ASSERT().\r
+  If Size of input data is not multiple of Cipher algorithm related block size,\r
+  then ASSERT().\r
+\r
+  @param[in]      Context      The specified Context.\r
+  @param[in]      InData       The input plaintext data to be encrypted.\r
+  @param[out]     OutData      The resultant encrypted ciphertext.\r
+  @param[in]      DataLength   Length of input data in bytes.\r
+\r
+  @retval TRUE  Encryption successful.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_CIPHER_ENCRYPT) (\r
+  IN            VOID   *Context,\r
+  IN      CONST UINT8  *InData,\r
+      OUT       UINT8  *OutData,\r
+  IN      CONST UINTN  DataLength\r
+  );\r
+\r
+\r
+/**\r
+  Prototype of Cipher decryption.\r
+  Decrypts cipher message with specified cipher.\r
+\r
+  If Context is NULL, then ASSERT().\r
+  if InData is NULL, then ASSERT().\r
+  If Size of input data is not a multiple of a certaion block size , then ASSERT().\r
+\r
+  @param[in]      Context      The specified Context.\r
+  @param[in]      InData       The input ciphertext data to be decrypted.\r
+  @param[out]     OutData      The resultant decrypted plaintext.\r
+  @param[in]      DataLength   Length of input data in bytes.\r
+\r
+  @retval TRUE  Decryption successful.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_CIPHER_DECRYPT) (\r
+  IN     CONST VOID   *Context,\r
+  IN     CONST UINT8  *InData,\r
+     OUT       UINT8  *OutData,\r
+  IN     CONST UINTN  DataLength\r
+  );\r
+\r
+//\r
+// The struct used to store the informatino and operation of  Cipher algorithm.\r
+//\r
+typedef struct _ENCRYPT_ALGORITHM {\r
+//\r
+// The ID of the Algorithm\r
+//\r
+UINT8                     AlgorithmId;\r
+//\r
+// The Key length of the Algorithm\r
+//\r
+UINTN                     KeyLength;\r
+//\r
+// Iv Size of the Algorithm\r
+//\r
+UINTN                     IvLength;\r
+//\r
+// The Block Size of the Algorithm\r
+//\r
+UINTN                     BlockSize;\r
+//\r
+// The Function pointer of GetContextSize.\r
+//\r
+CPL_CIPHER_GETCONTEXTSIZE CipherGetContextSize;\r
+//\r
+// The Function pointer of Cipher intitiaion.\r
+//\r
+CPL_CIPHER_INIT           CipherInitiate;\r
+//\r
+// The Function pointer of Cipher Encryption.\r
+//\r
+CPL_CIPHER_ENCRYPT        CipherEncrypt;\r
+//\r
+// The Function pointer of Cipher Decrption.\r
+//\r
+CPL_CIPHER_DECRYPT        CipherDecrypt;\r
+} ENCRYPT_ALGORITHM;\r
+\r
+//\r
+// The struct used to store the informatino and operation of  Autahentication algorithm.\r
+//\r
+typedef struct _AUTH_ALGORITHM {\r
+  //\r
+  // ID of the Algorithm\r
+  //\r
+  UINT8                    AlgorithmId;\r
+  //\r
+  // The Key length of the Algorithm\r
+  //\r
+  UINTN                    KeyLength;\r
+  //\r
+  // The ICV length of the Algorithm\r
+  //\r
+  UINTN                    IcvLength;\r
+  //\r
+  // The block size of the Algorithm\r
+  //\r
+  UINTN                    BlockSize;\r
+  //\r
+  // The function pointer of GetContextSize.\r
+  //\r
+  CPL_HASH_GETCONTEXTSIZE  HashGetContextSize;\r
+  //\r
+  // The function pointer of Initiatoion\r
+  //\r
+  CPL_HASH_INIT            HashInitiate;\r
+  //\r
+  // The function pointer of Hash Update.\r
+  //\r
+  CPL_HASH_UPDATE          HashUpdate;\r
+  //\r
+  // The fucntion pointer of Hash Final\r
+  //\r
+  CPL_HASH_FINAL           HashFinal;\r
+} AUTH_ALGORITHM;\r
+\r
+/**\r
+  Get the IV size of encrypt alogrithm. IV size is different from different algorithm.\r
+\r
+  @param[in]  AlgorithmId          The encrypt algorithm ID.\r
+\r
+  @return The value of IV size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetEncryptIvLength (\r
+  IN UINT8 AlgorithmId\r
+  );\r
+\r
+/**\r
+  Get the block size of encrypt alogrithm. Block size is different from different algorithm.\r
+\r
+  @param[in]  AlgorithmId          The encrypt algorithm ID.\r
+\r
+  @return The value of block size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetEncryptBlockSize (\r
+  IN UINT8   AlgorithmId\r
+  );\r
+\r
+/**\r
+  Get the ICV size of Authenticaion alogrithm. ICV size is different from different algorithm.\r
+\r
+  @param[in]  AuthAlgorithmId          The Authentication algorithm ID.\r
+\r
+  @return The value of ICV size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetIcvLength (\r
+  IN UINT8  AuthAlgorithmId\r
+  );\r
+\r
+/**\r
+  Generate a random data for IV. If the IvSize is zero, not needed to create\r
+  IV and return EFI_SUCCESS.\r
+\r
+  @param[in]  IvBuffer  The pointer of the IV buffer.\r
+  @param[in]  IvSize    The IV size.\r
+\r
+  @retval     EFI_SUCCESS  Create random data for IV.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecGenerateIv (\r
+  IN UINT8                           *IvBuffer,\r
+  IN UINTN                           IvSize\r
+  );\r
+\r
+#endif\r
+\r
diff --git a/NetworkPkg/IpSecDxe/IpSecDebug.c b/NetworkPkg/IpSecDxe/IpSecDebug.c
new file mode 100644 (file)
index 0000000..8a5811b
--- /dev/null
@@ -0,0 +1,172 @@
+/** @file\r
+  Interface of IPsec printing debug information.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecImpl.h"\r
+#include "IpSecDebug.h"\r
+\r
+//\r
+// The print title for IKEv1 variety phase.\r
+//\r
+CHAR8 *mStateStr[] = {\r
+  "IKEv1_MAIN_1",\r
+  "IKEv1_MAIN_2",\r
+  "IKEv1_MAIN_3",\r
+  "IKEv1_MAIN_ESTABLISHED",\r
+  "IKEv1_QUICK_1",\r
+  "IKEv1_QUICK_2",\r
+  "IKEv1_QUICK_ESTABLISHED"\r
+};\r
+//\r
+// The print title for IKEv1 variety Exchagne.\r
+//\r
+CHAR8 *mExchangeStr[] = {\r
+  "IKEv1 Main Exchange",\r
+  "IKEv1 Info Exchange",\r
+  "IKEv1 Quick Exchange",\r
+  "IKEv1 Unknown Exchange"\r
+};\r
+\r
+//\r
+// The print title for IKEv1 variety Payload.\r
+//\r
+CHAR8 *mPayloadStr[] = {\r
+  "IKEv1 None Payload",\r
+  "IKEv1 SA Payload",\r
+  "IKEv1 Proposal Payload",\r
+  "IKEv1 Transform Payload",\r
+  "IKEv1 KE Payload",\r
+  "IKEv1 ID Payload",\r
+  "IKEv1 Certificate Payload",\r
+  "IKEv1 Certificate Request Payload",\r
+  "IKEv1 Hash Payload",\r
+  "IKEv1 Signature Payload",\r
+  "IKEv1 Nonce Payload",\r
+  "IKEv1 Notify Payload",\r
+  "IKEv1 Delete Payload",\r
+  "IKEv1 Vendor Payload"\r
+};\r
+\r
+/**\r
+  Print the IP address.\r
+\r
+  @param[in]  Level     Debug print error level. Pass to DEBUG().\r
+  @param[in]  Ip        Point to a specified IP address.\r
+  @param[in]  IpVersion The IP Version.\r
+\r
+**/\r
+VOID\r
+IpSecDumpAddress (\r
+  IN UINTN               Level,\r
+  IN EFI_IP_ADDRESS      *Ip,\r
+  IN UINT8               IpVersion\r
+  )\r
+{\r
+  if (IpVersion == IP_VERSION_6) {\r
+    DEBUG (\r
+      (Level,\r
+      "%x%x:%x%x:%x%x:%x%x",\r
+      Ip->v6.Addr[0],\r
+      Ip->v6.Addr[1],\r
+      Ip->v6.Addr[2],\r
+      Ip->v6.Addr[3],\r
+      Ip->v6.Addr[4],\r
+      Ip->v6.Addr[5],\r
+      Ip->v6.Addr[6],\r
+      Ip->v6.Addr[7])\r
+      );\r
+    DEBUG (\r
+      (Level,\r
+      ":%x%x:%x%x:%x%x:%x%x\n",\r
+      Ip->v6.Addr[8],\r
+      Ip->v6.Addr[9],\r
+      Ip->v6.Addr[10],\r
+      Ip->v6.Addr[11],\r
+      Ip->v6.Addr[12],\r
+      Ip->v6.Addr[13],\r
+      Ip->v6.Addr[14],\r
+      Ip->v6.Addr[15])\r
+      );\r
+  } else {\r
+    DEBUG (\r
+      (Level,\r
+      "%d.%d.%d.%d\n",\r
+      Ip->v4.Addr[0],\r
+      Ip->v4.Addr[1],\r
+      Ip->v4.Addr[2],\r
+      Ip->v4.Addr[3])\r
+      );\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Print IKEv1 Current states.\r
+\r
+  @param[in]  Previous    The Previous state of IKEv1.\r
+  @param[in]  Current     The current state of IKEv1.\r
+\r
+**/\r
+VOID\r
+IpSecDumpState (\r
+  IN UINT32              Previous,\r
+  IN UINT32              Current\r
+  )\r
+{\r
+  if (Previous == Current) {\r
+    DEBUG ((DEBUG_INFO, "\n****Current state is %a\n", mStateStr[Previous]));\r
+  } else {\r
+    DEBUG ((DEBUG_INFO, "\n****Change state from %a to %a\n", mStateStr[Previous], mStateStr[Current]));\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Print the buffer in form of Hex.\r
+\r
+  @param[in]  Title       The strings to be printed before the data of the buffer.\r
+  @param[in]  Data        Points to buffer to be printed.\r
+  @param[in]  DataSize    The size of the buffer to be printed.\r
+\r
+**/\r
+VOID\r
+IpSecDumpBuf (\r
+  IN CHAR8                 *Title,\r
+  IN UINT8                 *Data,\r
+  IN UINTN                 DataSize\r
+  )\r
+{\r
+  UINTN Index;\r
+  UINTN DataIndex;\r
+  UINTN BytesRemaining;\r
+  UINTN BytesToPrint;\r
+\r
+  DataIndex       = 0;\r
+  BytesRemaining  = DataSize;\r
+\r
+  DEBUG ((DEBUG_INFO, "==%a %d bytes==\n", Title, DataSize));\r
+\r
+  while (BytesRemaining > 0) {\r
+\r
+    BytesToPrint = (BytesRemaining > IPSEC_DEBUG_BYTE_PER_LINE) ? IPSEC_DEBUG_BYTE_PER_LINE : BytesRemaining;\r
+\r
+    for (Index = 0; Index < BytesToPrint; Index++) {\r
+      DEBUG ((DEBUG_INFO, " 0x%02x,", Data[DataIndex++]));\r
+    }\r
+\r
+    DEBUG ((DEBUG_INFO, "\n"));\r
+    BytesRemaining -= BytesToPrint;\r
+  }\r
+\r
+}\r
diff --git a/NetworkPkg/IpSecDxe/IpSecDebug.h b/NetworkPkg/IpSecDxe/IpSecDebug.h
new file mode 100644 (file)
index 0000000..0e6e681
--- /dev/null
@@ -0,0 +1,102 @@
+/** @file\r
+  The definition of functions and MACROs used for IPsec debug information print.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _EFI_IPSEC_DEBUG_H_\r
+#define _EFI_IPSEC_DEBUG_H_\r
+\r
+#include <Library/DebugLib.h>\r
+\r
+#define IPSEC_DUMP_ADDRESS(Level, Ip, Version)           IpSecDumpAddress (Level, Ip, Version)\r
+#define IPSEC_DUMP_STATE(Previous, Current)              IpSecDumpState (Previous, Current)\r
+#define IPSEC_DUMP_PACKET(Packet, Direction, IpVersion)  IpSecDumpPacket (Packet, Direction, IpVersion)\r
+#define IPSEC_DUMP_PAYLOAD(IkePayload)                   IpSecDumpPayload (IkePayload)\r
+#define IPSEC_DUMP_BUF(Title, Data, DataSize)            IpSecDumpBuf (Title, Data, DataSize)\r
+\r
+#define IPSEC_DEBUG_BYTE_PER_LINE                       8\r
+\r
+\r
+/**\r
+  Print the IP address.\r
+\r
+  @param[in]  Level     Debug print error level. Pass to DEBUG().\r
+  @param[in]  Ip        Point to specified IP address.\r
+  @param[in]  IpVersion The IP Version.\r
+\r
+**/\r
+VOID\r
+IpSecDumpAddress (\r
+  IN UINTN               Level,\r
+  IN EFI_IP_ADDRESS      *Ip,\r
+  IN UINT8               IpVersion\r
+  );\r
+\r
+/**\r
+  Print IKEv1 Current states.\r
+\r
+  @param[in]  Previous    The Previous state of IKEv1.\r
+  @param[in]  Current     The current state of IKEv1.\r
+\r
+**/\r
+VOID\r
+IpSecDumpState (\r
+  IN UINT32              Previous,\r
+  IN UINT32              Current\r
+  );\r
+\r
+/**\r
+  Print the Ike Packet.\r
+\r
+  @param[in]  Packet      Point to IKE packet to be printed.\r
+  @param[in]  Direction   Point to the IKE packet is inbound or outbound.\r
+  @param[in]  IpVersion   Specified IP Version.\r
+\r
+**/\r
+/*\r
+VOID\r
+IpSecDumpPacket (\r
+  IN IKE_PACKET            *Packet,\r
+  IN EFI_IPSEC_TRAFFIC_DIR Direction,\r
+  IN UINT8                 IpVersion\r
+  );\r
+*/\r
+\r
+/**\r
+  Print the IKE Paylolad.\r
+\r
+  @param[in]  IkePayload  Points to the payload to be printed.\r
+\r
+**/\r
+/*\r
+VOID\r
+IpSecDumpPayload (\r
+  IN IKE_PAYLOAD           *IkePayload\r
+  );\r
+*/\r
+/**\r
+  Print the buffer in form of Hex.\r
+\r
+  @param[in]  Title       The strings to be printed before the data of the buffer.\r
+  @param[in]  Data        Points to the buffer to be printed.\r
+  @param[in]  DataSize    The size of the buffer to be printed.\r
+\r
+**/\r
+VOID\r
+IpSecDumpBuf (\r
+  IN CHAR8                 *Title,\r
+  IN UINT8                 *Data,\r
+  IN UINTN                 DataSize\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IpSecDxe/IpSecDriver.c b/NetworkPkg/IpSecDxe/IpSecDriver.c
new file mode 100644 (file)
index 0000000..b38f2a9
--- /dev/null
@@ -0,0 +1,282 @@
+/** @file\r
+  Driver Binding Protocol for IPsec Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include <Library/UdpIoLib.h>\r
+#include "IpSecConfigImpl.h"\r
+#include "IpSecDebug.h"\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle.\r
+\r
+  @param[in]  This                 Protocol instance pointer.\r
+  @param[in]  ControllerHandle     Handle of device to test.\r
+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child\r
+                                   device to start.\r
+\r
+  @retval EFI_SUCCES           This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on this device.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL\r
+  )\r
+{\r
+  //\r
+  //TODO: Add Udp4Protocol and Udp6Protocol testing.\r
+  //\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  @param[in]  This                 Protocol instance pointer.\r
+  @param[in]  ControllerHandle     Handle of device to bind driver to.\r
+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child\r
+                                   device to start.\r
+\r
+  @retval EFI_SUCCES           This driver is added to ControllerHandle\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle\r
+  @retval EFI_DEVICE_ERROR     The device could not be started due to a device error.\r
+                               Currently not implemented.\r
+  @retval other                This driver does not support this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  //\r
+  //TODO: Add Udp4Io and Udp6Io creation for the IKE.\r
+  //\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  @param[in]  This                 Protocol instance pointer.\r
+  @param[in]  ControllerHandle     Handle of a device to stop the driver on.\r
+  @param[in]  NumberOfChildren     Number of Handles in ChildHandleBuffer. If the number of\r
+                                   children is zero, stop the entire bus driver.\r
+  @param[in]  ChildHandleBuffer    List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCES           This driver removed ControllerHandle.\r
+  @retval other                This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecDriverBindingStop (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN UINTN                        NumberOfChildren,\r
+  IN EFI_HANDLE                   *ChildHandleBuffer\r
+  )\r
+{\r
+  //\r
+  //TODO: Add UdpIo4 and UdpIo6 destruction when the Udp driver unload or stop.\r
+  //\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gIpSecDriverBinding = {\r
+  IpSecDriverBindingSupported,\r
+  IpSecDriverBindingStart,\r
+  IpSecDriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+/**\r
+  This is a callback function when the mIpSecInstance.DisabledEvent is signaled.\r
+\r
+  @param[in]  Event        Event whose notification function is being invoked.\r
+  @param[in]  Context      Pointer to the notification function's context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IpSecCleanupAllSa (\r
+  IN  EFI_EVENT     Event,\r
+  IN  VOID          *Context\r
+  )\r
+{\r
+  IPSEC_PRIVATE_DATA  *Private;\r
+  UINT8               Value;\r
+  EFI_STATUS          Status;\r
+\r
+  Private = (IPSEC_PRIVATE_DATA *) Context;\r
+\r
+  //\r
+  // Set the Status Variable\r
+  //\r
+  Value  = IPSEC_STATUS_DISABLED;\r
+  Status = gRT->SetVariable (\r
+                  IPSECCONFIG_STATUS_NAME,\r
+                  &gEfiIpSecConfigProtocolGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                  sizeof (Value),\r
+                  &Value\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    Private->IpSec.DisabledFlag = TRUE;\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  This is the declaration of an EFI image entry point. This entry point is\r
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+  both device drivers and bus drivers.\r
+\r
+  The entry point for IPsec driver which installs the driver binding,\r
+  component name protocol, IPsec Config protcolon, and IPsec protocol in\r
+  its ImageHandle.\r
+\r
+  @param[in] ImageHandle        The firmware allocated handle for the UEFI image.\r
+  @param[in] SystemTable        A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval EFI_ALREADY_STARTED   The IPsec driver has been already loaded.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+  @retval Others                The operation is failed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecDriverEntryPoint (\r
+  IN EFI_HANDLE              ImageHandle,\r
+  IN EFI_SYSTEM_TABLE        *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  IPSEC_PRIVATE_DATA  *Private;\r
+  EFI_IPSEC_PROTOCOL  *IpSec;\r
+\r
+  //\r
+  // Check whether ipsec protocol has already been installed.\r
+  //\r
+  Status = gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &IpSec);\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_WARN, "_ModuleEntryPoint: IpSec has been already loaded\n"));\r
+    Status = EFI_ALREADY_STARTED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, (VOID **) &mDpc);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to locate EfiDpcProtocol\n"));\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Private = AllocateZeroPool (sizeof (IPSEC_PRIVATE_DATA));\r
+\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to allocate private data\n"));\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Create disable event to cleanup all sa when ipsec disabled by user.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  IpSecCleanupAllSa,\r
+                  Private,\r
+                  &mIpSecInstance.DisabledEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to create disable event\n"));\r
+    goto ON_FREE_PRIVATE;\r
+  }\r
+\r
+  Private->Signature    = IPSEC_PRIVATE_DATA_SIGNATURE;\r
+  Private->ImageHandle  = ImageHandle;\r
+  CopyMem (&Private->IpSec, &mIpSecInstance, sizeof (EFI_IPSEC_PROTOCOL));\r
+\r
+  //\r
+  // Initilize Private's members. Thess members is used for IKE.\r
+  //\r
+  InitializeListHead (&Private->Udp4List);\r
+  InitializeListHead (&Private->Udp6List);\r
+  InitializeListHead (&Private->Ikev1SessionList);\r
+  InitializeListHead (&Private->Ikev1EstablishedList);\r
+  InitializeListHead (&Private->Ikev2SessionList);\r
+  InitializeListHead (&Private->Ikev2EstablishedList);\r
+\r
+  //\r
+  // Initialize the ipsec config data and restore it from variable.\r
+  //\r
+  Status = IpSecConfigInitialize (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to initialize IpSecConfig\n"));\r
+    goto ON_CLOSE_EVENT;\r
+  }\r
+  //\r
+  // Install ipsec protocol which is used by ip driver to process ipsec header.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Private->Handle,\r
+                  &gEfiIpSecProtocolGuid,\r
+                  &Private->IpSec,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_UNINSTALL_CONFIG;\r
+  }\r
+\r
+  Status = EfiLibInstallDriverBindingComponentName2 (\r
+             ImageHandle,\r
+             SystemTable,\r
+             &gIpSecDriverBinding,\r
+             ImageHandle,\r
+             &gIpSecComponentName,\r
+             &gIpSecComponentName2\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_UNINSTALL_CONFIG;\r
+  }\r
+\r
+  return Status;\r
+\r
+ON_UNINSTALL_CONFIG:\r
+  gBS->UninstallProtocolInterface (\r
+        Private->Handle,\r
+        &gEfiIpSecConfigProtocolGuid,\r
+        &Private->IpSecConfig\r
+        );\r
+ON_CLOSE_EVENT:\r
+  gBS->CloseEvent (mIpSecInstance.DisabledEvent);\r
+  mIpSecInstance.DisabledEvent = NULL;\r
+ON_FREE_PRIVATE:\r
+  FreePool (Private);\r
+ON_EXIT:\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/IpSecDxe/IpSecDxe.inf b/NetworkPkg/IpSecDxe/IpSecDxe.inf
new file mode 100644 (file)
index 0000000..250ef1c
--- /dev/null
@@ -0,0 +1,63 @@
+## @file\r
+#  Component description file for IpSec module.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = IpSecDxe\r
+  FILE_GUID                      = EE8367C0-A1D6-4565-8F89-EF628547B722\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = IpSecDriverEntryPoint\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+  IpSecConfigImpl.c\r
+  IpSecConfigImpl.h\r
+  IpSecCryptIo.h\r
+  IpSecCryptIo.c\r
+  IpSecDebug.h\r
+  ComponentName.c\r
+  IpSecImpl.c\r
+  IpSecDebug.c\r
+  IpSecSaEngine.c\r
+  IpSecDriver.c\r
+  IpSecImpl.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+  MemoryAllocationLib\r
+  BaseLib\r
+  UefiLib\r
+  UefiBootServicesTableLib\r
+  UefiRuntimeServicesTableLib\r
+  UefiDriverEntryPoint\r
+  BaseMemoryLib\r
+  DebugLib\r
+  PrintLib\r
+  DpcLib\r
+  NetLib\r
+\r
+[Protocols]\r
+  gEfiIp4ConfigProtocolGuid                     # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiIpSecConfigProtocolGuid                   # PROTOCOL ALWAYS_PRODUCED\r
+  gEfiIpSecProtocolGuid                         # PROTOCOL ALWAYS_PRODUCED\r
diff --git a/NetworkPkg/IpSecDxe/IpSecImpl.c b/NetworkPkg/IpSecDxe/IpSecImpl.c
new file mode 100644 (file)
index 0000000..15884ae
--- /dev/null
@@ -0,0 +1,856 @@
+/** @file\r
+  The implementation of IPsec Protocol\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecConfigImpl.h"\r
+\r
+EFI_IPSEC_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
+\r
+  @param[in]  IpVersion         The IP version.\r
+  @param[in]  Address           Points to EFI_IP_ADDRESS to be checked.\r
+  @param[in]  PrefixLength      The PrefixeLength of this address.\r
+\r
+  @retval     TRUE      The address is a vaild address range.\r
+  @retval     FALSE     The address is not a vaild address range.\r
+\r
+**/\r
+BOOLEAN\r
+IpSecValidAddressRange (\r
+  IN UINT8                     IpVersion,\r
+  IN EFI_IP_ADDRESS            *Address,\r
+  IN UINT8                     PrefixLength\r
+  )\r
+{\r
+  UINT8           Div;\r
+  UINT8           Mod;\r
+  UINT8           Mask;\r
+  UINT8           AddrLen;\r
+  UINT8           *Addr;\r
+  EFI_IP_ADDRESS  ZeroAddr;\r
+\r
+  if (PrefixLength == 0) {\r
+    return TRUE;\r
+  }\r
+\r
+  AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128);\r
+\r
+  if (AddrLen <= PrefixLength) {\r
+    return FALSE;\r
+  }\r
+\r
+  Div   = (UINT8) (PrefixLength / 8);\r
+  Mod   = (UINT8) (PrefixLength % 8);\r
+  Addr  = (UINT8 *) Address;\r
+  ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));\r
+\r
+  //\r
+  // Check whether the mod part of host scope is zero or not.\r
+  //\r
+  if (Mod > 0) {\r
+    Mask = (UINT8) (0xFF << (8 - Mod));\r
+\r
+    if ((Addr[Div] | Mask) != Mask) {\r
+      return FALSE;\r
+    }\r
+\r
+    Div++;\r
+  }\r
+  //\r
+  // Check whether the div part of host scope is zero or not.\r
+  //\r
+  if (CompareMem (\r
+        &Addr[Div],\r
+        &ZeroAddr,\r
+        sizeof (EFI_IP_ADDRESS) - Div\r
+        ) != 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Extrct the Address Range from a Address.\r
+\r
+  This function keep the prefix address and zero other part address.\r
+\r
+  @param[in]  Address           Point to a specified address.\r
+  @param[in]  PrefixLength      The prefix length.\r
+  @param[out] Range             Contain the return Address Range.\r
+\r
+**/\r
+VOID\r
+IpSecExtractAddressRange (\r
+  IN EFI_IP_ADDRESS            *Address,\r
+  IN UINT8                     PrefixLength,\r
+  OUT EFI_IP_ADDRESS           *Range\r
+  )\r
+{\r
+  UINT8 Div;\r
+  UINT8 Mod;\r
+  UINT8 Mask;\r
+  UINT8 *Addr;\r
+\r
+  if (PrefixLength == 0) {\r
+    return ;\r
+  }\r
+\r
+  Div   = (UINT8) (PrefixLength / 8);\r
+  Mod   = (UINT8) (PrefixLength % 8);\r
+  Addr  = (UINT8 *) Range;\r
+\r
+  CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS));\r
+\r
+  //\r
+  // Zero the mod part of host scope.\r
+  //\r
+  if (Mod > 0) {\r
+    Mask      = (UINT8) (0xFF << (8 - Mod));\r
+    Addr[Div] = (UINT8) (Addr[Div] & Mask);\r
+    Div++;\r
+  }\r
+  //\r
+  // Zero the div part of host scope.\r
+  //\r
+  ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div);\r
+\r
+}\r
+\r
+/**\r
+  Checks if the IP Address in the address range of AddressInfos specified.\r
+\r
+  @param[in]  IpVersion         The IP version.\r
+  @param[in]  IpAddr            Point to EFI_IP_ADDRESS to be check.\r
+  @param[in]  AddressInfo       A list of EFI_IP_ADDRESS_INFO that is used to check\r
+                                the IP Address is matched.\r
+  @param[in]  AddressCount      The total numbers of the AddressInfo.\r
+\r
+  @retval   TRUE    If the Specified IP Address is in the range of the AddressInfos specified.\r
+  @retval   FALSE   If the Specified IP Address is not in the range of the AddressInfos specified.\r
+\r
+**/\r
+BOOLEAN\r
+IpSecMatchIpAddress (\r
+  IN UINT8                     IpVersion,\r
+  IN EFI_IP_ADDRESS            *IpAddr,\r
+  IN EFI_IP_ADDRESS_INFO       *AddressInfo,\r
+  IN UINT32                    AddressCount\r
+  )\r
+{\r
+  EFI_IP_ADDRESS  Range;\r
+  UINT32          Index;\r
+  BOOLEAN         IsMatch;\r
+\r
+  IsMatch = FALSE;\r
+\r
+  for (Index = 0; Index < AddressCount; Index++) {\r
+    //\r
+    // Check whether the target address is in the address range\r
+    // if it's a valid range of address.\r
+    //\r
+    if (IpSecValidAddressRange (\r
+          IpVersion,\r
+          &AddressInfo[Index].Address,\r
+          AddressInfo[Index].PrefixLength\r
+          )) {\r
+      //\r
+      // Get the range of the target address belongs to.\r
+      //\r
+      ZeroMem (&Range, sizeof (EFI_IP_ADDRESS));\r
+      IpSecExtractAddressRange (\r
+        IpAddr,\r
+        AddressInfo[Index].PrefixLength,\r
+        &Range\r
+        );\r
+\r
+      if (CompareMem (\r
+            &Range,\r
+            &AddressInfo[Index].Address,\r
+            sizeof (EFI_IP_ADDRESS)\r
+            ) == 0) {\r
+        //\r
+        // The target address is in the address range.\r
+        //\r
+        IsMatch = TRUE;\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (CompareMem (\r
+          IpAddr,\r
+          &AddressInfo[Index].Address,\r
+          sizeof (EFI_IP_ADDRESS)\r
+          ) == 0) {\r
+      //\r
+      // The target address is exact same as the address.\r
+      //\r
+      IsMatch = TRUE;\r
+      break;\r
+    }\r
+  }\r
+\r
+  return IsMatch;\r
+}\r
+\r
+/**\r
+  Check if the specified Protocol and Prot is supported by the specified SPD Entry.\r
+\r
+  This function is the subfunction of IPsecLookUpSpdEntry() that is used to\r
+  check if the sent/received IKE packet has the related SPD entry support.\r
+\r
+  @param[in]  Protocol          The Protocol to be checked.\r
+  @param[in]  IpPayload         Point to IP Payload to be check.\r
+  @param[in]  SpdProtocol       The Protocol supported by SPD.\r
+  @param[in]  SpdLocalPort      The Local Port in SPD.\r
+  @param[in]  SpdRemotePort     The Remote Port in SPD.\r
+  @param[in]  IsOutbound        Flag to indicate the is for IKE Packet sending or recieving.\r
+\r
+  @retval     TRUE      The Protocol and Port are supported by the SPD Entry.\r
+  @retval     FALSE     The Protocol and Port are not supported by the SPD Entry.\r
+\r
+**/\r
+BOOLEAN\r
+IpSecMatchNextLayerProtocol (\r
+  IN UINT8                     Protocol,\r
+  IN UINT8                     *IpPayload,\r
+  IN UINT16                    SpdProtocol,\r
+  IN UINT16                    SpdLocalPort,\r
+  IN UINT16                    SpdRemotePort,\r
+  IN BOOLEAN                   IsOutbound\r
+  )\r
+{\r
+  BOOLEAN IsMatch;\r
+\r
+  if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) {\r
+    return TRUE;\r
+  }\r
+\r
+  IsMatch = FALSE;\r
+\r
+  if (SpdProtocol == Protocol) {\r
+    switch (Protocol) {\r
+    case EFI_IP_PROTO_UDP:\r
+    case EFI_IP_PROTO_TCP:\r
+      //\r
+      // For udp and tcp, (0, 0) means no need to check local and remote\r
+      // port. The payload is passed from upper level, which means it should\r
+      // be in network order.\r
+      //\r
+      IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
+      IsMatch = (BOOLEAN) (IsMatch ||\r
+                           (IsOutbound &&\r
+                           (BOOLEAN)(\r
+                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort &&\r
+                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort\r
+                              )\r
+                            ));\r
+\r
+      IsMatch = (BOOLEAN) (IsMatch ||\r
+                           (!IsOutbound &&\r
+                           (BOOLEAN)(\r
+                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort &&\r
+                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort\r
+                              )\r
+                           ));\r
+      break;\r
+\r
+    case EFI_IP_PROTO_ICMP:\r
+      //\r
+      // For icmpv4, type code is replaced with local port and remote port,\r
+      // and (0, 0) means no need to check.\r
+      //\r
+      IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
+      IsMatch = (BOOLEAN) (IsMatch ||\r
+                           (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&\r
+                                      ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort\r
+                                      )\r
+                           );\r
+      break;\r
+\r
+    case IP6_ICMP:\r
+      //\r
+      // For icmpv6, type code is replaced with local port and remote port,\r
+      // and (0, 0) means no need to check.\r
+      //\r
+      IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
+\r
+      IsMatch = (BOOLEAN) (IsMatch ||\r
+                           (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&\r
+                                      ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort\r
+                                      )\r
+                          );\r
+      break;\r
+\r
+    default:\r
+      IsMatch = TRUE;\r
+      break;\r
+    }\r
+  }\r
+\r
+  return IsMatch;\r
+}\r
+\r
+/**\r
+  Find the SAD through a specified SPD's SAD list.\r
+\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
+\r
+  @return  The pointer to a certain SAD entry.\r
+\r
+**/\r
+IPSEC_SAD_ENTRY *\r
+IpSecLookupSadBySpd (\r
+  IN LIST_ENTRY                 *SadList,\r
+  IN EFI_IP_ADDRESS             *DestAddress\r
+  )\r
+{\r
+  LIST_ENTRY      *Entry;\r
+  IPSEC_SAD_ENTRY *SadEntry;\r
+\r
+  for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {\r
+\r
+    SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);\r
+    //\r
+    // Find the right sad entry which contains the appointed dest address.\r
+    //\r
+    if (CompareMem (\r
+          &SadEntry->Id->DestAddress,\r
+          DestAddress,\r
+          sizeof (EFI_IP_ADDRESS)\r
+          ) == 0) {\r
+      return SadEntry;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Find the SAD through whole SAD list.\r
+\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
+\r
+  @return  the pointer to a certain SAD entry.\r
+\r
+**/\r
+IPSEC_SAD_ENTRY *\r
+IpSecLookupSadBySpi (\r
+  IN UINT32                   Spi,\r
+  IN EFI_IP_ADDRESS           *DestAddress\r
+  )\r
+{\r
+  LIST_ENTRY      *Entry;\r
+  LIST_ENTRY      *SadList;\r
+  IPSEC_SAD_ENTRY *SadEntry;\r
+\r
+  SadList = &mConfigData[IPsecConfigDataTypeSad];\r
+\r
+  for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {\r
+\r
+    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+    //\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
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Look up if there is existing SAD entry for specified IP packet sending.\r
+\r
+  This function is called by the IPsecProcess when there is some IP packet needed to\r
+  send out. This function checks if there is an existing SAD entry that can be serviced\r
+  to this IP packet sending. If no existing SAD entry could be used, this\r
+  function will invoke an IPsec Key Exchange Negotiation.\r
+\r
+  @param[in]  Private           Points to private data.\r
+  @param[in]  NicHandle         Points to a NIC handle.\r
+  @param[in]  IpVersion         The version of IP.\r
+  @param[in]  IpHead            The IP Header of packet to be sent out.\r
+  @param[in]  IpPayload         The IP Payload to be sent out.\r
+  @param[in]  OldLastHead       The Last protocol of the IP packet.\r
+  @param[in]  SpdEntry          Points to a related SPD entry.\r
+  @param[out] SadEntry          Contains the Point of a related SAD entry.\r
+\r
+  @retval EFI_DEVICE_ERROR  One of following conditions is TRUE:\r
+                            - 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
+**/\r
+EFI_STATUS\r
+IpSecLookupSadEntry (\r
+  IN IPSEC_PRIVATE_DATA      *Private,\r
+  IN EFI_HANDLE              NicHandle,\r
+  IN UINT8                   IpVersion,\r
+  IN VOID                    *IpHead,\r
+  IN UINT8                   *IpPayload,\r
+  IN UINT8                   OldLastHead,\r
+  IN IPSEC_SPD_ENTRY         *SpdEntry,\r
+  OUT IPSEC_SAD_ENTRY        **SadEntry\r
+  )\r
+{\r
+  IPSEC_SAD_ENTRY *Entry;\r
+  IPSEC_SAD_DATA  *Data;\r
+  EFI_IP_ADDRESS  DestIp;\r
+  UINT32          SeqNum32;\r
+\r
+  *SadEntry   = NULL;\r
+  //\r
+  // Parse the 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_IP_ADDRESS)\r
+      );\r
+  }\r
+  //\r
+  // Find the sad entry in the spd.sas list according to the dest address.\r
+  //\r
+  Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp);\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
+      //\r
+      //IkeNegotiate (UdpService, SpdEntry, &DestIp);\r
+    }\r
+\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  Data = Entry->Data;\r
+\r
+  if (!Data->ManualSet) {\r
+    if (Data->ESNEnabled) {\r
+      //\r
+      // Validate the 64bit sn number if 64bit sn enabled.\r
+      //\r
+      if (Data->SequenceNumber + 1 < Data->SequenceNumber) {\r
+        //\r
+        // TODO: Re-negotiate SA\r
+        //\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+    } else {\r
+      //\r
+      // Validate the 32bit sn number if 64bit sn disabled.\r
+      //\r
+      SeqNum32 = (UINT32) Data->SequenceNumber;\r
+      if (SeqNum32 + 1 < SeqNum32) {\r
+        //\r
+        // TODO: Re-negotiate SA\r
+        //\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+    }\r
+  }\r
+\r
+  *SadEntry = Entry;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Find a PAD entry according to a remote IP address.\r
+\r
+  @param[in]  IpVersion         The version of IP.\r
+  @param[in]  IpAddr            Points to remote IP address.\r
+\r
+  @return the pointer of related PAD entry.\r
+\r
+**/\r
+IPSEC_PAD_ENTRY *\r
+IpSecLookupPadEntry (\r
+  IN UINT8                   IpVersion,\r
+  IN EFI_IP_ADDRESS          *IpAddr\r
+  )\r
+{\r
+  LIST_ENTRY          *PadList;\r
+  LIST_ENTRY          *Entry;\r
+  EFI_IP_ADDRESS_INFO *IpAddrInfo;\r
+  IPSEC_PAD_ENTRY     *PadEntry;\r
+\r
+  PadList = &mConfigData[IPsecConfigDataTypePad];\r
+\r
+  for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) {\r
+\r
+    PadEntry    = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
+    IpAddrInfo  = &PadEntry->Id->Id.IpAddress;\r
+    //\r
+    // Find the right pad entry which contain the appointed dest addr.\r
+    //\r
+    if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) {\r
+      return PadEntry;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Check if the specified IP packet can be serviced by this SPD entry.\r
+\r
+  @param[in]  SpdEntry          Point to SPD entry.\r
+  @param[in]  IpVersion         Version of IP.\r
+  @param[in]  IpHead            Point to IP header.\r
+  @param[in]  IpPayload         Point to IP payload.\r
+  @param[in]  Protocol          The Last protocol of IP packet.\r
+  @param[in]  IsOutbound        Traffic direction.\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
+\r
+**/\r
+EFI_IPSEC_ACTION\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
+  )\r
+{\r
+  EFI_IPSEC_SPD_SELECTOR  *SpdSel;\r
+  IP4_HEAD                *Ip4;\r
+  EFI_IP6_HEADER          *Ip6;\r
+  EFI_IP_ADDRESS          SrcAddr;\r
+  EFI_IP_ADDRESS          DstAddr;\r
+  BOOLEAN                 SpdMatch;\r
+\r
+  ASSERT (SpdEntry != NULL);\r
+  SpdSel  = SpdEntry->Selector;\r
+  Ip4     = (IP4_HEAD *) IpHead;\r
+  Ip6     = (EFI_IP6_HEADER *) IpHead;\r
+\r
+  ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS));\r
+  ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS));\r
+\r
+  //\r
+  // Parse the source and destination address from ip header.\r
+  //\r
+  if (IpVersion == IP_VERSION_4) {\r
+    CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR));\r
+    CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR));\r
+  } else {\r
+    CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
+    CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));\r
+  }\r
+  //\r
+  // Check the local and remote addresses for outbound traffic\r
+  //\r
+  SpdMatch = (BOOLEAN)(IsOutbound &&\r
+                       IpSecMatchIpAddress (\r
+                         IpVersion,\r
+                         &SrcAddr,\r
+                         SpdSel->LocalAddress,\r
+                         SpdSel->LocalAddressCount\r
+                         ) &&\r
+                       IpSecMatchIpAddress (\r
+                         IpVersion,\r
+                         &DstAddr,\r
+                         SpdSel->RemoteAddress,\r
+                         SpdSel->RemoteAddressCount\r
+                         )\r
+                       );\r
+\r
+  //\r
+  // Check the local and remote addresses for inbound traffic\r
+  //\r
+  SpdMatch = (BOOLEAN) (SpdMatch ||\r
+                        (!IsOutbound &&\r
+                        IpSecMatchIpAddress (\r
+                          IpVersion,\r
+                          &DstAddr,\r
+                          SpdSel->LocalAddress,\r
+                          SpdSel->LocalAddressCount\r
+                          ) &&\r
+                        IpSecMatchIpAddress (\r
+                          IpVersion,\r
+                          &SrcAddr,\r
+                          SpdSel->RemoteAddress,\r
+                          SpdSel->RemoteAddressCount\r
+                          )\r
+                        ));\r
+\r
+  //\r
+  // Check the next layer protocol and local and remote ports.\r
+  //\r
+  SpdMatch = (BOOLEAN) (SpdMatch &&\r
+                        IpSecMatchNextLayerProtocol (\r
+                          Protocol,\r
+                          IpPayload,\r
+                          SpdSel->NextLayerProtocol,\r
+                          SpdSel->LocalPort,\r
+                          SpdSel->RemotePort,\r
+                          IsOutbound\r
+                          )\r
+                        );\r
+\r
+  if (SpdMatch) {\r
+    //\r
+    // Find the right spd entry if match the 5 key elements.\r
+    //\r
+    return SpdEntry->Data->Action;\r
+  }\r
+\r
+  return (EFI_IPSEC_ACTION) - 1;\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]      LastHead         The protocol of the next layer to be processed by IPsec.\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.\r
+  @param[in]      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
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecProcess (\r
+  IN     EFI_IPSEC_PROTOCOL              *This,\r
+  IN     EFI_HANDLE                      NicHandle,\r
+  IN     UINT8                           IpVersion,\r
+  IN OUT VOID                            *IpHead,\r
+  IN     UINT8                           *LastHead,\r
+  IN     VOID                            *OptionsBuffer,\r
+  IN     UINT32                          OptionsLength,\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA         **FragmentTable,\r
+  IN     UINT32                          *FragmentCount,\r
+  IN     EFI_IPSEC_TRAFFIC_DIR           TrafficDirection,\r
+     OUT EFI_EVENT                       *RecycleSignal\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
+  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
+  if (!IsOutbound) {\r
+    //\r
+    // For inbound traffic, process the ipsec header of the packet.\r
+    //\r
+    Status = IpSecProtectInboundPacket (\r
+              IpVersion,\r
+              IpHead,\r
+              LastHead,\r
+              OptionsBuffer,\r
+              OptionsLength,\r
+              FragmentTable,\r
+              FragmentCount,\r
+              &SpdEntry,\r
+              RecycleSignal\r
+              );\r
+\r
+    if (Status == EFI_ACCESS_DENIED) {\r
+      //\r
+      // The packet is denied to access.\r
+      //\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    if (Status == EFI_SUCCESS) {\r
+      //\r
+      // Check the spd entry if the packet is accessible.\r
+      //\r
+      if (SpdEntry == NULL) {\r
+        Status = EFI_ACCESS_DENIED;\r
+        goto ON_EXIT;\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
+\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  Status  = EFI_ACCESS_DENIED;\r
+  SpdList = &mConfigData[IPsecConfigDataTypeSpd];\r
+\r
+  for (Entry = SpdList->ForwardLink; Entry != SpdList; Entry = Entry->ForwardLink) {\r
+    //\r
+    // For outbound and non-ipsec Inbound traffic: check the spd entry.\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
+\r
+    switch (Action) {\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
+        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
+      goto ON_EXIT;\r
+\r
+    case EfiIPsecActionBypass:\r
+      Status = EFI_SUCCESS;\r
+      goto ON_EXIT;\r
+\r
+    case EfiIPsecActionDiscard:\r
+      goto ON_EXIT;\r
+\r
+    default:\r
+      //\r
+      // Discard the packet if no spd entry match.\r
+      //\r
+      break;\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/IpSecDxe/IpSecImpl.h b/NetworkPkg/IpSecDxe/IpSecImpl.h
new file mode 100644 (file)
index 0000000..644c658
--- /dev/null
@@ -0,0 +1,313 @@
+/** @file\r
+  The definitions related to IPsec protocol implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _IP_SEC_IMPL_H_\r
+#define _IP_SEC_IMPL_H_\r
+\r
+#include <Uefi.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/NetLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Protocol/IpSec.h>\r
+#include <Protocol/IpSecConfig.h>\r
+#include <Protocol/Dpc.h>\r
+#include <Protocol/ComponentName.h>\r
+#include <Protocol/ComponentName2.h>\r
+\r
+typedef struct _IPSEC_PRIVATE_DATA IPSEC_PRIVATE_DATA;\r
+typedef struct _IPSEC_SPD_ENTRY IPSEC_SPD_ENTRY;\r
+typedef struct _IPSEC_PAD_ENTRY IPSEC_PAD_ENTRY;\r
+typedef struct _IPSEC_SPD_DATA IPSEC_SPD_DATA;\r
+\r
+#define IPSEC_PRIVATE_DATA_SIGNATURE        SIGNATURE_32 ('I', 'P', 'S', 'E')\r
+\r
+#define IPSEC_PRIVATE_DATA_FROM_IPSEC(a)    CR (a, IPSEC_PRIVATE_DATA, IpSec, IPSEC_PRIVATE_DATA_SIGNATURE)\r
+#define IPSEC_PRIVATE_DATA_FROM_UDP4LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp4List, IPSEC_PRIVATE_DATA_SIGNATURE)\r
+#define IPSEC_PRIVATE_DATA_FROM_UDP6LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp6List, IPSEC_PRIVATE_DATA_SIGNATURE)\r
+#define IPSEC_UDP_SERVICE_FROM_LIST(a)      BASE_CR (a, IKE_UDP_SERVICE, List)\r
+#define IPSEC_SPD_ENTRY_FROM_LIST(a)        BASE_CR (a, IPSEC_SPD_ENTRY, List)\r
+#define IPSEC_SAD_ENTRY_FROM_LIST(a)        BASE_CR (a, IPSEC_SAD_ENTRY, List)\r
+#define IPSEC_PAD_ENTRY_FROM_LIST(a)        BASE_CR (a, IPSEC_PAD_ENTRY, List)\r
+#define IPSEC_SAD_ENTRY_FROM_SPD(a)         BASE_CR (a, IPSEC_SAD_ENTRY, BySpd)\r
+\r
+#define IPSEC_STATUS_DISABLED       0\r
+#define IPSEC_STATUS_ENABLED        1\r
+#define IPSEC_ESP_PROTOCOL          50\r
+#define IPSEC_AH_PROTOCOL           51\r
+#define IPSEC_DEFAULT_VARIABLE_SIZE 0x100\r
+\r
+//\r
+// Internal Structure Definition\r
+//\r
+#pragma pack(1)\r
+typedef struct _EFI_AH_HEADER {\r
+  UINT8   NextHeader;\r
+  UINT8   PayloadLen;\r
+  UINT16  Reserved;\r
+  UINT32  Spi;\r
+  UINT32  SequenceNumber;\r
+} EFI_AH_HEADER;\r
+\r
+typedef struct _EFI_ESP_HEADER {\r
+  UINT32  Spi;\r
+  UINT32  SequenceNumber;\r
+} EFI_ESP_HEADER;\r
+\r
+typedef struct _EFI_ESP_TAIL {\r
+  UINT8 PaddingLength;\r
+  UINT8 NextHeader;\r
+} EFI_ESP_TAIL;\r
+#pragma pack()\r
+\r
+struct _IPSEC_SPD_DATA {\r
+  CHAR16                    Name[100];\r
+  UINT32                    PackageFlag;\r
+  EFI_IPSEC_ACTION          Action;\r
+  EFI_IPSEC_PROCESS_POLICY  *ProcessingPolicy;\r
+  LIST_ENTRY                Sas;\r
+};\r
+\r
+struct _IPSEC_SPD_ENTRY {\r
+  EFI_IPSEC_SPD_SELECTOR  *Selector;\r
+  IPSEC_SPD_DATA          *Data;\r
+  LIST_ENTRY              List;\r
+};\r
+\r
+typedef struct _IPSEC_SAD_DATA {\r
+  EFI_IPSEC_MODE        Mode;\r
+  UINT64                SequenceNumber;\r
+  UINT8                 AntiReplayWindowSize;\r
+  UINT64                AntiReplayBitmap[4];  // bitmap for received packet\r
+  EFI_IPSEC_ALGO_INFO   AlgoInfo;\r
+  EFI_IPSEC_SA_LIFETIME SaLifetime;\r
+  UINT32                PathMTU;\r
+  IPSEC_SPD_ENTRY       *SpdEntry;\r
+  BOOLEAN               ESNEnabled;           // Extended (64-bit) SN enabled\r
+  BOOLEAN               ManualSet;\r
+} IPSEC_SAD_DATA;\r
+\r
+typedef struct _IPSEC_SAD_ENTRY {\r
+  EFI_IPSEC_SA_ID  *Id;\r
+  IPSEC_SAD_DATA  *Data;\r
+  LIST_ENTRY      List;\r
+  LIST_ENTRY      BySpd;                      // Linked on IPSEC_SPD_DATA.Sas\r
+} IPSEC_SAD_ENTRY;\r
+\r
+struct _IPSEC_PAD_ENTRY {\r
+  EFI_IPSEC_PAD_ID    *Id;\r
+  EFI_IPSEC_PAD_DATA  *Data;\r
+  LIST_ENTRY          List;\r
+};\r
+\r
+typedef struct _IPSEC_RECYCLE_CONTEXT {\r
+  EFI_IPSEC_FRAGMENT_DATA *FragmentTable;\r
+  UINT8                   *PayloadBuffer;\r
+} IPSEC_RECYCLE_CONTEXT;\r
+\r
+struct _IPSEC_PRIVATE_DATA {\r
+  UINT32                    Signature;\r
+  EFI_HANDLE                Handle;           // Virtual handle to install private prtocol\r
+  EFI_HANDLE                ImageHandle;\r
+  EFI_IPSEC_PROTOCOL        IpSec;\r
+  EFI_IPSEC_CONFIG_PROTOCOL IpSecConfig;\r
+  BOOLEAN                   SetBySelf;\r
+  LIST_ENTRY                Udp4List;\r
+  UINTN                     Udp4Num;\r
+  LIST_ENTRY                Udp6List;\r
+  UINTN                     Udp6Num;\r
+  LIST_ENTRY                Ikev1SessionList;\r
+  LIST_ENTRY                Ikev1EstablishedList;\r
+  LIST_ENTRY                Ikev2SessionList;\r
+  LIST_ENTRY                Ikev2EstablishedList;\r
+  BOOLEAN                   IsIPsecDisabling;\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]      LastHead           The Last Header in IP header on return.\r
+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.\r
+  @param[in]      OptionsLength      Length of the options buffer. It is optional.\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]      FragmentCount      Number of fragments.\r
+  @param[out]     SpdEntry           Pointer to contain the address of SPD entry on return.\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
+IpSecProtectInboundPacket (\r
+  IN     UINT8                       IpVersion,\r
+  IN OUT VOID                        *IpHead,\r
+  IN     UINT8                       *LastHead,\r
+  IN     VOID                        *OptionsBuffer, OPTIONAL\r
+  IN     UINT32                      OptionsLength,  OPTIONAL\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN     UINT32                      *FragmentCount,\r
+     OUT IPSEC_SPD_ENTRY             **SpdEntry,\r
+     OUT EFI_EVENT                   *RecycleEvent\r
+  );\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]      LastHead           The Last Header in IP header.\r
+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.\r
+  @param[in]      OptionsLength      Length of the options buffer. It is optional.\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      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     UINT8                       *LastHead,\r
+  IN     VOID                        *OptionsBuffer, OPTIONAL\r
+  IN     UINT32                      OptionsLength,  OPTIONAL\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN     UINT32                      *FragmentCount,\r
+  IN     IPSEC_SAD_ENTRY             *SadEntry,\r
+     OUT EFI_EVENT                   *RecycleEvent\r
+  );\r
+\r
+/**\r
+  Check if the IP Address in the address range of AddressInfos specified.\r
+\r
+  @param[in]  IpVersion         The IP version.\r
+  @param[in]  IpAddr            Points to EFI_IP_ADDRESS to be check.\r
+  @param[in]  AddressInfo       A list of EFI_IP_ADDRESS_INFO that is used to check\r
+                                the IP Address is matched.\r
+  @param[in]  AddressCount      The total numbers of the AddressInfo.\r
+\r
+  @retval   TRUE    If the Specified IP Address is in the range of the AddressInfos specified.\r
+  @retval   FALSE   If the Specified IP Address is not in the range of the AddressInfos specified.\r
+\r
+**/\r
+BOOLEAN\r
+IpSecMatchIpAddress (\r
+  IN UINT8                                  IpVersion,\r
+  IN EFI_IP_ADDRESS                         *IpAddr,\r
+  IN EFI_IP_ADDRESS_INFO                    *AddressInfo,\r
+  IN UINT32                                 AddressCount\r
+  );\r
+\r
+/**\r
+  Find a PAD entry according to remote IP address.\r
+\r
+  @param[in]  IpVersion         The version of IP.\r
+  @param[in]  IpAddr            Point to remote IP address.\r
+\r
+  @return The pointer of related PAD entry.\r
+\r
+**/\r
+IPSEC_PAD_ENTRY *\r
+IpSecLookupPadEntry (\r
+  IN UINT8                                  IpVersion,\r
+  IN EFI_IP_ADDRESS                         *IpAddr\r
+  );\r
+\r
+/**\r
+  Find the SAD through whole SAD list.\r
+\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
+\r
+  @return  The pointer to a certain SAD entry.\r
+\r
+**/\r
+IPSEC_SAD_ENTRY *\r
+IpSecLookupSadBySpi (\r
+  IN UINT32                                 Spi,\r
+  IN EFI_IP_ADDRESS                         *DestAddress\r
+  )\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]      LastHead         The protocol of the next layer to be processed by IPsec.\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.\r
+  @param[in]      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
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecProcess (\r
+  IN     EFI_IPSEC_PROTOCOL              *This,\r
+  IN     EFI_HANDLE                      NicHandle,\r
+  IN     UINT8                           IpVersion,\r
+  IN OUT VOID                            *IpHead,\r
+  IN     UINT8                           *LastHead,\r
+  IN     VOID                            *OptionsBuffer,\r
+  IN     UINT32                          OptionsLength,\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA         **FragmentTable,\r
+  IN     UINT32                          *FragmentCount,\r
+  IN     EFI_IPSEC_TRAFFIC_DIR           TrafficDirection,\r
+     OUT EFI_EVENT                       *RecycleSignal\r
+  );\r
+\r
+extern EFI_DPC_PROTOCOL    *mDpc;\r
+extern EFI_IPSEC_PROTOCOL  mIpSecInstance;\r
+\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2;\r
+extern EFI_COMPONENT_NAME_PROTOCOL  gIpSecComponentName;\r
+\r
+\r
+#endif\r
diff --git a/NetworkPkg/IpSecDxe/IpSecSaEngine.c b/NetworkPkg/IpSecDxe/IpSecSaEngine.c
new file mode 100644 (file)
index 0000000..8abf4d6
--- /dev/null
@@ -0,0 +1,934 @@
+/** @file\r
+  IPsec inbound and outbound traffic processing.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "IpSecImpl.h"\r
+#include "IpSecDebug.h"\r
+#include "IpSecCryptIo.h"\r
+\r
+extern LIST_ENTRY     mConfigData[IPsecConfigDataTypeMaximum];\r
+\r
+/**\r
+  The call back function of NetbufFromExt.\r
+\r
+  @param[in]  Arg            The argument passed from the caller.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IpSecOnRecyclePacket (\r
+  IN VOID                            *Arg\r
+  )\r
+{\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 header 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
+**/\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 (Size > sizeof (EFI_IP6_HEADER)) {\r
+    //\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
+    Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8);\r
+  } else {\r
+    Size = 0;\r
+  }\r
+\r
+  return Size;\r
+}\r
+\r
+/**\r
+  Authenticate the IpSec Payload and store the result in the IcvBuffer.\r
+\r
+  @param[in]      BufferToAuth    The buffer to be Authenticated.\r
+  @param[in]      AuthSize        The size of the buffer to be Authenticated.\r
+  @param[in, out] IcvBuffer       The buffer to store the ICV.\r
+  @param[in]      IcvSize         The size of ICV.\r
+  @param[in]      Key             The Key passed to the CryptLib to generate a\r
+                                  CRYPT_HANDLE.\r
+  @param[in]      AuthAlgId       The Authentication Algorithm ID.\r
+\r
+  @retval EFI_UNSUPPORTED     If the AuthAlg is not in the support list.\r
+  @retval EFI_SUCCESS         Authenticated the payload successfully.\r
+  @retval otherwise           Authentication of the payload failed.\r
+**/\r
+EFI_STATUS\r
+IpSecAuthPayload (\r
+  IN     UINT8                           *BufferToAuth,\r
+  IN     UINTN                           AuthSize,\r
+  IN OUT UINT8                           *IcvBuffer,\r
+  IN     UINTN                           IcvSize,\r
+  IN     VOID                            *Key,\r
+  IN     UINT8                           AuthAlgId\r
+  )\r
+{\r
+  switch (AuthAlgId) {\r
+    case EFI_IPSEC_AALG_NONE :\r
+    case EFI_IPSEC_AALG_NULL :\r
+        return EFI_SUCCESS;\r
+\r
+    default:\r
+        return EFI_UNSUPPORTED;\r
+  }\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
+\r
+  //\r
+  // Calculate the size of authentication payload.\r
+  //\r
+  *IcvSize  = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);\r
+  AuthSize  = EspSize - *IcvSize;\r
+\r
+  //\r
+  // Calculate the icv buffer and size of the payload.\r
+  //\r
+  Status = IpSecAuthPayload (\r
+             EspBuffer,\r
+             AuthSize,\r
+             IcvBuffer,\r
+             *IcvSize,\r
+             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\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
+  ESP Decrypt the payload.\r
+\r
+  @param[in, out] PayloadBuffer      Pointer to the buffer containing the ESP wrapped;\r
+                                     to be decrypted on input, and plaintext on return. The\r
+                                     number of bytes of data to be decrypted is\r
+                                     specified by EncryptSize.\r
+  @param[in]      EncryptSize        The size of the PayloadBuffer as input.\r
+  @param[in]      SadEntry           The related SAD entry.\r
+  @param[in]      IvSize             The size of IV.\r
+  @param[out]     PlainPayloadSize   Contains the return value of decrypted size.\r
+  @param[out]     PaddingSize        Contains the return value of Padding size.\r
+  @param[out]     NextHeader         Contains the return value of the last protocol header\r
+                                     of the IP packet.\r
+\r
+  @retval EFI_UNSUPPORTED    The Algorithm pointed to by the SAD entry is not supported.\r
+  @retval EFI_SUCCESS        The operation completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspDecryptPayload (\r
+  IN OUT UINT8                       *PayloadBuffer,\r
+  IN     UINTN                       EncryptSize,\r
+  IN     IPSEC_SAD_ENTRY             *SadEntry,\r
+  IN     UINTN                       *IvSize,\r
+     OUT UINTN                       *PlainPayloadSize,\r
+     OUT UINTN                       *PaddingSize,\r
+     OUT UINT8                       *NextHeader\r
+  )\r
+{\r
+  EFI_ESP_TAIL *EspTail;\r
+\r
+  switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {\r
+    case EFI_IPSEC_EALG_NULL:\r
+      EspTail            = (EFI_ESP_TAIL *) (PayloadBuffer + EncryptSize - sizeof (EFI_ESP_TAIL));\r
+      *PaddingSize       = EspTail->PaddingLength;\r
+      *NextHeader        = EspTail->NextHeader;\r
+      *PlainPayloadSize  = EncryptSize - EspTail->PaddingLength - sizeof (EFI_ESP_TAIL);\r
+      break;\r
+\r
+    case EFI_IPSEC_EALG_3DESCBC:\r
+    case EFI_IPSEC_EALG_AESCBC:\r
+      //\r
+      // TODO: support these algorithm\r
+      //\r
+      return EFI_UNSUPPORTED;\r
+    default :\r
+      return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  ESP Encrypt the payload.\r
+\r
+  @param[in, out] BufferToEncrypt Pointer to the buffer containing plaintext to be\r
+                                  encrypted on input, and ciphertext on return. The\r
+                                  number of bytes of data to be encrypted is\r
+                                  specified by EncryptSize.\r
+  @param[in, out] EncryptSize     The size of the plaintext on input, and the size of the\r
+                                  ciphertext on return.\r
+  @param[in]      IvBuffer        Points to IV data.\r
+  @param[in]      IvSize          Size of IV.\r
+  @param[in]      SadEntry        Related SAD entry.\r
+\r
+  @retval EFI_UNSUPPORTED    The Algorithm pointed by SAD entry is not supported.\r
+  @retval EFI_SUCCESS        The operation completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspEncryptPayload (\r
+  IN OUT UINT8                       *BufferToEncrypt,\r
+  IN OUT UINTN                       EncryptSize,\r
+  IN     UINT8                       *IvBuffer,\r
+  IN     UINTN                       IvSize,\r
+  IN     IPSEC_SAD_ENTRY             *SadEntry\r
+  )\r
+{\r
+  switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {\r
+    case EFI_IPSEC_EALG_NULL:\r
+      return EFI_SUCCESS;\r
+\r
+    case EFI_IPSEC_EALG_3DESCBC:\r
+    case EFI_IPSEC_EALG_AESCBC:\r
+      //\r
+      // TODO: support these algorithms\r
+      //\r
+      return EFI_UNSUPPORTED;\r
+    default :\r
+      return EFI_UNSUPPORTED;\r
+\r
+  }\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]      OptionsBuffer      Pointer to the options buffer. It is optional.\r
+  @param[in]      OptionsLength      Length of the options buffer. It is optional.\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]      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_ACCESS_DENIED        One or more following conditions is TRUE:\r
+                                   - ESP header was not found.\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     VOID                        *OptionsBuffer, OPTIONAL\r
+  IN     UINT32                      OptionsLength,  OPTIONAL\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN     UINT32                      *FragmentCount,\r
+     OUT IPSEC_SPD_ENTRY             **SpdEntry,\r
+     OUT EFI_EVENT                   *RecycleEvent\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  NET_BUF               *Payload;\r
+  UINTN                 EspSize;\r
+  UINTN                 IvSize;\r
+  UINTN                 PlainPayloadSize;\r
+  UINTN                 PaddingSize;\r
+  UINTN                 IcvSize;\r
+  UINT8                 *ProcessBuffer;\r
+  EFI_IP_ADDRESS        DestIp;\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
+  UINT32                Spi;\r
+  UINT8                 NextHeader;\r
+  UINT16                IpSecHeadSize;\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
+  // 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
+  // Get the esp size and eso header from netbuf.\r
+  //\r
+  EspSize   = Payload->TotalSize;\r
+  EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);\r
+  if (EspHeader == NULL) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\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
+  // Lookup sad entry according to the spi and dest address.\r
+  //\r
+  Spi       = NTOHL (EspHeader->Spi);\r
+  SadEntry  = IpSecLookupSadBySpi (Spi, &DestIp);\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
+  // Allocate buffer for decryption and authentication by esp.\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
+  // Authenticate the esp wrapped buffer by the sad entry if has auth key.\r
+  //\r
+  IcvSize = 0;\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 has decrypt key.\r
+  //\r
+  IvSize = 0;\r
+  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+    Status = IpSecEspDecryptPayload (\r
+               ProcessBuffer + sizeof (EFI_ESP_HEADER),\r
+               EspSize - sizeof (EFI_ESP_HEADER) - IcvSize,\r
+               SadEntry,\r
+               &IvSize,\r
+               &PlainPayloadSize,\r
+               &PaddingSize,\r
+               &NextHeader\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  } else {\r
+    EspTail           = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));\r
+    PaddingSize       = EspTail->PaddingLength;\r
+    NextHeader        = EspTail->NextHeader;\r
+    PlainPayloadSize  = EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize - 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
+  // TODO: Who 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
+  (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;\r
+  (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;\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 + 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
+  // Update the spd association of the sad entry.\r
+  //\r
+  *SpdEntry = SadData->SpdEntry;\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]      LastHead           The Last Header in IP header.\r
+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.\r
+  @param[in]      OptionsLength      Length of the options buffer. It is optional.\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
+  @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     UINT8                       *LastHead,\r
+  IN     VOID                        *OptionsBuffer, OPTIONAL\r
+  IN     UINT32                      OptionsLength,  OPTIONAL\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN     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
+\r
+  Status          = EFI_ACCESS_DENIED;\r
+  SaId            = SadEntry->Id;\r
+  SadData         = SadEntry->Data;\r
+  ProcessBuffer   = NULL;\r
+  RecycleContext  = NULL;\r
+  *RecycleEvent   = 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
+  // 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
+  // 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
+  // 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
+  // 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
+  // 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->Spi            = HTONL (SaId->Spi);\r
+\r
+  //\r
+  // Copy the rest of payload (after iv) from the original fragment buffer.\r
+  //\r
+  BytesCopied = 0;\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
+  // 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
+  // Encrypt the payload (after iv) by the sad entry if has encrypt key.\r
+  //\r
+  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+    Status = IpSecEspEncryptPayload (\r
+               RestOfPayload,\r
+               EncryptSize,\r
+               (UINT8 *) (EspHeader + 1),\r
+               IvSize,\r
+               SadEntry\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+  //\r
+  // Authenticate the esp wrapped buffer by the sad entry if has auth key.\r
+  //\r
+  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+    Status = IpSecAuthPayload (\r
+               ProcessBuffer,\r
+               EspSize - IcvSize,\r
+               ProcessBuffer + EspSize - IcvSize,\r
+               IcvSize,\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\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
+  // TODO: Who 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 + EspSize));\r
+  } else {\r
+    ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);\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]      LastHead           The Last Header in IP header on return.\r
+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.\r
+  @param[in]      OptionsLength      Length of the options buffer. It is optional.\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]      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     UINT8                       *LastHead,\r
+  IN     VOID                        *OptionsBuffer, OPTIONAL\r
+  IN     UINT32                      OptionsLength,  OPTIONAL\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN     UINT32                      *FragmentCount,\r
+     OUT IPSEC_SPD_ENTRY             **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 function 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 returns 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/AH header\r
+                                     on return.\r
+  @param[in]      LastHead           The Last Header in the IP header.\r
+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.\r
+  @param[in]      OptionsLength      Length of the options buffer. It is optional.\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
+  @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_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     UINT8                       *LastHead,\r
+  IN     VOID                        *OptionsBuffer, OPTIONAL\r
+  IN     UINT32                      OptionsLength,  OPTIONAL\r
+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,\r
+  IN     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
diff --git a/NetworkPkg/Mtftp6Dxe/ComponentName.c b/NetworkPkg/Mtftp6Dxe/ComponentName.c
new file mode 100644 (file)
index 0000000..0f91b64
--- /dev/null
@@ -0,0 +1,308 @@
+/** @file\r
+  UEFI Component Name(2) protocol implementation for Mtftp6 driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for bus drivers\r
+                                attempting to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that attempts to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL     gMtftp6ComponentName = {\r
+  Mtftp6ComponentNameGetDriverName,\r
+  Mtftp6ComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL    gMtftp6ComponentName2 = {\r
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp6ComponentNameGetDriverName,\r
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp6ComponentNameGetControllerName,\r
+  "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE        mMtftp6DriverNameTable[] = {\r
+  {\r
+    "eng;en",\r
+    L"MTFTP6 Network Service Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+{\r
+  return LookupUnicodeString2 (\r
+          Language,\r
+          This->SupportedLanguages,\r
+          mMtftp6DriverNameTable,\r
+          DriverName,\r
+          (BOOLEAN)(This == &gMtftp6ComponentName)\r
+          );\r
+}\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                attempting to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that attempts to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,\r
+  IN  EFI_HANDLE                                      ControllerHandle,\r
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,\r
+  IN  CHAR8                                           *Language,\r
+  OUT CHAR16                                          **ControllerName\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c
new file mode 100644 (file)
index 0000000..d448c78
--- /dev/null
@@ -0,0 +1,703 @@
+/** @file\r
+  Driver Binding functions and Service Binding functions\r
+  implementation for Mtftp6 Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL   gMtftp6DriverBinding = {\r
+  Mtftp6DriverBindingSupported,\r
+  Mtftp6DriverBindingStart,\r
+  Mtftp6DriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL  gMtftp6ServiceBindingTemplate = {\r
+  Mtftp6ServiceBindingCreateChild,\r
+  Mtftp6ServiceBindingDestroyChild\r
+};\r
+\r
+\r
+/**\r
+  Destory the MTFTP6 service. The MTFTP6 service may be partly initialized,\r
+  or partly destroyed. If a resource is destroyed, it is marked as such in\r
+  case the destroy failed and is called again later.\r
+\r
+  @param[in]  Service            The MTFTP6 service to be destroyed.\r
+\r
+**/\r
+VOID\r
+Mtftp6DestroyService (\r
+  IN MTFTP6_SERVICE     *Service\r
+  )\r
+{\r
+  //\r
+  // Make sure all children instances have been already destoryed.\r
+  //\r
+  ASSERT (Service->ChildrenNum == 0);\r
+\r
+  if (Service->DummyUdpIo != NULL) {\r
+    UdpIoFreeIo (Service->DummyUdpIo);\r
+  }\r
+\r
+  if (Service->Timer != NULL) {\r
+    gBS->CloseEvent (Service->Timer);\r
+  }\r
+\r
+  FreePool (Service);\r
+}\r
+\r
+\r
+/**\r
+  Create then initialize a MTFTP6 service binding instance.\r
+\r
+  @param[in]  Controller             The controller to install the MTFTP6 service\r
+                                     binding on.\r
+  @param[in]  Image                  The driver binding image of the MTFTP6 driver.\r
+  @param[out] Service                The variable to receive the created service\r
+                                     binding instance.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to create the instance\r
+  @retval EFI_DEVICE_ERROR       Failed to create a NULL UDP port to keep connection with UDP.\r
+  @retval EFI_SUCCESS            The service instance is created for the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6CreateService (\r
+  IN  EFI_HANDLE            Controller,\r
+  IN  EFI_HANDLE            Image,\r
+  OUT MTFTP6_SERVICE        **Service\r
+  )\r
+{\r
+  MTFTP6_SERVICE            *Mtftp6Srv;\r
+  EFI_STATUS                Status;\r
+\r
+  ASSERT (Service != NULL);\r
+\r
+  *Service  = NULL;\r
+  Mtftp6Srv = AllocateZeroPool (sizeof (MTFTP6_SERVICE));\r
+\r
+  if (Mtftp6Srv == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Mtftp6Srv->Signature      = MTFTP6_SERVICE_SIGNATURE;\r
+  Mtftp6Srv->Controller     = Controller;\r
+  Mtftp6Srv->Image          = Image;\r
+  Mtftp6Srv->InDestory      = FALSE;\r
+  Mtftp6Srv->ChildrenNum    = 0;\r
+\r
+  CopyMem (\r
+    &Mtftp6Srv->ServiceBinding,\r
+    &gMtftp6ServiceBindingTemplate,\r
+    sizeof (EFI_SERVICE_BINDING_PROTOCOL)\r
+    );\r
+\r
+  InitializeListHead (&Mtftp6Srv->Children);\r
+\r
+  //\r
+  // Create a internal timer for all instances.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  Mtftp6OnTimerTick,\r
+                  Mtftp6Srv,\r
+                  &Mtftp6Srv->Timer\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Mtftp6Srv);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Create a dummy Udp6Io to build parent-child relationship between Udp6 driver\r
+  // and Mtftp6 driver.\r
+  //\r
+  Mtftp6Srv->DummyUdpIo = UdpIoCreateIo (\r
+                            Controller,\r
+                            Image,\r
+                            Mtftp6ConfigDummyUdpIo,\r
+                            UDP_IO_UDP6_VERSION,\r
+                            NULL\r
+                            );\r
+\r
+  if (Mtftp6Srv->DummyUdpIo == NULL) {\r
+    gBS->CloseEvent (Mtftp6Srv->Timer);\r
+    FreePool (Mtftp6Srv);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  *Service = Mtftp6Srv;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Destroy the MTFTP6 instance and recycle the resources.\r
+\r
+  @param[in]  Instance        The pointer to the MTFTP6 instance.\r
+\r
+**/\r
+VOID\r
+Mtftp6DestroyInstance (\r
+  IN MTFTP6_INSTANCE         *Instance\r
+  )\r
+{\r
+  LIST_ENTRY                 *Entry;\r
+  LIST_ENTRY                 *Next;\r
+  MTFTP6_BLOCK_RANGE         *Block;\r
+\r
+  if (Instance->Config != NULL) {\r
+    FreePool (Instance->Config);\r
+  }\r
+\r
+  if (Instance->Token != NULL && Instance->Token->Event != NULL) {\r
+    gBS->SignalEvent (Instance->Token->Event);\r
+  }\r
+\r
+  if (Instance->LastPacket != NULL) {\r
+    NetbufFree (Instance->LastPacket);\r
+  }\r
+\r
+  if (Instance->UdpIo!= NULL) {\r
+    UdpIoFreeIo (Instance->UdpIo);\r
+  }\r
+\r
+  if (Instance->McastUdpIo != NULL) {\r
+    UdpIoFreeIo (Instance->McastUdpIo);\r
+  }\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {\r
+    Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);\r
+    RemoveEntryList (Entry);\r
+    FreePool (Block);\r
+  }\r
+\r
+  FreePool (Instance);\r
+}\r
+\r
+\r
+/**\r
+  Create the MTFTP6 instance and initialize it.\r
+\r
+  @param[in]  Service              The pointer to the MTFTP6 service.\r
+  @param[out] Instance             The pointer to the MTFTP6 instance.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.\r
+  @retval EFI_SUCCESS            The MTFTP6 instance is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6CreateInstance (\r
+  IN  MTFTP6_SERVICE         *Service,\r
+  OUT MTFTP6_INSTANCE        **Instance\r
+  )\r
+{\r
+  MTFTP6_INSTANCE            *Mtftp6Ins;\r
+\r
+  *Instance = NULL;\r
+  Mtftp6Ins = AllocateZeroPool (sizeof (MTFTP6_INSTANCE));\r
+\r
+  if (Mtftp6Ins == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Mtftp6Ins->Signature = MTFTP6_INSTANCE_SIGNATURE;\r
+  Mtftp6Ins->InDestory = FALSE;\r
+  Mtftp6Ins->Service   = Service;\r
+\r
+  CopyMem (\r
+    &Mtftp6Ins->Mtftp6,\r
+    &gMtftp6ProtocolTemplate,\r
+    sizeof (EFI_MTFTP6_PROTOCOL)\r
+    );\r
+\r
+  InitializeListHead (&Mtftp6Ins->Link);\r
+  InitializeListHead (&Mtftp6Ins->BlkList);\r
+\r
+  *Instance = Mtftp6Ins;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  This is the declaration of an EFI image entry point. This entry point is\r
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+  both device drivers and bus drivers.\r
+\r
+  Entry point of the MTFTP6 driver to install various protocols.\r
+\r
+  @param[in]  ImageHandle           The firmware allocated handle for the UEFI image.\r
+  @param[in]  SystemTable           The pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverEntryPoint (\r
+  IN EFI_HANDLE             ImageHandle,\r
+  IN EFI_SYSTEM_TABLE       *SystemTable\r
+  )\r
+{\r
+  return EfiLibInstallDriverBindingComponentName2 (\r
+           ImageHandle,\r
+           SystemTable,\r
+           &gMtftp6DriverBinding,\r
+           ImageHandle,\r
+           &gMtftp6ComponentName,\r
+           &gMtftp6ComponentName2\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle. This service\r
+  is called by the EFI boot service ConnectController(). In\r
+  order to make drivers as small as possible, there are calling\r
+  restrictions for this service. ConnectController() must\r
+  follow these calling restrictions. If any other agent wishes to call\r
+  Supported(), it must also follow these calling restrictions.\r
+\r
+  @param[in]  This                Protocol instance pointer.\r
+  @param[in]  ControllerHandle    Handle of device to test\r
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child.\r
+                                  device to start.\r
+\r
+  @retval EFI_SUCCESS         This driver supports this device.\r
+  @retval Others              This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  )\r
+{\r
+  return gBS->OpenProtocol (\r
+                Controller,\r
+                &gEfiUdp6ServiceBindingProtocolGuid,\r
+                NULL,\r
+                This->DriverBindingHandle,\r
+                Controller,\r
+                EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                );\r
+}\r
+\r
+\r
+/**\r
+  Start this driver on ControllerHandle. This service is called by the\r
+  EFI boot service ConnectController(). In order to make\r
+  drivers as small as possible, there are calling restrictions for\r
+  this service. ConnectController() must follow these\r
+  calling restrictions. If any other agent wishes to call Start() it\r
+  must also follow these calling restrictions.\r
+\r
+  @param[in]  This                 Protocol instance pointer.\r
+  @param[in]  ControllerHandle     Handle of device to bind driver to.\r
+  @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child\r
+                                   device to start.\r
+\r
+  @retval EFI_SUCCESS          This driver is added to ControllerHandle.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.\r
+  @retval Others               This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
+  )\r
+{\r
+  MTFTP6_SERVICE            *Service;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // Directly return if driver is already running on this Nic handle.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiMtftp6ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  //\r
+  // Create Mtftp6 service for this Nic handle\r
+  //\r
+  Status = Mtftp6CreateService (\r
+             Controller,\r
+             This->DriverBindingHandle,\r
+             &Service\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  ASSERT (Service != NULL);\r
+\r
+  //\r
+  // Start the internal timer to track the packet retransmission.\r
+  //\r
+  Status = gBS->SetTimer (\r
+                  Service->Timer,\r
+                  TimerPeriodic,\r
+                  TICKS_PER_SECOND\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Install the Mtftp6 service on the Nic handle.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Controller,\r
+                  &gEfiMtftp6ServiceBindingProtocolGuid,\r
+                  &Service->ServiceBinding,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  Mtftp6DestroyService (Service);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stop this driver on ControllerHandle. This service is called by the\r
+  EFI boot service DisconnectController(). In order to\r
+  make drivers as small as possible, there are a few calling\r
+  restrictions for this service. DisconnectController()\r
+  must follow these calling restrictions. If any other agent wishes\r
+  to call Stop() it must also follow these calling restrictions.\r
+\r
+  @param[in]  This              Protocol instance pointer.\r
+  @param[in]  ControllerHandle  Handle of device to stop driver on\r
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
+                                children is zero, stop the entire bus driver.\r
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCESS       This driver is removed ControllerHandle.\r
+  @retval EFI_DEVICE_ERROR  An unexpected error.\r
+  @retval Others            This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL *This,\r
+  IN  EFI_HANDLE                  Controller,\r
+  IN  UINTN                       NumberOfChildren,\r
+  IN  EFI_HANDLE                  *ChildHandleBuffer\r
+  )\r
+{\r
+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
+  MTFTP6_SERVICE                *Service;\r
+  MTFTP6_INSTANCE               *Instance;\r
+  EFI_HANDLE                    NicHandle;\r
+  EFI_STATUS                    Status;\r
+  EFI_TPL                       OldTpl;\r
+\r
+  //\r
+  // Locate the Nic handle to retrieve the Mtftp6 private data.\r
+  //\r
+  NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp6ProtocolGuid);\r
+\r
+  if (NicHandle == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  NicHandle,\r
+                  &gEfiMtftp6ServiceBindingProtocolGuid,\r
+                  (VOID **) &ServiceBinding,\r
+                  This->DriverBindingHandle,\r
+                  NicHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Service = MTFTP6_SERVICE_FROM_THIS (ServiceBinding);\r
+\r
+  if (Service->InDestory) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (NumberOfChildren == 0) {\r
+    //\r
+    // Destory the Mtftp6 service if there is no Mtftp6 child instance left.\r
+    //\r
+    Service->InDestory = TRUE;\r
+\r
+    gBS->UninstallProtocolInterface (\r
+           NicHandle,\r
+           &gEfiMtftp6ServiceBindingProtocolGuid,\r
+           ServiceBinding\r
+           );\r
+\r
+    Mtftp6DestroyService (Service);\r
+\r
+  } else {\r
+    //\r
+    // Destory the Mtftp6 child instance one by one.\r
+    //\r
+    while (!IsListEmpty (&Service->Children)) {\r
+      Instance = NET_LIST_HEAD (&Service->Children, MTFTP6_INSTANCE, Link);\r
+      Mtftp6ServiceBindingDestroyChild (ServiceBinding, Instance->Handle);\r
+    }\r
+\r
+    if (Service->ChildrenNum != 0) {\r
+      Status = EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Creates a child handle and installs a protocol.\r
+\r
+  The CreateChild() function installs a protocol on ChildHandle.\r
+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+  @param[in]      This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+                              then a new handle is created. If it is a pointer to an existing\r
+                              UEFI handle, then the protocol is added to the existing UEFI handle.\r
+\r
+  @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+  @retval Others                The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ServiceBindingCreateChild (\r
+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN OUT EFI_HANDLE                    *ChildHandle\r
+  )\r
+{\r
+  MTFTP6_SERVICE            *Service;\r
+  MTFTP6_INSTANCE           *Instance;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+  VOID                      *Udp6;\r
+\r
+  if (This == NULL || ChildHandle == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Service = MTFTP6_SERVICE_FROM_THIS (This);\r
+\r
+  Status = Mtftp6CreateInstance (Service, &Instance);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  ASSERT (Instance != NULL);\r
+\r
+  //\r
+  // Install the Mtftp6 protocol on the new child handle.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiMtftp6ProtocolGuid,\r
+                  &Instance->Mtftp6,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Instance->Handle = *ChildHandle;\r
+\r
+  //\r
+  // Open the Udp6 protocol by child.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Service->DummyUdpIo->UdpHandle,\r
+                  &gEfiUdp6ProtocolGuid,\r
+                  (VOID **) &Udp6,\r
+                  gMtftp6DriverBinding.DriverBindingHandle,\r
+                  Instance->Handle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           Instance->Handle,\r
+           &gEfiMtftp6ProtocolGuid,\r
+           &Instance->Mtftp6,\r
+           NULL\r
+           );\r
+\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Add the new Mtftp6 instance into the children list of Mtftp6 service.\r
+  //\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  InsertTailList (&Service->Children, &Instance->Link);\r
+  Service->ChildrenNum++;\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  Mtftp6DestroyInstance (Instance);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Destroys a child handle with a protocol installed on it.\r
+\r
+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+  last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+  @param[in]  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in]  ChildHandle Handle of the child to destroy.\r
+\r
+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.\r
+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.\r
+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+  @retval Others                The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+  IN EFI_HANDLE                   ChildHandle\r
+  )\r
+{\r
+  MTFTP6_SERVICE            *Service;\r
+  MTFTP6_INSTANCE           *Instance;\r
+  EFI_MTFTP6_PROTOCOL       *Mtftp6;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  if (This == NULL || ChildHandle == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Locate the Nic handle to retrieve the Mtftp6 private data.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ChildHandle,\r
+                  &gEfiMtftp6ProtocolGuid,\r
+                  (VOID **) &Mtftp6,\r
+                  gMtftp6DriverBinding.DriverBindingHandle,\r
+                  ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Instance = MTFTP6_INSTANCE_FROM_THIS (Mtftp6);\r
+  Service  = MTFTP6_SERVICE_FROM_THIS (This);\r
+\r
+  if (Instance->Service != Service) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check whether the instance already in destory state.\r
+  //\r
+  if (Instance->InDestory) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Instance->InDestory = TRUE;\r
+\r
+  gBS->CloseProtocol (\r
+         Service->DummyUdpIo->UdpHandle,\r
+         &gEfiUdp6ProtocolGuid,\r
+         gMtftp6DriverBinding.DriverBindingHandle,\r
+         ChildHandle\r
+         );\r
+\r
+  //\r
+  // Uninstall the MTFTP6 protocol first to enable a top down destruction.\r
+  //\r
+  Status = gBS->UninstallProtocolInterface (\r
+                  ChildHandle,\r
+                  &gEfiMtftp6ProtocolGuid,\r
+                  Mtftp6\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Instance->InDestory = FALSE;\r
+    gBS->RestoreTPL (OldTpl);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Remove the Mtftp6 instance from the children list of Mtftp6 service.\r
+  //\r
+  RemoveEntryList (&Instance->Link);\r
+  Service->ChildrenNum --;\r
+\r
+  Mtftp6DestroyInstance (Instance);\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h
new file mode 100644 (file)
index 0000000..94f73a8
--- /dev/null
@@ -0,0 +1,151 @@
+/** @file\r
+  Driver Binding functions and Service Binding functions\r
+  declaration for Mtftp6 Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_MTFTP6_DRIVER_H__\r
+#define __EFI_MTFTP6_DRIVER_H__\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+\r
+extern EFI_COMPONENT_NAME_PROTOCOL  gMtftp6ComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2;\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle. This service\r
+  is called by the EFI boot service ConnectController(). In\r
+  order to make drivers as small as possible, there are a few calling\r
+  restrictions for this service. ConnectController() must\r
+  follow these calling restrictions. If any other agent wishes to call\r
+  Supported(), it must also follow these calling restrictions.\r
+\r
+  @param[in]  This                Protocol instance pointer.\r
+  @param[in]  ControllerHandle    Handle of device to test.\r
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child\r
+                                  device to start.\r
+\r
+  @retval EFI_SUCCESS         This driver supports this device.\r
+  @retval Others              This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
+  );\r
+\r
+/**\r
+  Start this driver on ControllerHandle. This service is called by the\r
+  EFI boot service ConnectController(). In order to make\r
+  drivers as small as possible, there are calling restrictions for\r
+  this service. ConnectController() must follow these\r
+  calling restrictions. If any other agent wishes to call Start() it\r
+  must also follow these calling restrictions.\r
+\r
+  @param[in]  This                 Protocol instance pointer.\r
+  @param[in]  ControllerHandle     Handle of device to bind driver to.\r
+  @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child\r
+                                   device to start.\r
+\r
+  @retval EFI_SUCCESS          This driver is added to ControllerHandle.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.\r
+  @retval Others               This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
+  );\r
+\r
+/**\r
+  Stop this driver on ControllerHandle. This service is called by the\r
+  EFI boot service DisconnectController(). In order to\r
+  make drivers as small as possible, there are calling\r
+  restrictions for this service. DisconnectController()\r
+  must follow these calling restrictions. If any other agent wishes\r
+  to call Stop(), it must also follow these calling restrictions.\r
+\r
+  @param[in]  This              Protocol instance pointer.\r
+  @param[in]  ControllerHandle  Handle of device to stop driver on\r
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
+                                children is zero, stop the entire bus driver.\r
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCESS       This driver is removed ControllerHandle.\r
+  @retval EFI_DEVICE_ERROR  An unexpected error.\r
+  @retval Others            This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL *This,\r
+  IN  EFI_HANDLE                  Controller,\r
+  IN  UINTN                       NumberOfChildren,\r
+  IN  EFI_HANDLE                  *ChildHandleBuffer\r
+  );\r
+\r
+/**\r
+  Creates a child handle and installs a protocol.\r
+\r
+  The CreateChild() function installs a protocol on ChildHandle.\r
+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+  @param[in]      This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+                              then a new handle is created. If it is a pointer to an existing\r
+                              UEFI handle, then the protocol is added to the existing UEFI handle.\r
+\r
+  @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+  @retval Others                The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ServiceBindingCreateChild (\r
+  IN     EFI_SERVICE_BINDING_PROTOCOL *This,\r
+  IN OUT EFI_HANDLE                   *ChildHandle\r
+  );\r
+\r
+/**\r
+  Destroys a child handle with a protocol installed on it.\r
+\r
+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+  last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+  @param[in]  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in]  ChildHandle Handle of the child to destroy.\r
+\r
+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.\r
+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.\r
+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+  @retval Others                The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+  IN EFI_HANDLE                   ChildHandle\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf b/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf
new file mode 100644 (file)
index 0000000..ecf1f7c
--- /dev/null
@@ -0,0 +1,69 @@
+## @file\r
+#  Component description file for Mtftp6 module.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = Mtftp6Dxe\r
+  FILE_GUID                      = 99F03B99-98D8-49dd-A8D3-3219D0FFE41E\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = Mtftp6DriverEntryPoint\r
+  UNLOAD_IMAGE                   = NetLibDefaultUnload\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+#  DRIVER_BINDING                =  gMtftp6DriverBinding\r
+#  COMPONENT_NAME                =  gMtftp6ComponentName\r
+#  COMPONENT_NAME2               =  gMtftp6ComponentName2\r
+#\r
+\r
+[Sources]\r
+  Mtftp6Driver.c\r
+  Mtftp6Driver.h\r
+  Mtftp6Impl.c\r
+  Mtftp6Impl.h\r
+  Mtftp6Option.c\r
+  Mtftp6Option.h\r
+  Mtftp6Support.h\r
+  Mtftp6Support.c\r
+  Mtftp6Rrq.c\r
+  Mtftp6Wrq.c\r
+  ComponentName.c\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  UefiLib\r
+  BaseLib\r
+  UefiBootServicesTableLib\r
+  UefiRuntimeServicesTableLib\r
+  UefiDriverEntryPoint\r
+  DebugLib\r
+  NetLib\r
+  UdpIoLib\r
+\r
+\r
+[Protocols]\r
+  gEfiUdp6ServiceBindingProtocolGuid\r
+  gEfiUdp6ProtocolGuid\r
+  gEfiMtftp6ServiceBindingProtocolGuid\r
+  gEfiMtftp6ProtocolGuid\r
+\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c
new file mode 100644 (file)
index 0000000..fcbbf11
--- /dev/null
@@ -0,0 +1,634 @@
+/** @file\r
+  This EFI_MTFTP6_PROTOCOL interface implementation.\r
+\r
+  It supports the following RFCs:\r
+   RFC1350 - THE TFTP PROTOCOL (REVISION 2)\r
+   RFC2090 - TFTP Multicast Option\r
+   RFC2347 - TFTP Option Extension\r
+   RFC2348 - TFTP Blocksize Option\r
+   RFC2349 - TFTP Timeout Interval and Transfer Size Options\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate = {\r
+  EfiMtftp6GetModeData,\r
+  EfiMtftp6Configure,\r
+  EfiMtftp6GetInfo,\r
+  EfiMtftp6ParseOptions,\r
+  EfiMtftp6ReadFile,\r
+  EfiMtftp6WriteFile,\r
+  EfiMtftp6ReadDirectory,\r
+  EfiMtftp6Poll\r
+  };\r
+\r
+/**\r
+  Returns the current operating mode data for the MTFTP6 instance.\r
+\r
+  The GetModeData() function returns the current operating mode and\r
+  cached data packet for the MTFTP6 instance.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[out] ModeData           The buffer in which the EFI MTFTPv6 Protocol driver mode\r
+                                 data is returned.\r
+\r
+  @retval  EFI_SUCCESS           The configuration data was returned successfully.\r
+  @retval  EFI_OUT_OF_RESOURCES  The required mode data could not be allocated.\r
+  @retval  EFI_INVALID_PARAMETER This is NULL or ModeData is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6GetModeData (\r
+  IN  EFI_MTFTP6_PROTOCOL    *This,\r
+  OUT EFI_MTFTP6_MODE_DATA   *ModeData\r
+  )\r
+{\r
+  MTFTP6_INSTANCE  *Instance;\r
+  EFI_TPL          OldTpl;\r
+\r
+  if (This == NULL || ModeData == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl   = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Instance = MTFTP6_INSTANCE_FROM_THIS (This);\r
+\r
+  //\r
+  // Copy back the configure data if the instance already configured.\r
+  //\r
+  if (Instance->Config != NULL) {\r
+    CopyMem (\r
+      &ModeData->ConfigData,\r
+      Instance->Config,\r
+      sizeof (EFI_MTFTP6_CONFIG_DATA)\r
+      );\r
+  } else {\r
+    ZeroMem (\r
+      &ModeData->ConfigData,\r
+      sizeof (EFI_MTFTP6_CONFIG_DATA)\r
+      );\r
+  }\r
+\r
+  //\r
+  // Set the current support options in mode data.\r
+  //\r
+  ModeData->SupportedOptionCount = MTFTP6_SUPPORTED_OPTIONS_NUM;\r
+  ModeData->SupportedOptions     = (UINT8 **) mMtftp6SupportedOptions;\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Initializes, changes, or resets the default operational setting for\r
+  this EFI MTFTPv6 Protocol driver instance.\r
+\r
+  The Configure() function is used to set and change the configuration\r
+  data for this EFI MTFTPv6 Protocol driver instance. The configuration\r
+  data can be reset to startup defaults by calling Configure() with\r
+  MtftpConfigData set to NULL. Whenever the instance is reset, any\r
+  pending operation is aborted. By changing the EFI MTFTPv6 Protocol\r
+  driver instance configuration data, the client can connect to\r
+  different MTFTPv6 servers. The configuration parameters in\r
+  MtftpConfigData are used as the default parameters in later MTFTPv6\r
+  operations and can be overridden in later operations.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  MtftpConfigData    Pointer to the configuration data structure.\r
+\r
+  @retval  EFI_SUCCESS           The EFI MTFTPv6 Protocol instance was configured successfully.\r
+  @retval  EFI_INVALID_PARAMETER One or more following conditions are TRUE:\r
+                                 - This is NULL.\r
+                                 - MtftpConfigData.StationIp is neither zero nor one\r
+                                   of the configured IP addresses in the underlying IPv6 driver.\r
+                                 - MtftpCofigData.ServerIp is not a valid IPv6 unicast address.\r
+                                 Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_ACCESS_DENIED     - The configuration could not be changed at this time because there\r
+                                   is some MTFTP background operation in progress.\r
+                                 - MtftpCofigData.LocalPort is already in use.\r
+                                 Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source\r
+                                 address for this instance, but no source address was available for use.\r
+  @retval  EFI_OUT_OF_RESOURCES  The EFI MTFTPv6 Protocol driver instance data could not be\r
+                                 allocated.\r
+                                 Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred. The EFI\r
+                                 MTFTPv6 Protocol driver instance is not configured.\r
+                                 Note: It is not defined in the UEFI 2.3 Specification.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6Configure (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData     OPTIONAL\r
+  )\r
+{\r
+  MTFTP6_SERVICE            *Service;\r
+  MTFTP6_INSTANCE           *Instance;\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+  EFI_UDP6_CONFIG_DATA      Udp6Cfg;\r
+  EFI_STATUS                Status;\r
+  EFI_TPL                   OldTpl;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (MtftpConfigData != NULL && !NetIp6IsValidUnicast (&MtftpConfigData->ServerIp)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl   = gBS->RaiseTPL (TPL_CALLBACK);\r
+  Instance = MTFTP6_INSTANCE_FROM_THIS (This);\r
+  Service  = Instance->Service;\r
+  Status   = EFI_SUCCESS;\r
+\r
+  if (MtftpConfigData == NULL) {\r
+    //\r
+    // Configure the instance as NULL to abort the current session.\r
+    //\r
+    Mtftp6OperationClean (Instance, EFI_ABORTED);\r
+    FreePool (Instance->Config);\r
+    Instance->Config = NULL;\r
+  } else {\r
+    //\r
+    // It's not allowed to configure one instance twice without configure null.\r
+    //\r
+    if (Instance->Config != NULL) {\r
+      Status = EFI_ACCESS_DENIED;\r
+      goto ON_EXIT;\r
+    }\r
+    //\r
+    // Allocate the configure buffer of the instance and store the user's data.\r
+    //\r
+    Instance->Config = AllocateZeroPool (sizeof (EFI_MTFTP6_CONFIG_DATA));\r
+\r
+    if (Instance->Config == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    CopyMem (Instance->Config, MtftpConfigData, sizeof (EFI_MTFTP6_CONFIG_DATA));\r
+\r
+    //\r
+    // Don't configure the udpio here because each operation might override\r
+    // the configuration, so delay udpio configuration in each operation.\r
+    //\r
+    Instance->UdpIo = UdpIoCreateIo (\r
+                        Service->Controller,\r
+                        Service->Image,\r
+                        Mtftp6ConfigDummyUdpIo,\r
+                        UDP_IO_UDP6_VERSION,\r
+                        NULL\r
+                        );\r
+\r
+    if (Instance->UdpIo == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    //\r
+    // Continue to configure the downside Udp6 instance by user's data.\r
+    //\r
+    ZeroMem (&Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+    Udp6Cfg.AcceptPromiscuous  = FALSE;\r
+    Udp6Cfg.AcceptAnyPort      = FALSE;\r
+    Udp6Cfg.AllowDuplicatePort = FALSE;\r
+    Udp6Cfg.TrafficClass       = 0;\r
+    Udp6Cfg.HopLimit           = 128;\r
+    Udp6Cfg.ReceiveTimeout     = 0;\r
+    Udp6Cfg.TransmitTimeout    = 0;\r
+    Udp6Cfg.StationPort        = Instance->Config->LocalPort;\r
+    Udp6Cfg.RemotePort         = Instance->Config->InitialServerPort;\r
+\r
+    CopyMem (\r
+      &Udp6Cfg.StationAddress,\r
+      &Instance->Config->StationIp,\r
+      sizeof(EFI_IPv6_ADDRESS)\r
+      );\r
+\r
+    CopyMem (\r
+      &Udp6Cfg.RemoteAddress,\r
+      &Instance->Config->ServerIp,\r
+      sizeof (EFI_IPv6_ADDRESS)\r
+      );\r
+\r
+    Udp6   = Instance->UdpIo->Protocol.Udp6;\r
+    Status = Udp6->Configure (Udp6, &Udp6Cfg);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+  if (EFI_ERROR (Status)) {\r
+    if (Instance->Config != NULL) {\r
+      FreePool (Instance->Config);\r
+      Instance->Config = NULL;\r
+    }\r
+    if (Instance->UdpIo != NULL) {\r
+      UdpIoFreeIo (Instance->UdpIo);\r
+      Instance->UdpIo = NULL;\r
+    }\r
+  }\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Get the information of the download from the server.\r
+\r
+  The GetInfo() function assembles an MTFTPv6 request packet\r
+  with options, sends it to the MTFTPv6 server, and may return\r
+  an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries\r
+  occur only if no response packets are received from the MTFTPv6\r
+  server before the timeout expires.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  OverrideData       Data that is used to override the existing parameters. If NULL, the\r
+                                 default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure()\r
+                                 function are used.\r
+  @param[in]  Filename           Pointer to ASCIIZ file name string.\r
+  @param[in]  ModeStr            Pointer to ASCIIZ mode string. If NULL, octet will be used.\r
+  @param[in]  OptionCount        Number of option/value string pairs in OptionList.\r
+  @param[in]  OptionList         Pointer to array of option/value string pairs. Ignored if\r
+                                 OptionCount is zero.\r
+  @param[out] PacketLength       The number of bytes in the returned packet.\r
+  @param[out] Packet             The pointer to the received packet. This buffer must be freed by\r
+                                 the caller.\r
+\r
+  @retval  EFI_SUCCESS              An MTFTPv6 OACK packet was received and is in the Packet.\r
+                                    Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_INVALID_PARAMETER    One or more of the following conditions is TRUE:\r
+                                    - This is NULL.\r
+                                    - Filename is NULL.\r
+                                    - OptionCount is not zero and OptionList is NULL.\r
+                                    - One or more options in OptionList have wrong format.\r
+                                    - PacketLength is NULL.\r
+                                    - OverrideData.ServerIp is not valid unicast IPv6 addresses.\r
+  @retval  EFI_UNSUPPORTED          One or more options in the OptionList are unsupported by\r
+                                    this implementation.\r
+  @retval  EFI_NOT_STARTED          The EFI MTFTPv6 Protocol driver has not been started.\r
+  @retval  EFI_NO_MAPPING           The underlying IPv6 driver was responsible for choosing a source\r
+                                    address for this instance, but no source address was available for use.\r
+  @retval  EFI_ACCESS_DENIED        The previous operation has not completed yet.\r
+  @retval  EFI_OUT_OF_RESOURCES     Required system resources could not be allocated.\r
+  @retval  EFI_TFTP_ERROR           An MTFTPv6 ERROR packet was received and is in the Packet.\r
+  @retval  EFI_NETWORK_UNREACHABLE  An ICMP network unreachable error packet was received and the Packet is set to NULL.\r
+                                    Note: It is not defined in UEFI 2.3 Specification.\r
+  @retval  EFI_HOST_UNREACHABLE     An ICMP host unreachable error packet was received and the Packet is set to NULL.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received and the Packet is set to NULL.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_PORT_UNREACHABLE     An ICMP port unreachable error packet was received and the Packet is set to NULL.\r
+  @retval  EFI_ICMP_ERROR           Some other ICMP ERROR packet was received and the Packet is set to NULL.\r
+                                    Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_PROTOCOL_ERROR       An unexpected MTFTPv6 packet was received and is in the Packet.\r
+  @retval  EFI_TIMEOUT              No responses were received from the MTFTPv6 server.\r
+  @retval  EFI_DEVICE_ERROR         An unexpected network error or system error occurred.\r
+  @retval  EFI_NO_MEDIA             There was a media error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6GetInfo (\r
+  IN  EFI_MTFTP6_PROTOCOL      *This,\r
+  IN  EFI_MTFTP6_OVERRIDE_DATA *OverrideData         OPTIONAL,\r
+  IN  UINT8                    *Filename,\r
+  IN  UINT8                    *ModeStr              OPTIONAL,\r
+  IN  UINT8                    OptionCount,\r
+  IN  EFI_MTFTP6_OPTION        *OptionList           OPTIONAL,\r
+  OUT UINT32                   *PacketLength,\r
+  OUT EFI_MTFTP6_PACKET        **Packet              OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_MTFTP6_TOKEN          Token;\r
+  MTFTP6_GETINFO_CONTEXT    Context;\r
+\r
+  if (This == NULL ||\r
+      Filename == NULL ||\r
+      PacketLength == NULL ||\r
+      (OptionCount != 0 && OptionList == NULL) ||\r
+      (OverrideData != NULL && !NetIp6IsValidUnicast (&OverrideData->ServerIp))\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Packet != NULL) {\r
+    *Packet = NULL;\r
+  }\r
+\r
+  *PacketLength         = 0;\r
+\r
+  Context.Packet        = Packet;\r
+  Context.PacketLen     = PacketLength;\r
+  Context.Status        = EFI_SUCCESS;\r
+\r
+  //\r
+  // Fill fields of the Token for GetInfo operation.\r
+  //\r
+  Token.Status          = EFI_SUCCESS;\r
+  Token.Event           = NULL;\r
+  Token.OverrideData    = OverrideData;\r
+  Token.Filename        = Filename;\r
+  Token.ModeStr         = ModeStr;\r
+  Token.OptionCount     = OptionCount;\r
+  Token.OptionList      = OptionList;\r
+  Token.BufferSize      = 0;\r
+  Token.Buffer          = NULL;\r
+  Token.Context         = &Context;\r
+  Token.CheckPacket     = Mtftp6CheckPacket;\r
+  Token.TimeoutCallback = NULL;\r
+  Token.PacketNeeded    = NULL;\r
+\r
+  //\r
+  // Start the GetInfo operation by issue the Token.\r
+  //\r
+  Status = Mtftp6OperationStart (This, &Token, EFI_MTFTP6_OPCODE_RRQ);\r
+\r
+  if (Status == EFI_ABORTED) {\r
+    //\r
+    // Return the status if failed to issue.\r
+    //\r
+    return Context.Status;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Parse the options in an MTFTPv6 OACK packet.\r
+\r
+  The ParseOptions() function parses the option fields in an MTFTPv6 OACK\r
+  packet and returns the number of options that were found, and optionally,\r
+  a list of pointers to the options in the packet. If one or more of the\r
+  option fields are not valid, then EFI_PROTOCOL_ERROR is returned and\r
+  *OptionCount and *OptionList stop at the last valid option.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  PacketLen          Length of the OACK packet to be parsed.\r
+  @param[in]  Packet             Pointer to the OACK packet to be parsed.\r
+  @param[out] OptionCount        Pointer to the number of options in the following OptionList.\r
+  @param[out] OptionList         Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the\r
+                                 OptionList points to the corresponding MTFTP option buffer\r
+                                 in the Packet. Call the EFI Boot Service FreePool() to\r
+                                 release the OptionList if the options in this OptionList\r
+                                 are not needed anymore.\r
+\r
+  @retval  EFI_SUCCESS           The OACK packet was valid and the OptionCount and\r
+                                 OptionList parameters have been updated.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+                                 - PacketLen is 0.\r
+                                 - Packet is NULL or Packet is not a valid MTFTPv6 packet.\r
+                                 - OptionCount is NULL.\r
+  @retval  EFI_NOT_FOUND         No options were found in the OACK packet.\r
+  @retval  EFI_OUT_OF_RESOURCES  Storage for the OptionList array can not be allocated.\r
+  @retval  EFI_PROTOCOL_ERROR    One or more of the option fields is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ParseOptions (\r
+  IN     EFI_MTFTP6_PROTOCOL    *This,\r
+  IN     UINT32                 PacketLen,\r
+  IN     EFI_MTFTP6_PACKET      *Packet,\r
+  OUT    UINT32                 *OptionCount,\r
+  OUT    EFI_MTFTP6_OPTION      **OptionList          OPTIONAL\r
+  )\r
+{\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return Mtftp6ParseStart (Packet, PacketLen, OptionCount, OptionList);\r
+}\r
+\r
+\r
+/**\r
+  Download a file from an MTFTPv6 server.\r
+\r
+  The ReadFile() function is used to initialize and start an MTFTPv6 download\r
+  process, and optionally, wait for completion. When the download operation\r
+  completes, whether successfully or not, the Token.Status field is updated\r
+  by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it\r
+  is not NULL.\r
+  Data can be downloaded from the MTFTPv6 server into either of the following\r
+  locations:\r
+  - A fixed buffer that is pointed to by Token.Buffer\r
+  - A download service function that is pointed to by Token.CheckPacket.\r
+  If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket\r
+  will be called first. If the call is successful, the packet will be stored\r
+  in Token.Buffer.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to the token structure to provide the parameters that are\r
+                                 used in this operation.\r
+\r
+  @retval  EFI_SUCCESS              The data file has been transferred successfully.\r
+  @retval  EFI_OUT_OF_RESOURCES     Required system resources could not be allocated.\r
+  @retval  EFI_BUFFER_TOO_SMALL     BufferSize is not zero but not large enough to hold the\r
+                                    downloaded data in downloading process.\r
+                                    Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_ABORTED              Current operation is aborted by user.\r
+  @retval  EFI_NETWORK_UNREACHABLE  An ICMP network unreachable error packet was received.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_HOST_UNREACHABLE     An ICMP host unreachable error packet was received.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_PORT_UNREACHABLE     An ICMP port unreachable error packet was received.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_ICMP_ERROR           An ICMP ERROR packet was received.\r
+  @retval  EFI_TIMEOUT              No responses were received from the MTFTPv6 server.\r
+  @retval  EFI_TFTP_ERROR           An MTFTPv6 ERROR packet was received.\r
+  @retval  EFI_DEVICE_ERROR         An unexpected network error or system error occurred.\r
+  @retval  EFI_NO_MEDIA             There was a media error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ReadFile (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token\r
+  )\r
+{\r
+  return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_RRQ);\r
+}\r
+\r
+\r
+/**\r
+  Send a file to an MTFTPv6 server.\r
+\r
+  The WriteFile() function is used to initialize an uploading operation\r
+  with the given option list and optionally wait for completion. If one\r
+  or more of the options is not supported by the server, the unsupported\r
+  options are ignored and a standard TFTP process starts instead. When\r
+  the upload process completes, whether successfully or not, Token.Event\r
+  is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status.\r
+  The caller can supply the data to be uploaded in the following two modes:\r
+  - Through the user-provided buffer\r
+  - Through a callback function\r
+  With the user-provided buffer, the Token.BufferSize field indicates\r
+  the length of the buffer, and the driver will upload the data in the\r
+  buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver\r
+  will call this callback function to get more data from the user to upload.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to the token structure to provide the parameters that are\r
+                                 used in this operation.\r
+\r
+  @retval  EFI_SUCCESS           The upload session has started.\r
+  @retval  EFI_UNSUPPORTED       The operation is not supported by this implementation.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token.Filename is NULL.\r
+                                 - Token.OptionCount is not zero and Token.OptionList is NULL.\r
+                                 - One or more options in Token.OptionList have wrong format.\r
+                                 - Token.Buffer and Token.PacketNeeded are both NULL.\r
+                                 - Token.OverrideData.ServerIp is not a valid unicast IPv6 address.\r
+  @retval  EFI_UNSUPPORTED       One or more options in the Token.OptionList are not\r
+                                 supported by this implementation.\r
+  @retval  EFI_NOT_STARTED       The EFI MTFTPv6 Protocol driver has not been started.\r
+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source\r
+                                 address for this instance, but no source address was available for use.\r
+  @retval  EFI_ALREADY_STARTED   This Token is already being used in another MTFTPv6 session.\r
+  @retval  EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval  EFI_ACCESS_DENIED     The previous operation has not completed yet.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6WriteFile (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token\r
+  )\r
+{\r
+  return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_WRQ);\r
+}\r
+\r
+\r
+/**\r
+  Download a data file directory from an MTFTPv6 server.\r
+\r
+  The ReadDirectory() function is used to return a list of files on the\r
+  MTFTPv6 server that are logically (or operationally) related to\r
+  Token.Filename. The directory request packet that is sent to the server\r
+  is built with the option list that was provided by the caller, if present.\r
+  The file information that the server returns is put into either of\r
+  the following locations:\r
+  - A fixed buffer that is pointed to by Token.Buffer.\r
+  - A download service function that is pointed to by Token.CheckPacket.\r
+  If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket\r
+  will be called first. If the call is successful, the packet will be stored\r
+  in Token.Buffer.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to the token structure to provide the parameters that are\r
+                                 used in this operation.\r
+\r
+  @retval  EFI_SUCCESS           The MTFTPv6 related file "directory" has been downloaded.\r
+  @retval  EFI_UNSUPPORTED       The EFI MTFTPv6 Protocol driver does not support this function.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token.Filename is NULL.\r
+                                 - Token.OptionCount is not zero and Token.OptionList is NULL.\r
+                                 - One or more options in Token.OptionList have wrong format.\r
+                                 - Token.Buffer and Token.CheckPacket are both NULL.\r
+                                 - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.\r
+  @retval  EFI_UNSUPPORTED       One or more options in the Token.OptionList are not\r
+                                 supported by this implementation.\r
+  @retval  EFI_NOT_STARTED       The EFI MTFTPv6 Protocol driver has not been started.\r
+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source\r
+                                 address for this instance, but no source address was available for use.\r
+  @retval  EFI_ALREADY_STARTED   This Token is already being used in another MTFTPv6 session.\r
+  @retval  EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval  EFI_ACCESS_DENIED     The previous operation has not completed yet.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ReadDirectory (\r
+  IN EFI_MTFTP6_PROTOCOL        *This,\r
+  IN EFI_MTFTP6_TOKEN           *Token\r
+  )\r
+{\r
+  return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_DIR);\r
+}\r
+\r
+\r
+/**\r
+  Polls for incoming data packets and processes outgoing data packets.\r
+\r
+  The Poll() function can be used by network drivers and applications\r
+  to increase the rate that data packets are moved between the\r
+  communications device and the transmit and receive queues. In some\r
+  systems, the periodic timer event in the managed network driver may\r
+  not poll the underlying communications device fast enough to transmit\r
+  and/or receive all data packets without missing incoming packets or\r
+  dropping outgoing packets. Drivers and applications that are\r
+  experiencing packet loss should try calling the Poll() function\r
+  more often.\r
+\r
+  @param[in]  This                   The MTFTP6 protocol instance.\r
+\r
+\r
+  @retval  EFI_SUCCESS           Incoming or outgoing data was processed.\r
+  @retval  EFI_NOT_STARTED       This EFI MTFTPv6 Protocol instance has not been started.\r
+  @retval  EFI_INVALID_PARAMETER This is NULL.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+  @retval  EFI_TIMEOUT           Data was dropped out of the transmit and/or receive queue.\r
+                                 Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6Poll (\r
+  IN EFI_MTFTP6_PROTOCOL    *This\r
+  )\r
+{\r
+  MTFTP6_INSTANCE           *Instance;\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = MTFTP6_INSTANCE_FROM_THIS (This);\r
+\r
+  //\r
+  // Check the instance whether configured or in destory.\r
+  //\r
+  if (Instance->Config == NULL) {\r
+    return EFI_NOT_STARTED;\r
+  } else if (Instance->InDestory) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Udp6 = Instance->UdpIo->Protocol.Udp6;\r
+\r
+  return Udp6->Poll (Udp6);\r
+}\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h
new file mode 100644 (file)
index 0000000..bf924a9
--- /dev/null
@@ -0,0 +1,469 @@
+/** @file\r
+  Mtftp6 internal data structure and definition declaration.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_MTFTP6_IMPL_H__\r
+#define __EFI_MTFTP6_IMPL_H__\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Udp6.h>\r
+#include <Protocol/Mtftp6.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DriverBinding.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+typedef struct _MTFTP6_SERVICE  MTFTP6_SERVICE;\r
+typedef struct _MTFTP6_INSTANCE MTFTP6_INSTANCE;\r
+\r
+#include "Mtftp6Driver.h"\r
+#include "Mtftp6Option.h"\r
+#include "Mtftp6Support.h"\r
+\r
+#define MTFTP6_SERVICE_SIGNATURE       SIGNATURE_32 ('M', 'F', '6', 'S')\r
+#define MTFTP6_INSTANCE_SIGNATURE      SIGNATURE_32 ('M', 'F', '6', 'I')\r
+\r
+#define MTFTP6_DEFAULT_SERVER_CMD_PORT 69\r
+#define MTFTP6_DEFAULT_TIMEOUT         3\r
+#define MTFTP6_GET_MAPPING_TIMEOUT     3\r
+#define MTFTP6_DEFAULT_MAX_RETRY       5\r
+#define MTFTP6_DEFAULT_BLK_SIZE        512\r
+#define MTFTP6_TICK_PER_SECOND         10000000U\r
+\r
+#define MTFTP6_SERVICE_FROM_THIS(a)    CR (a, MTFTP6_SERVICE, ServiceBinding, MTFTP6_SERVICE_SIGNATURE)\r
+#define MTFTP6_INSTANCE_FROM_THIS(a)   CR (a, MTFTP6_INSTANCE, Mtftp6, MTFTP6_INSTANCE_SIGNATURE)\r
+\r
+extern EFI_MTFTP6_PROTOCOL             gMtftp6ProtocolTemplate;\r
+\r
+typedef struct _MTFTP6_GETINFO_CONTEXT{\r
+  EFI_MTFTP6_PACKET             **Packet;\r
+  UINT32                        *PacketLen;\r
+  EFI_STATUS                    Status;\r
+} MTFTP6_GETINFO_CONTEXT;\r
+\r
+//\r
+// Control block for MTFTP6 instance, it's per configuration data.\r
+//\r
+struct _MTFTP6_INSTANCE {\r
+  UINT32                        Signature;\r
+  EFI_HANDLE                    Handle;\r
+  LIST_ENTRY                    Link;\r
+  EFI_MTFTP6_PROTOCOL           Mtftp6;\r
+  MTFTP6_SERVICE                *Service;\r
+  EFI_MTFTP6_CONFIG_DATA        *Config;\r
+\r
+  EFI_MTFTP6_TOKEN              *Token;\r
+  MTFTP6_EXT_OPTION_INFO        ExtInfo;\r
+\r
+  UINT16                        BlkSize;\r
+  UINT16                        LastBlk;\r
+  LIST_ENTRY                    BlkList;\r
+\r
+  EFI_IPv6_ADDRESS              ServerIp;\r
+  UINT16                        ServerCmdPort;\r
+  UINT16                        ServerDataPort;\r
+  UDP_IO                        *UdpIo;\r
+\r
+  EFI_IPv6_ADDRESS              McastIp;\r
+  UINT16                        McastPort;\r
+  UDP_IO                        *McastUdpIo;\r
+\r
+  NET_BUF                       *LastPacket;\r
+  UINT32                        CurRetry;\r
+  UINT32                        MaxRetry;\r
+  UINT32                        PacketToLive;\r
+  UINT32                        Timeout;\r
+\r
+  EFI_TPL                       OldTpl;\r
+  BOOLEAN                       IsTransmitted;\r
+  BOOLEAN                       IsMaster;\r
+  BOOLEAN                       InDestory;\r
+};\r
+\r
+//\r
+// Control block for MTFTP6 service, it's per Nic handle.\r
+//\r
+struct _MTFTP6_SERVICE {\r
+  UINT32                        Signature;\r
+  EFI_SERVICE_BINDING_PROTOCOL  ServiceBinding;\r
+  EFI_HANDLE                    Controller;\r
+  EFI_HANDLE                    Image;\r
+\r
+  UINT16                        ChildrenNum;\r
+  LIST_ENTRY                    Children;\r
+  //\r
+  // It is used to be as internal calculagraph for all instances.\r
+  //\r
+  EFI_EVENT                     Timer;\r
+  //\r
+  // It is used to maintain the parent-child relationship between\r
+  // mtftp driver and udp driver.\r
+  //\r
+  UDP_IO                        *DummyUdpIo;\r
+  BOOLEAN                       InDestory;\r
+};\r
+\r
+/**\r
+  Returns the current operating mode data for the MTFTP6 instance.\r
+\r
+  The GetModeData() function returns the current operating mode and\r
+  cached data packet for the MTFTP6 instance.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[out] ModeData           The buffer in which the EFI MTFTPv6 Protocol driver mode\r
+                                 data is returned.\r
+\r
+  @retval  EFI_SUCCESS           The configuration data was returned successfully.\r
+  @retval  EFI_OUT_OF_RESOURCES  The required mode data could not be allocated.\r
+  @retval  EFI_INVALID_PARAMETER This is NULL, or ModeData is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6GetModeData (\r
+  IN  EFI_MTFTP6_PROTOCOL    *This,\r
+  OUT EFI_MTFTP6_MODE_DATA   *ModeData\r
+  );\r
+\r
+/**\r
+  Initializes, changes, or resets the default operational setting for\r
+  this EFI MTFTPv6 Protocol driver instance.\r
+\r
+  The Configure() function is used to set and change the configuration\r
+  data for this EFI MTFTPv6 Protocol driver instance. The configuration\r
+  data can be reset to startup defaults by calling Configure() with\r
+  MtftpConfigData set to NULL. Whenever the instance is reset, any\r
+  pending operation is aborted. By changing the EFI MTFTPv6 Protocol\r
+  driver instance configuration data, the client can connect to\r
+  different MTFTPv6 servers. The configuration parameters in\r
+  MtftpConfigData are used as the default parameters in later MTFTPv6\r
+  operations and can be overridden in later operations.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  MtftpConfigData    Pointer to the configuration data structure.\r
+\r
+  @retval  EFI_SUCCESS           The EFI MTFTPv6 Protocol instance was configured successfully.\r
+  @retval  EFI_INVALID_PARAMETER One or more following conditions are TRUE:\r
+                                 - This is NULL.\r
+                                 - MtftpConfigData.StationIp is neither zero nor one\r
+                                   of the configured IP addresses in the underlying IPv6 driver.\r
+                                 - MtftpCofigData.ServerIp is not a valid IPv6 unicast address.\r
+                                 Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_ACCESS_DENIED     - The configuration could not be changed at this time because there\r
+                                   is some MTFTP background operation in progress.\r
+                                 - MtftpCofigData.LocalPort is already in use.\r
+                                 Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source\r
+                                 address for this instance, but no source address was available for use.\r
+  @retval  EFI_OUT_OF_RESOURCES  The EFI MTFTPv6 Protocol driver instance data could not be\r
+                                 allocated.\r
+                                 Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred. The EFI\r
+                                 MTFTPv6 Protocol driver instance is not configured.\r
+                                 Note: It is not defined in the UEFI 2.3 Specification.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6Configure (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData     OPTIONAL\r
+  );\r
+\r
+/**\r
+  Get the information of the download from the server.\r
+\r
+  The GetInfo() function assembles an MTFTPv6 request packet\r
+  with options, sends it to the MTFTPv6 server, and may return\r
+  an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries\r
+  occur only if no response packets are received from the MTFTPv6\r
+  server before the timeout expires.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  OverrideData       Data that is used to override the existing parameters. If NULL, the\r
+                                 default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure()\r
+                                 function are used.\r
+  @param[in]  Filename           Pointer to ASCIIZ file name string.\r
+  @param[in]  ModeStr            Pointer to ASCIIZ mode string. If NULL, octet will be used\r
+  @param[in]  OptionCount        Number of option/value string pairs in OptionList.\r
+  @param[in]  OptionList         Pointer to array of option/value string pairs. Ignored if\r
+                                 OptionCount is zero.\r
+  @param[out] PacketLength       The number of bytes in the returned packet.\r
+  @param[out] Packet             The pointer to the received packet. This buffer must be freed by\r
+                                 the caller.\r
+\r
+  @retval  EFI_SUCCESS              An MTFTPv6 OACK packet was received and is in the Packet.\r
+                                    Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_INVALID_PARAMETER    One or more of the following conditions is TRUE:\r
+                                    - This is NULL.\r
+                                    - Filename is NULL.\r
+                                    - OptionCount is not zero and OptionList is NULL.\r
+                                    - One or more options in OptionList have wrong format.\r
+                                    - PacketLength is NULL.\r
+                                    - OverrideData.ServerIp is not a valid unicast IPv6 address.\r
+  @retval  EFI_UNSUPPORTED          One or more options in the OptionList are unsupported by\r
+                                    this implementation.\r
+  @retval  EFI_NOT_STARTED          The EFI MTFTPv6 Protocol driver has not been started.\r
+  @retval  EFI_NO_MAPPING           The underlying IPv6 driver was responsible for choosing a source\r
+                                    address for this instance, but no source address was available for use.\r
+  @retval  EFI_ACCESS_DENIED        The previous operation has not completed yet.\r
+  @retval  EFI_OUT_OF_RESOURCES     Required system resources could not be allocated.\r
+  @retval  EFI_TFTP_ERROR           An MTFTPv6 ERROR packet was received and is in the Packet.\r
+  @retval  EFI_NETWORK_UNREACHABLE  An ICMP network unreachable error packet was received, and the Packet is set to NULL.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_HOST_UNREACHABLE     An ICMP host unreachable error packet was received, and the Packet is set to NULL.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received, and the Packet is set to NULL.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_PORT_UNREACHABLE     An ICMP port unreachable error packet was received, and the Packet is set to NULL.\r
+  @retval  EFI_ICMP_ERROR           Some other ICMP ERROR packet was received, and the Packet is set to NULL.\r
+                                    Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_PROTOCOL_ERROR       An unexpected MTFTPv6 packet was received and is in the Packet.\r
+  @retval  EFI_TIMEOUT              No responses were received from the MTFTPv6 server.\r
+  @retval  EFI_DEVICE_ERROR         An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6GetInfo (\r
+  IN  EFI_MTFTP6_PROTOCOL      *This,\r
+  IN  EFI_MTFTP6_OVERRIDE_DATA *OverrideData         OPTIONAL,\r
+  IN  UINT8                    *Filename,\r
+  IN  UINT8                    *ModeStr              OPTIONAL,\r
+  IN  UINT8                    OptionCount,\r
+  IN  EFI_MTFTP6_OPTION        *OptionList           OPTIONAL,\r
+  OUT UINT32                   *PacketLength,\r
+  OUT EFI_MTFTP6_PACKET        **Packet              OPTIONAL\r
+  );\r
+\r
+/**\r
+  Parse the options in an MTFTPv6 OACK packet.\r
+\r
+  The ParseOptions() function parses the option fields in an MTFTPv6 OACK\r
+  packet and returns the number of options that were found, and optionally,\r
+  a list of pointers to the options in the packet. If one or more of the\r
+  option fields are not valid, then EFI_PROTOCOL_ERROR is returned and\r
+  *OptionCount and *OptionList stop at the last valid option.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  PacketLen          Length of the OACK packet to be parsed.\r
+  @param[in]  Packet             Pointer to the OACK packet to be parsed.\r
+  @param[out] OptionCount        Pointer to the number of options in the following OptionList.\r
+  @param[out] OptionList         Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the\r
+                                 OptionList points to the corresponding MTFTP option buffer\r
+                                 in the Packet. Call the EFI Boot Service FreePool() to\r
+                                 release the OptionList if the options in this OptionList\r
+                                 are not needed any more.\r
+\r
+  @retval  EFI_SUCCESS           The OACK packet was valid and the OptionCount, and\r
+                                 OptionList parameters have been updated.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+                                 - PacketLen is 0.\r
+                                 - Packet is NULL or Packet is not a valid MTFTPv6 packet.\r
+                                 - OptionCount is NULL.\r
+  @retval  EFI_NOT_FOUND         No options were found in the OACK packet.\r
+  @retval  EFI_OUT_OF_RESOURCES  Storage for the OptionList array can not be allocated.\r
+  @retval  EFI_PROTOCOL_ERROR    One or more of the option fields is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ParseOptions (\r
+  IN     EFI_MTFTP6_PROTOCOL    *This,\r
+  IN     UINT32                 PacketLen,\r
+  IN     EFI_MTFTP6_PACKET      *Packet,\r
+  OUT    UINT32                 *OptionCount,\r
+  OUT    EFI_MTFTP6_OPTION      **OptionList          OPTIONAL\r
+  );\r
+\r
+/**\r
+  Download a file from an MTFTPv6 server.\r
+\r
+  The ReadFile() function is used to initialize and start an MTFTPv6 download\r
+  process and optionally wait for completion. When the download operation\r
+  completes, whether successfully or not, the Token.Status field is updated\r
+  by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it\r
+  is not NULL.\r
+  Data can be downloaded from the MTFTPv6 server into either of the following\r
+  locations:\r
+  - A fixed buffer that is pointed to by Token.Buffer.\r
+  - A download service function that is pointed to by Token.CheckPacket.\r
+  If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket\r
+  will be called first. If the call is successful, the packet will be stored\r
+  in Token.Buffer.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to the token structure to provide the parameters that are\r
+                                 used in this operation.\r
+\r
+  @retval  EFI_SUCCESS              The data file has been transferred successfully.\r
+  @retval  EFI_OUT_OF_RESOURCES     Required system resources could not be allocated.\r
+  @retval  EFI_BUFFER_TOO_SMALL     BufferSize is not zero but not large enough to hold the\r
+                                    downloaded data in downloading process.\r
+                                    Note: It does not match the UEFI 2.3 Specification.\r
+  @retval  EFI_ABORTED              Current operation is aborted by user.\r
+  @retval  EFI_NETWORK_UNREACHABLE  An ICMP network unreachable error packet was received.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_HOST_UNREACHABLE     An ICMP host unreachable error packet was received.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_PORT_UNREACHABLE     An ICMP port unreachable error packet was received.\r
+                                    Note: It is not defined in the UEFI 2.3 Specification.\r
+  @retval  EFI_ICMP_ERROR           An ICMP ERROR packet was received.\r
+  @retval  EFI_TIMEOUT              No responses were received from the MTFTPv6 server.\r
+  @retval  EFI_TFTP_ERROR           An MTFTPv6 ERROR packet was received.\r
+  @retval  EFI_DEVICE_ERROR         An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ReadFile (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token\r
+  );\r
+\r
+/**\r
+  Send a file to an MTFTPv6 server.\r
+\r
+  The WriteFile() function is used to initialize an uploading operation\r
+  with the given option list, and optionally, wait for completion. If one\r
+  or more of the options is not supported by the server, the unsupported\r
+  options are ignored and a standard TFTP process starts instead. When\r
+  the upload process completes, whether successfully or not, Token.Event\r
+  is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status.\r
+  The caller can supply the data to be uploaded in the following two modes:\r
+  - Through the user-provided buffer.\r
+  - Through a callback function.\r
+  With the user-provided buffer, the Token.BufferSize field indicates\r
+  the length of the buffer, and the driver will upload the data in the\r
+  buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver\r
+  will call this callback function to get more data from the user to upload.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to the token structure to provide the parameters that are\r
+                                 used in this operation.\r
+\r
+  @retval  EFI_SUCCESS           The upload session has started.\r
+  @retval  EFI_UNSUPPORTED       The operation is not supported by this implementation.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token.Filename is NULL.\r
+                                 - Token.OptionCount is not zero and Token.OptionList is NULL.\r
+                                 - One or more options in Token.OptionList have wrong format.\r
+                                 - Token.Buffer and Token.PacketNeeded are both NULL.\r
+                                 - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.\r
+  @retval  EFI_UNSUPPORTED       One or more options in the Token.OptionList are not\r
+                                 supported by this implementation.\r
+  @retval  EFI_NOT_STARTED       The EFI MTFTPv6 Protocol driver has not been started.\r
+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source\r
+                                 address for this instance, but no source address was available for use.\r
+  @retval  EFI_ALREADY_STARTED   This Token is already being used in another MTFTPv6 session.\r
+  @retval  EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval  EFI_ACCESS_DENIED     The previous operation has not completed yet.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6WriteFile (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token\r
+  );\r
+\r
+/**\r
+  Download a data file directory from an MTFTPv6 server.\r
+\r
+  The ReadDirectory() function is used to return a list of files on the\r
+  MTFTPv6 server that are logically (or operationally) related to\r
+  Token.Filename. The directory request packet that is sent to the server\r
+  is built with the option list that was provided by caller, if present.\r
+  The file information that the server returns is put into either of\r
+  the following locations:\r
+  - A fixed buffer that is pointed to by Token.Buffer.\r
+  - A download service function that is pointed to by Token.CheckPacket.\r
+  If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket\r
+  will be called first. If the call is successful, the packet will be stored\r
+  in Token.Buffer.\r
+\r
+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to the token structure to provide the parameters that are\r
+                                 used in this operation.\r
+\r
+  @retval  EFI_SUCCESS           The MTFTPv6 related file "directory" has been downloaded.\r
+  @retval  EFI_UNSUPPORTED       The EFI MTFTPv6 Protocol driver does not support this function.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token.Filename is NULL.\r
+                                 - Token.OptionCount is not zero and Token.OptionList is NULL.\r
+                                 - One or more options in Token.OptionList have wrong format.\r
+                                 - Token.Buffer and Token.CheckPacket are both NULL.\r
+                                 - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.\r
+  @retval  EFI_UNSUPPORTED       One or more options in the Token.OptionList are not\r
+                                 supported by this implementation.\r
+  @retval  EFI_NOT_STARTED       The EFI MTFTPv6 Protocol driver has not been started.\r
+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source\r
+                                 address for this instance, but no source address was available for use.\r
+  @retval  EFI_ALREADY_STARTED   This Token is already being used in another MTFTPv6 session.\r
+  @retval  EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.\r
+  @retval  EFI_ACCESS_DENIED     The previous operation has not completed yet.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ReadDirectory (\r
+  IN EFI_MTFTP6_PROTOCOL        *This,\r
+  IN EFI_MTFTP6_TOKEN           *Token\r
+  );\r
+\r
+/**\r
+  Polls for incoming data packets and processes outgoing data packets.\r
+\r
+  The Poll() function can be used by network drivers and applications\r
+  to increase the rate that data packets are moved between the\r
+  communications device and the transmit and receive queues.In some\r
+  systems, the periodic timer event in the managed network driver may\r
+  not poll the underlying communications device fast enough to transmit\r
+  and/or receive all data packets without missing incoming packets or\r
+  dropping outgoing packets. Drivers and applications that are\r
+  experiencing packet loss should try calling the Poll() function\r
+  more often.\r
+\r
+  @param[in]  This                   The MTFTP6 protocol instance.\r
+\r
+\r
+  @retval  EFI_SUCCESS           Incoming or outgoing data was processed.\r
+  @retval  EFI_NOT_STARTED       This EFI MTFTPv6 Protocol instance has not been started.\r
+  @retval  EFI_INVALID_PARAMETER This is NULL.\r
+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+  @retval  EFI_TIMEOUT           Data was dropped out of the transmit and/or receive queue.\r
+                                 Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6Poll (\r
+  IN EFI_MTFTP6_PROTOCOL    *This\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c
new file mode 100644 (file)
index 0000000..0dcf546
--- /dev/null
@@ -0,0 +1,416 @@
+/** @file\r
+  Mtftp6 option parse functions implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM] = {\r
+  "blksize",\r
+  "timeout",\r
+  "tsize",\r
+  "multicast"\r
+};\r
+\r
+\r
+/**\r
+  Parse the NULL terminated ASCII string of multicast option.\r
+\r
+  @param[in]  Str           The pointer to the Ascii string of multicast option.\r
+  @param[in]  ExtInfo       The pointer to the option information to be filled.\r
+\r
+  @retval EFI_SUCCESS            Parse the multicast option successfully.\r
+  @retval EFI_INVALID_PARAMETER  The string is malformatted.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to perform the operation due to lack of\r
+                                 resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseMcastOption (\r
+  IN UINT8                  *Str,\r
+  IN MTFTP6_EXT_OPTION_INFO *ExtInfo\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  UINT32                    Num;\r
+  CHAR8                     *Ip6Str;\r
+  CHAR8                     *TempStr;\r
+\r
+  //\r
+  // The multicast option is formated like "addr,port,mc"\r
+  // The server can also omit the ip and port, use ",,1"\r
+  //\r
+  if (*Str == ',') {\r
+\r
+    ZeroMem (&ExtInfo->McastIp, sizeof (EFI_IPv6_ADDRESS));\r
+  } else {\r
+\r
+    Ip6Str = (CHAR8 *) AllocateCopyPool (AsciiStrSize ((CHAR8 *) Str), Str);\r
+    if (Ip6Str == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    //\r
+    // The IPv6 address locates before comma in the input Str.\r
+    //\r
+    TempStr = Ip6Str;\r
+    while ((*TempStr != '\0') && (*TempStr != ',')) {\r
+      TempStr++;\r
+    }\r
+\r
+    *TempStr = '\0';\r
+\r
+    Status = NetLibAsciiStrToIp6 (Ip6Str, &ExtInfo->McastIp);\r
+    FreePool (Ip6Str);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    while ((*Str != '\0') && (*Str != ',')) {\r
+      Str++;\r
+    }\r
+  }\r
+\r
+  if (*Str != ',') {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Str++;\r
+\r
+  //\r
+  // Convert the port setting. the server can send us a port number or\r
+  // empty string. such as the port in ",,1"\r
+  //\r
+  if (*Str == ',') {\r
+\r
+    ExtInfo->McastPort = 0;\r
+  } else {\r
+\r
+    Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str);\r
+\r
+    if (Num > 65535) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    ExtInfo->McastPort = (UINT16) Num;\r
+\r
+    while (NET_IS_DIGIT (*Str)) {\r
+      Str++;\r
+    }\r
+  }\r
+\r
+  if (*Str != ',') {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Str++;\r
+\r
+  //\r
+  // Check the master/slave setting, 1 for master, 0 for slave.\r
+  //\r
+  Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str);\r
+\r
+  if (Num != 0 && Num != 1) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ExtInfo->IsMaster = (BOOLEAN) (Num == 1);\r
+\r
+  while (NET_IS_DIGIT (*Str)) {\r
+    Str++;\r
+  }\r
+\r
+  if (*Str != '\0') {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse the MTFTP6 extesion options.\r
+\r
+  @param[in]  Options       The pointer to the extension options list.\r
+  @param[in]  Count         The num of the extension options.\r
+  @param[in]  IsRequest     If FALSE, the extension options is included\r
+                            by a request packet.\r
+  @param[in]  ExtInfo       The pointer to the option information to be filled.\r
+\r
+  @retval EFI_SUCCESS            Parse the multicast option successfully.\r
+  @retval EFI_INVALID_PARAMETER  There is one option is malformatted at least.\r
+  @retval EFI_UNSUPPORTED        There is one option is not supported at least.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseExtensionOption (\r
+  IN EFI_MTFTP6_OPTION        *Options,\r
+  IN UINT32                   Count,\r
+  IN BOOLEAN                  IsRequest,\r
+  IN MTFTP6_EXT_OPTION_INFO   *ExtInfo\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_MTFTP6_OPTION           *Opt;\r
+  UINT32                      Index;\r
+  UINT32                      Value;\r
+\r
+  ExtInfo->BitMap = 0;\r
+\r
+  for (Index = 0; Index < Count; Index++) {\r
+\r
+    Opt = Options + Index;\r
+\r
+    if (Opt->OptionStr == NULL || Opt->ValueStr == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "blksize") == 0) {\r
+      //\r
+      // block size option, valid value is between [8, 65464]\r
+      //\r
+      Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);\r
+\r
+      if ((Value < 8) || (Value > 65464)) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      ExtInfo->BlkSize = (UINT16) Value;\r
+      ExtInfo->BitMap |= MTFTP6_OPT_BLKSIZE_BIT;\r
+\r
+    } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "timeout") == 0) {\r
+      //\r
+      // timeout option, valid value is between [1, 255]\r
+      //\r
+      Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);\r
+\r
+      if (Value < 1 || Value > 255) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      ExtInfo->Timeout = (UINT8) Value;\r
+      ExtInfo->BitMap |= MTFTP6_OPT_TIMEOUT_BIT;\r
+\r
+    } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "tsize") == 0) {\r
+      //\r
+      // tsize option, the biggest transfer supported is 4GB with block size option\r
+      //\r
+      ExtInfo->Tsize   = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);\r
+      ExtInfo->BitMap |= MTFTP6_OPT_TSIZE_BIT;\r
+\r
+    } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "multicast") == 0) {\r
+      //\r
+      // Multicast option, if it is a request, the value must be a zero string,\r
+      // otherwise, it must be like "addr,port,mc" string, mc indicates master.\r
+      //\r
+      if (!IsRequest) {\r
+\r
+        Status = Mtftp6ParseMcastOption (Opt->ValueStr, ExtInfo);\r
+\r
+        if (EFI_ERROR (Status)) {\r
+          return Status;\r
+        }\r
+      } else if (*(Opt->ValueStr) != '\0') {\r
+\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      ExtInfo->BitMap |= MTFTP6_OPT_MCAST_BIT;\r
+\r
+    } else if (IsRequest) {\r
+      //\r
+      // If it's a request, unsupported; else if it's a reply, ignore.\r
+      //\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Go through the packet to fill the options array with the start\r
+  addresses of each MTFTP option name/value pair.\r
+\r
+  @param[in]      Packet                 The packet to be checked.\r
+  @param[in]      PacketLen              The length of the packet.\r
+  @param[in, out] Count                  The num of the Options on input.\r
+                                         The actual one on output.\r
+  @param[in]      Options                The option array to be filled.\r
+                                         It is optional.\r
+\r
+  @retval EFI_SUCCESS            The packet has been parsed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is malformatted.\r
+  @retval EFI_BUFFER_TOO_SMALL   The Options array is too small.\r
+  @retval EFI_PROTOCOL_ERROR     An unexpected MTFTPv6 packet was received.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParsePacketOption (\r
+  IN     EFI_MTFTP6_PACKET     *Packet,\r
+  IN     UINT32                PacketLen,\r
+  IN OUT UINT32                *Count,\r
+  IN     EFI_MTFTP6_OPTION     *Options          OPTIONAL\r
+  )\r
+{\r
+  UINT8                        *Cur;\r
+  UINT8                        *Last;\r
+  UINT8                        Num;\r
+  UINT8                        *Name;\r
+  UINT8                        *Value;\r
+\r
+  Num   = 0;\r
+  Cur   = (UINT8 *) Packet + MTFTP6_OPCODE_LEN;\r
+  Last  = (UINT8 *) Packet + PacketLen - 1;\r
+\r
+  //\r
+  // process option name and value pairs.\r
+  // The last byte is always zero.\r
+  //\r
+  while (Cur < Last) {\r
+    Name = Cur;\r
+\r
+    while (*Cur != 0) {\r
+      Cur++;\r
+    }\r
+\r
+    if (Cur == Last) {\r
+      return EFI_PROTOCOL_ERROR;\r
+    }\r
+\r
+    Value = ++Cur;\r
+\r
+    while (*Cur != 0) {\r
+      Cur++;\r
+    }\r
+\r
+    Num++;\r
+\r
+    if (Options != NULL && Num <= *Count) {\r
+      Options[Num - 1].OptionStr  = Name;\r
+      Options[Num - 1].ValueStr   = Value;\r
+    }\r
+\r
+    Cur++;\r
+  }\r
+\r
+  //\r
+  // Return buffer too small if the buffer passed-in isn't enough.\r
+  //\r
+  if (*Count < Num || Options == NULL) {\r
+    *Count = Num;\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  *Count = Num;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Go through the packet, generate option list array and fill it\r
+  by the result of parse options.\r
+\r
+  @param[in]      Packet                 The packet to be checked.\r
+  @param[in]      PacketLen              The length of the packet.\r
+  @param[in, out] OptionCount            The num of the Options on input.\r
+                                         The actual one on output.\r
+  @param[out]     OptionList             The option list array to be generated\r
+                                         and filled. It is optional.\r
+\r
+  @retval EFI_SUCCESS            The packet has been parsed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is malformatted.\r
+  @retval EFI_PROTOCOL_ERROR     There is one option is malformatted at least.\r
+  @retval EFI_NOT_FOUND          The packet has no options.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the array.\r
+  @retval EFI_BUFFER_TOO_SMALL   The size of option list array is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseStart (\r
+  IN     EFI_MTFTP6_PACKET      *Packet,\r
+  IN     UINT32                 PacketLen,\r
+  IN OUT UINT32                 *OptionCount,\r
+     OUT EFI_MTFTP6_OPTION      **OptionList          OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+\r
+  if (PacketLen == 0 || Packet == NULL || OptionCount == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *OptionCount = 0;\r
+\r
+  if (OptionList != NULL) {\r
+    *OptionList = NULL;\r
+  }\r
+\r
+  if (NTOHS (Packet->OpCode) != EFI_MTFTP6_OPCODE_OACK) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // The last byte must be zero to terminate the options.\r
+  //\r
+  if (*((UINT8 *) Packet + PacketLen - 1) != 0) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // Parse packet with NULL buffer for the first time to get the number\r
+  // of options in the packet.\r
+  //\r
+  Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, NULL);\r
+\r
+  if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Return not found if there is no option parsed.\r
+  //\r
+  if (*OptionCount == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Only need parse out the number of options.\r
+  //\r
+  if (OptionList == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Allocate the buffer according to the option number parsed before.\r
+  //\r
+  *OptionList = AllocateZeroPool (*OptionCount * sizeof (EFI_MTFTP6_OPTION));\r
+\r
+  if (*OptionList == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Parse packet with allocated buffer for the second time to fill the pointer array\r
+  // of the options in the packet.\r
+  //\r
+  Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, *OptionList);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h
new file mode 100644 (file)
index 0000000..8e2671f
--- /dev/null
@@ -0,0 +1,148 @@
+/** @file\r
+  Mtftp6 option parse functions declaration.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_MTFTP6_OPTION_H__\r
+#define __EFI_MTFTP6_OPTION_H__\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+\r
+#include <Library/NetLib.h>\r
+#include <Library/UdpIoLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+\r
+#define MTFTP6_SUPPORTED_OPTIONS_NUM  4\r
+#define MTFTP6_OPCODE_LEN             2\r
+#define MTFTP6_ERRCODE_LEN            2\r
+#define MTFTP6_BLKNO_LEN              2\r
+#define MTFTP6_DATA_HEAD_LEN          4\r
+\r
+//\r
+// The bit map definition for Mtftp6 extension options.\r
+//\r
+#define MTFTP6_OPT_BLKSIZE_BIT        0x01\r
+#define MTFTP6_OPT_TIMEOUT_BIT        0x02\r
+#define MTFTP6_OPT_TSIZE_BIT          0x04\r
+#define MTFTP6_OPT_MCAST_BIT          0x08\r
+\r
+extern CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM];\r
+\r
+typedef struct {\r
+  UINT16                    BlkSize;\r
+  UINT8                     Timeout;\r
+  UINT32                    Tsize;\r
+  EFI_IPv6_ADDRESS          McastIp;\r
+  UINT16                    McastPort;\r
+  BOOLEAN                   IsMaster;\r
+  UINT32                    BitMap;\r
+} MTFTP6_EXT_OPTION_INFO;\r
+\r
+/**\r
+  Parse the Ascii string of multi-cast option.\r
+\r
+  @param[in]  Str           The pointer to the Ascii string of multi-cast option.\r
+  @param[in]  ExtInfo       The pointer to the option information to be filled.\r
+\r
+  @retval EFI_SUCCESS            Parse the multicast option successfully.\r
+  @retval EFI_INVALID_PARAMETER  The string is malformatted.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseMcastOption (\r
+  IN UINT8                  *Str,\r
+  IN MTFTP6_EXT_OPTION_INFO *ExtInfo\r
+  );\r
+\r
+\r
+/**\r
+  Parse the MTFTP6 extesion options.\r
+\r
+  @param[in]  Options       The pointer to the extension options list.\r
+  @param[in]  Count         The num of the extension options.\r
+  @param[in]  IsRequest     If FALSE, the extension options is included\r
+                            by a request packet.\r
+  @param[in]  ExtInfo       The pointer to the option information to be filled.\r
+\r
+  @retval EFI_SUCCESS            Parse the multi-cast option successfully.\r
+  @retval EFI_INVALID_PARAMETER  An option is malformatted.\r
+  @retval EFI_UNSUPPORTED        An option is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseExtensionOption (\r
+  IN EFI_MTFTP6_OPTION        *Options,\r
+  IN UINT32                   Count,\r
+  IN BOOLEAN                  IsRequest,\r
+  IN MTFTP6_EXT_OPTION_INFO   *ExtInfo\r
+  );\r
+\r
+\r
+/**\r
+  Go through the packet to fill the options array with the start\r
+  addresses of each MTFTP option name/value pair.\r
+\r
+  @param[in]      Packet                 The packet to be checked.\r
+  @param[in]      PacketLen              The length of the packet.\r
+  @param[in, out] Count                  The num of the Options on input.\r
+                                         The actual one on output.\r
+  @param[in]      Options                The option array to be filled\r
+                                         it's optional.\r
+\r
+  @retval EFI_SUCCESS            The packet has been parsed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is malformatted\r
+  @retval EFI_BUFFER_TOO_SMALL   The Options array is too small\r
+  @retval EFI_PROTOCOL_ERROR     An unexpected MTFTPv6 packet was received.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParsePacketOption (\r
+  IN     EFI_MTFTP6_PACKET     *Packet,\r
+  IN     UINT32                PacketLen,\r
+  IN OUT UINT32                *Count,\r
+  IN     EFI_MTFTP6_OPTION     *Options          OPTIONAL\r
+  );\r
+\r
+\r
+/**\r
+  Go through the packet, generate option list array and fill it\r
+  by the result of parse options.\r
+\r
+  @param[in]      Packet                 The packet to be checked.\r
+  @param[in]      PacketLen              The length of the packet.\r
+  @param[in, out] OptionCount            The num of the Options on input.\r
+                                         The actual one on output.\r
+  @param[out]     OptionList             The option list array to be generated\r
+                                         and filled. It is optional.\r
+\r
+  @retval EFI_SUCCESS            The packet has been parsed successfully.\r
+  @retval EFI_INVALID_PARAMETER  The packet is malformatted.\r
+  @retval EFI_PROTOCOL_ERROR     An option is malformatted.\r
+  @retval EFI_NOT_FOUND          The packet has no options.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the array.\r
+  @retval EFI_BUFFER_TOO_SMALL   The size of option list array is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseStart (\r
+  IN     EFI_MTFTP6_PACKET      *Packet,\r
+  IN     UINT32                 PacketLen,\r
+  IN OUT UINT32                 *OptionCount,\r
+     OUT EFI_MTFTP6_OPTION      **OptionList          OPTIONAL\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c
new file mode 100644 (file)
index 0000000..da36432
--- /dev/null
@@ -0,0 +1,900 @@
+/** @file\r
+  Mtftp6 Rrq process functions implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+/**\r
+  Build and send a ACK packet for download.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  BlockNum              The block number to be acked.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the packet.\r
+  @retval EFI_SUCCESS           The ACK has been sent.\r
+  @retval Others                Failed to send the ACK.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqSendAck (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 BlockNum\r
+  )\r
+{\r
+  EFI_MTFTP6_PACKET         *Ack;\r
+  NET_BUF                   *Packet;\r
+\r
+  //\r
+  // Allocate net buffer to create ack packet.\r
+  //\r
+  Packet = NetbufAlloc (sizeof (EFI_MTFTP6_ACK_HEADER));\r
+\r
+  if (Packet == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ack = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (\r
+                                Packet,\r
+                                sizeof (EFI_MTFTP6_ACK_HEADER),\r
+                                FALSE\r
+                                );\r
+  ASSERT (Ack != NULL);\r
+\r
+  Ack->Ack.OpCode    = HTONS (EFI_MTFTP6_OPCODE_ACK);\r
+  Ack->Ack.Block[0]  = HTONS (BlockNum);\r
+\r
+  //\r
+  // Reset current retry count of the instance.\r
+  //\r
+  Instance->CurRetry = 0;\r
+\r
+  return Mtftp6TransmitPacket (Instance, Packet);\r
+}\r
+\r
+\r
+/**\r
+  Deliver the received data block to the user, which can be saved\r
+  in the user provide buffer or through the CheckPacket callback.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  Packet                The pointer to the received packet.\r
+  @param[in]  Len                   The packet length.\r
+  @param[out] UdpPacket             The net buf of the received packet.\r
+\r
+  @retval EFI_SUCCESS           The data was saved successfully.\r
+  @retval EFI_ABORTED           The user tells to abort by return an error through\r
+                                CheckPacket.\r
+  @retval EFI_BUFFER_TOO_SMALL  The user's buffer is too small, and buffer length is\r
+                                updated to the actual buffer size needed.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqSaveBlock (\r
+  IN  MTFTP6_INSTANCE       *Instance,\r
+  IN  EFI_MTFTP6_PACKET     *Packet,\r
+  IN  UINT32                Len,\r
+  OUT NET_BUF               **UdpPacket\r
+  )\r
+{\r
+  EFI_MTFTP6_TOKEN          *Token;\r
+  EFI_STATUS                Status;\r
+  UINT16                    Block;\r
+  UINT64                    Start;\r
+  UINT32                    DataLen;\r
+  UINT64                    TotalBlock;\r
+  BOOLEAN                   Completed;\r
+\r
+  Completed = FALSE;\r
+  Token     = Instance->Token;\r
+  Block     = NTOHS (Packet->Data.Block);\r
+  DataLen   = Len - MTFTP6_DATA_HEAD_LEN;\r
+\r
+  //\r
+  // This is the last block, save the block num\r
+  //\r
+  if (DataLen < Instance->BlkSize) {\r
+       Completed = TRUE;\r
+    Instance->LastBlk = Block;\r
+    Mtftp6SetLastBlockNum (&Instance->BlkList, Block);\r
+  }\r
+\r
+  //\r
+  // Remove this block number from the file hole. If Mtftp6RemoveBlockNum\r
+  // returns EFI_NOT_FOUND, the block has been saved, don't save it again.\r
+  // Note that : For bigger files, allowing the block counter to roll over\r
+  // to accept transfers of unlimited size. So TotalBlock is memorised as\r
+  // continuous block counter.\r
+  //\r
+  Status = Mtftp6RemoveBlockNum (&Instance->BlkList, Block, Completed, &TotalBlock);\r
+\r
+  if (Status == EFI_NOT_FOUND) {\r
+    return EFI_SUCCESS;\r
+  } else if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Token->CheckPacket != NULL) {\r
+    //\r
+    // Callback to the check packet routine with the received packet.\r
+    //\r
+    Status = Token->CheckPacket (&Instance->Mtftp6, Token, (UINT16) Len, Packet);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Free the received packet before send new packet in ReceiveNotify,\r
+      // since the Udp6Io might need to be reconfigured.\r
+      //\r
+      NetbufFree (*UdpPacket);\r
+      *UdpPacket = NULL;\r
+      //\r
+      // Send the Mtftp6 error message if user aborted the current session.\r
+      //\r
+      Mtftp6SendError (\r
+        Instance,\r
+        EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,\r
+        (UINT8 *) "User aborted download"\r
+        );\r
+\r
+      return EFI_ABORTED;\r
+    }\r
+  }\r
+\r
+  if (Token->Buffer != NULL) {\r
+\r
+    Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize);\r
+    if (Start + DataLen <= Token->BufferSize) {\r
+      CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);\r
+      //\r
+      // Update the file size when received the last block\r
+      //\r
+      if ((Instance->LastBlk == Block) && Completed) {\r
+        Token->BufferSize = Start + DataLen;\r
+      }\r
+    } else if (Instance->LastBlk != 0) {\r
+      //\r
+      // Don't save the data if the buffer is too small, return\r
+      // EFI_BUFFER_TOO_SMALL if received the last packet. This\r
+      // will give a accurate file length.\r
+      //\r
+      Token->BufferSize = Start + DataLen;\r
+\r
+      //\r
+      // Free the received packet before send new packet in ReceiveNotify,\r
+      // since the udpio might need to be reconfigured.\r
+      //\r
+      NetbufFree (*UdpPacket);\r
+      *UdpPacket = NULL;\r
+      //\r
+      // Send the Mtftp6 error message if no enough buffer.\r
+      //\r
+      Mtftp6SendError (\r
+        Instance,\r
+        EFI_MTFTP6_ERRORCODE_DISK_FULL,\r
+        (UINT8 *) "User provided memory block is too small"\r
+        );\r
+\r
+      return EFI_BUFFER_TOO_SMALL;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Process the received data packets. It will save the block\r
+  then send back an ACK if it is active.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  Packet                The pointer to the received packet.\r
+  @param[in]  Len                   The length of the packet.\r
+  @param[out] UdpPacket             The net buf of received packet.\r
+  @param[out] IsCompleted           If TRUE, the download has been completed.\r
+                                    Otherwise, the download has not been completed.\r
+\r
+  @retval EFI_SUCCESS           The data packet was successfully processed.\r
+  @retval EFI_ABORTED           The download was aborted by the user.\r
+  @retval EFI_BUFFER_TOO_SMALL  The user-provided buffer is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqHandleData (\r
+  IN  MTFTP6_INSTANCE       *Instance,\r
+  IN  EFI_MTFTP6_PACKET     *Packet,\r
+  IN  UINT32                Len,\r
+  OUT NET_BUF               **UdpPacket,\r
+  OUT BOOLEAN               *IsCompleted\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  UINT16                    BlockNum;\r
+  INTN                      Expected;\r
+\r
+  *IsCompleted = FALSE;\r
+  BlockNum     = NTOHS (Packet->Data.Block);\r
+  Expected     = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+  ASSERT (Expected >= 0);\r
+\r
+  //\r
+  // If we are active and received an unexpected packet, retransmit\r
+  // the last ACK then restart receiving. If we are passive, save\r
+  // the block.\r
+  //\r
+  if (Instance->IsMaster && (Expected != BlockNum)) {\r
+    //\r
+    // Free the received packet before send new packet in ReceiveNotify,\r
+    // since the udpio might need to be reconfigured.\r
+    //\r
+    NetbufFree (*UdpPacket);\r
+    *UdpPacket = NULL;\r
+\r
+    Mtftp6TransmitPacket (Instance, Instance->LastPacket);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Status = Mtftp6RrqSaveBlock (Instance, Packet, Len, UdpPacket);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Reset the passive client's timer whenever it received a valid data packet.\r
+  //\r
+  if (!Instance->IsMaster) {\r
+    Instance->PacketToLive = Instance->Timeout * 2;\r
+  }\r
+\r
+  //\r
+  // Check whether we have received all the blocks. Send the ACK if we\r
+  // are active (unicast client or master client for multicast download).\r
+  // If we have received all the blocks, send an ACK even if we are passive\r
+  // to tell the server that we are done.\r
+  //\r
+  Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+  if (Instance->IsMaster || Expected < 0) {\r
+    if (Expected < 0) {\r
+      //\r
+      // If we are passive client, then the just received Block maybe\r
+      // isn't the last block. We need to send an ACK to the last block\r
+      // to inform the server that we are done. If we are active client,\r
+      // the Block == Instance->LastBlock.\r
+      //\r
+      BlockNum     = Instance->LastBlk;\r
+      *IsCompleted = TRUE;\r
+\r
+    } else {\r
+      BlockNum     = (UINT16) (Expected - 1);\r
+    }\r
+    //\r
+    // Free the received packet before send new packet in ReceiveNotify,\r
+    // since the udpio might need to be reconfigured.\r
+    //\r
+    NetbufFree (*UdpPacket);\r
+    *UdpPacket = NULL;\r
+\r
+    Mtftp6RrqSendAck (Instance, BlockNum);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Validate whether the options received in the server's OACK packet is valid.\r
+  The options are valid only if:\r
+  1. The server doesn't include options not requested by us.\r
+  2. The server can only use smaller blksize than that is requested.\r
+  3. The server can only use the same timeout as requested.\r
+  4. The server doesn't change its multicast channel.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  ReplyInfo             The pointer to options information in reply packet.\r
+  @param[in]  RequestInfo           The pointer to requested options info.\r
+\r
+  @retval     TRUE                  If the option in the OACK is valid.\r
+  @retval     FALSE                 If the option is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+Mtftp6RrqOackValid (\r
+  IN MTFTP6_INSTANCE           *Instance,\r
+  IN MTFTP6_EXT_OPTION_INFO    *ReplyInfo,\r
+  IN MTFTP6_EXT_OPTION_INFO    *RequestInfo\r
+  )\r
+{\r
+  //\r
+  // It is invalid for server to return options we don't request\r
+  //\r
+  if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Server can only specify a smaller block size to be used and\r
+  // return the timeout matches that requested.\r
+  //\r
+  if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||\r
+      (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))\r
+      ) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // The server can send ",,master" to client to change its master\r
+  // setting. But if it use the specific multicast channel, it can't\r
+  // change the setting.\r
+  //\r
+  if (((ReplyInfo->BitMap & MTFTP6_OPT_MCAST_BIT) != 0) && !NetIp6IsUnspecifiedAddr (&Instance->McastIp)) {\r
+\r
+    if (!NetIp6IsUnspecifiedAddr (&ReplyInfo->McastIp) && CompareMem (\r
+                                                            &ReplyInfo->McastIp,\r
+                                                            &Instance->McastIp,\r
+                                                            sizeof (EFI_IPv6_ADDRESS)\r
+                                                            ) != 0) {\r
+      return FALSE;\r
+    }\r
+\r
+    if ((ReplyInfo->McastPort != 0) && (ReplyInfo->McastPort != Instance->McastPort)) {\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Configure Udp6Io to receive a packet from a multicast address.\r
+\r
+  @param[in]  McastIo               The pointer to the mcast Udp6Io.\r
+  @param[in]  Context               The pointer to the context.\r
+\r
+  @retval EFI_SUCCESS           The mcast Udp6Io was successfully configured.\r
+  @retval Others                Failed to configure the Udp6Io.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6RrqConfigMcastUdpIo (\r
+  IN UDP_IO                 *McastIo,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+  EFI_UDP6_CONFIG_DATA      *Udp6Cfg;\r
+  EFI_IPv6_ADDRESS          Group;\r
+  MTFTP6_INSTANCE           *Instance;\r
+\r
+  Udp6     = McastIo->Protocol.Udp6;\r
+  Udp6Cfg  = &(McastIo->Config.Udp6);\r
+  Instance = (MTFTP6_INSTANCE *) Context;\r
+\r
+  //\r
+  // Set the configure data for the mcast Udp6Io.\r
+  //\r
+  ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+  Udp6Cfg->AcceptPromiscuous  = FALSE;\r
+  Udp6Cfg->AcceptAnyPort      = FALSE;\r
+  Udp6Cfg->AllowDuplicatePort = FALSE;\r
+  Udp6Cfg->TrafficClass       = 0;\r
+  Udp6Cfg->HopLimit           = 128;\r
+  Udp6Cfg->ReceiveTimeout     = 0;\r
+  Udp6Cfg->TransmitTimeout    = 0;\r
+  Udp6Cfg->StationPort        = Instance->McastPort;\r
+  Udp6Cfg->RemotePort         = 0;\r
+\r
+  CopyMem (\r
+    &Udp6Cfg->RemoteAddress,\r
+    &Instance->ServerIp,\r
+    sizeof (EFI_IPv6_ADDRESS)\r
+    );\r
+\r
+  //\r
+  // Configure the mcast Udp6Io.\r
+  //\r
+  Status = Udp6->Configure (Udp6, Udp6Cfg);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Join the multicast group\r
+  //\r
+  CopyMem (&Group, &Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  return Udp6->Groups (Udp6, TRUE, &Group);\r
+}\r
+\r
+\r
+/**\r
+  Process the OACK packet for Rrq.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  Packet                The pointer to the received packet.\r
+  @param[in]  Len                   The length of the packet.\r
+  @param[out] UdpPacket             The net buf of received packet.\r
+  @param[out] IsCompleted           If TRUE, the download has been completed.\r
+                                    Otherwise, the download has not been completed.\r
+\r
+  @retval EFI_DEVICE_ERROR      Failed to create/start a multicast Udp6 child.\r
+  @retval EFI_TFTP_ERROR        An error happened during the process.\r
+  @retval EFI_SUCCESS           The OACK packet successfully processed.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqHandleOack (\r
+  IN  MTFTP6_INSTANCE       *Instance,\r
+  IN  EFI_MTFTP6_PACKET     *Packet,\r
+  IN  UINT32                Len,\r
+  OUT NET_BUF               **UdpPacket,\r
+  OUT BOOLEAN               *IsCompleted\r
+  )\r
+{\r
+  EFI_MTFTP6_OPTION         *Options;\r
+  UINT32                    Count;\r
+  MTFTP6_EXT_OPTION_INFO    ExtInfo;\r
+  EFI_STATUS                Status;\r
+  INTN                      Expected;\r
+\r
+  *IsCompleted = FALSE;\r
+\r
+  //\r
+  // If already started the master download, don't change the\r
+  // setting. Master download always succeeds.\r
+  //\r
+  Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+  ASSERT (Expected != -1);\r
+\r
+  if (Instance->IsMaster && Expected != 1) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));\r
+\r
+  //\r
+  // Parse the options in the packet.\r
+  //\r
+  Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Parse the extensive options in the packet.\r
+  //\r
+  Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);\r
+\r
+  if (EFI_ERROR (Status) || !Mtftp6RrqOackValid (Instance, &ExtInfo, &Instance->ExtInfo)) {\r
+    //\r
+    // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.\r
+    //\r
+    if (Status != EFI_OUT_OF_RESOURCES) {\r
+      //\r
+      // Free the received packet before send new packet in ReceiveNotify,\r
+      // since the udpio might need to be reconfigured.\r
+      //\r
+      NetbufFree (*UdpPacket);\r
+      *UdpPacket = NULL;\r
+      //\r
+      // Send the Mtftp6 error message if invalid packet.\r
+      //\r
+      Mtftp6SendError (\r
+        Instance,\r
+        EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,\r
+        (UINT8 *) "Mal-formated OACK packet"\r
+        );\r
+    }\r
+\r
+    return EFI_TFTP_ERROR;\r
+  }\r
+\r
+  if ((ExtInfo.BitMap & MTFTP6_OPT_MCAST_BIT) != 0) {\r
+\r
+    //\r
+    // Save the multicast info. Always update the Master, only update the\r
+    // multicast IP address, block size, timeoute at the first time. If IP\r
+    // address is updated, create a UDP child to receive the multicast.\r
+    //\r
+    Instance->IsMaster = ExtInfo.IsMaster;\r
+\r
+    if (NetIp6IsUnspecifiedAddr (&Instance->McastIp)) {\r
+      if (NetIp6IsUnspecifiedAddr (&ExtInfo.McastIp) || ExtInfo.McastPort == 0) {\r
+        //\r
+        // Free the received packet before send new packet in ReceiveNotify,\r
+        // since the udpio might need to be reconfigured.\r
+        //\r
+        NetbufFree (*UdpPacket);\r
+        *UdpPacket = NULL;\r
+        //\r
+        // Send the Mtftp6 error message if invalid multi-cast setting.\r
+        //\r
+        Mtftp6SendError (\r
+          Instance,\r
+          EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,\r
+          (UINT8 *) "Illegal multicast setting"\r
+          );\r
+\r
+        return EFI_TFTP_ERROR;\r
+      }\r
+\r
+      //\r
+      // Create a UDP child then start receive the multicast from it.\r
+      //\r
+      CopyMem (\r
+        &Instance->McastIp,\r
+        &ExtInfo.McastIp,\r
+        sizeof (EFI_IP_ADDRESS)\r
+        );\r
+\r
+      Instance->McastPort  = ExtInfo.McastPort;\r
+      Instance->McastUdpIo = UdpIoCreateIo (\r
+                               Instance->Service->Controller,\r
+                               Instance->Service->Image,\r
+                               Mtftp6RrqConfigMcastUdpIo,\r
+                               UDP_IO_UDP6_VERSION,\r
+                               Instance\r
+                               );\r
+\r
+      if (Instance->McastUdpIo == NULL) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      Status = UdpIoRecvDatagram (\r
+                 Instance->McastUdpIo,\r
+                 Mtftp6RrqInput,\r
+                 Instance,\r
+                 0\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        //\r
+        // Free the received packet before send new packet in ReceiveNotify,\r
+        // since the udpio might need to be reconfigured.\r
+        //\r
+        NetbufFree (*UdpPacket);\r
+        *UdpPacket = NULL;\r
+        //\r
+        // Send the Mtftp6 error message if failed to create Udp6Io to receive.\r
+        //\r
+        Mtftp6SendError (\r
+          Instance,\r
+          EFI_MTFTP6_ERRORCODE_ACCESS_VIOLATION,\r
+          (UINT8 *) "Failed to create socket to receive multicast packet"\r
+          );\r
+\r
+        return Status;\r
+      }\r
+\r
+      //\r
+      // Update the parameters used.\r
+      //\r
+      if (ExtInfo.BlkSize != 0) {\r
+        Instance->BlkSize = ExtInfo.BlkSize;\r
+      }\r
+\r
+      if (ExtInfo.Timeout != 0) {\r
+        Instance->Timeout = ExtInfo.Timeout;\r
+      }\r
+    }\r
+\r
+  } else {\r
+\r
+    Instance->IsMaster = TRUE;\r
+\r
+    if (ExtInfo.BlkSize != 0) {\r
+      Instance->BlkSize = ExtInfo.BlkSize;\r
+    }\r
+\r
+    if (ExtInfo.Timeout != 0) {\r
+      Instance->Timeout = ExtInfo.Timeout;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Free the received packet before send new packet in ReceiveNotify,\r
+  // since the udpio might need to be reconfigured.\r
+  //\r
+  NetbufFree (*UdpPacket);\r
+  *UdpPacket = NULL;\r
+  //\r
+  // Send an ACK to (Expected - 1) which is 0 for unicast download,\r
+  // or tell the server we want to receive the Expected block.\r
+  //\r
+  return Mtftp6RrqSendAck (Instance, (UINT16) (Expected - 1));\r
+}\r
+\r
+\r
+/**\r
+  The packet process callback for Mtftp6 download.\r
+\r
+  @param[in]  UdpPacket             The pointer to the packet received.\r
+  @param[in]  UdpEpt                The pointer to the Udp6 access point.\r
+  @param[in]  IoStatus              The status from Udp6 instance.\r
+  @param[in]  Context               The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6RrqInput (\r
+  IN NET_BUF                *UdpPacket,\r
+  IN UDP_END_POINT          *UdpEpt,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  MTFTP6_INSTANCE           *Instance;\r
+  EFI_MTFTP6_PACKET         *Packet;\r
+  BOOLEAN                   IsCompleted;\r
+  BOOLEAN                   IsMcast;\r
+  EFI_STATUS                Status;\r
+  UINT16                    Opcode;\r
+  UINT32                    TotalNum;\r
+  UINT32                    Len;\r
+\r
+  Instance = (MTFTP6_INSTANCE *) Context;\r
+\r
+  NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);\r
+\r
+  Status      = EFI_SUCCESS;\r
+  Packet      = NULL;\r
+  IsCompleted = FALSE;\r
+  IsMcast     = FALSE;\r
+  TotalNum    = 0;\r
+\r
+  //\r
+  // Return error status if Udp6 instance failed to receive.\r
+  //\r
+  if (EFI_ERROR (IoStatus)) {\r
+    Status = IoStatus;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  ASSERT (UdpPacket != NULL);\r
+\r
+  if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Find the port this packet is from to restart receive correctly.\r
+  //\r
+  if (CompareMem (\r
+        Ip6Swap128 (&UdpEpt->LocalAddr.v6),\r
+        &Instance->McastIp,\r
+        sizeof (EFI_IPv6_ADDRESS)\r
+        ) == 0) {\r
+    IsMcast = TRUE;\r
+  } else {\r
+    IsMcast = FALSE;\r
+  }\r
+\r
+  //\r
+  // Client send initial request to server's listening port. Server\r
+  // will select a UDP port to communicate with the client. The server\r
+  // is required to use the same port as RemotePort to multicast the\r
+  // data.\r
+  //\r
+  if (UdpEpt->RemotePort != Instance->ServerDataPort) {\r
+    if (Instance->ServerDataPort != 0) {\r
+      goto ON_EXIT;\r
+    } else {\r
+      //\r
+      // For the subsequent exchange of requests, reconfigure the udpio as\r
+      // (serverip, serverport, localip, localport).\r
+      // Ususally, the client set serverport as 0 to receive and reset it\r
+      // once the first packet arrives to send ack.\r
+      //\r
+      Instance->ServerDataPort = UdpEpt->RemotePort;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
+  //\r
+  Len      = UdpPacket->TotalSize;\r
+  TotalNum = UdpPacket->BlockOpNum;\r
+\r
+  if (TotalNum > 1) {\r
+    Packet = AllocateZeroPool (Len);\r
+\r
+    if (Packet == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
+\r
+  } else {\r
+    Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
+    ASSERT (Packet != NULL);\r
+  }\r
+\r
+  Opcode = NTOHS (Packet->OpCode);\r
+\r
+  //\r
+  // Callback to the user's CheckPacket if provided. Abort the transmission\r
+  // if CheckPacket returns an EFI_ERROR code.\r
+  //\r
+  if ((Instance->Token->CheckPacket != NULL) &&\r
+      (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)\r
+      ) {\r
+\r
+    Status = Instance->Token->CheckPacket (\r
+                                &Instance->Mtftp6,\r
+                                Instance->Token,\r
+                                (UINT16) Len,\r
+                                Packet\r
+                                );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Send an error message to the server to inform it\r
+      //\r
+      if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {\r
+        //\r
+        // Free the received packet before send new packet in ReceiveNotify,\r
+        // since the udpio might need to be reconfigured.\r
+        //\r
+        NetbufFree (UdpPacket);\r
+        UdpPacket = NULL;\r
+        //\r
+        // Send the Mtftp6 error message if user aborted the current session.\r
+        //\r
+        Mtftp6SendError (\r
+          Instance,\r
+          EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+          (UINT8 *) "User aborted the transfer"\r
+          );\r
+      }\r
+\r
+      Status = EFI_ABORTED;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Switch the process routines by the operation code.\r
+  //\r
+  switch (Opcode) {\r
+  case EFI_MTFTP6_OPCODE_DATA:\r
+    if ((Len > (UINT32) (MTFTP6_DATA_HEAD_LEN + Instance->BlkSize)) || (Len < (UINT32) MTFTP6_DATA_HEAD_LEN)) {\r
+      goto ON_EXIT;\r
+    }\r
+    //\r
+    // Handle the data packet of Rrq.\r
+    //\r
+    Status = Mtftp6RrqHandleData (\r
+               Instance,\r
+               Packet,\r
+               Len,\r
+               &UdpPacket,\r
+               &IsCompleted\r
+               );\r
+    break;\r
+\r
+  case EFI_MTFTP6_OPCODE_OACK:\r
+    if (IsMcast || Len <= MTFTP6_OPCODE_LEN) {\r
+      goto ON_EXIT;\r
+    }\r
+    //\r
+    // Handle the Oack packet of Rrq.\r
+    //\r
+    Status = Mtftp6RrqHandleOack (\r
+               Instance,\r
+               Packet,\r
+               Len,\r
+               &UdpPacket,\r
+               &IsCompleted\r
+               );\r
+    break;\r
+\r
+  default:\r
+    //\r
+    // Drop and return eror if received error message.\r
+    //\r
+    Status = EFI_TFTP_ERROR;\r
+    break;\r
+  }\r
+\r
+ON_EXIT:\r
+  //\r
+  // Free the resources, then if !EFI_ERROR (Status), restart the\r
+  // receive, otherwise end the session.\r
+  //\r
+  if (Packet != NULL && TotalNum > 1) {\r
+    FreePool (Packet);\r
+  }\r
+  if (UdpPacket != NULL) {\r
+    NetbufFree (UdpPacket);\r
+  }\r
+  if (!EFI_ERROR (Status) && !IsCompleted) {\r
+    if (IsMcast) {\r
+      Status = UdpIoRecvDatagram (\r
+                 Instance->McastUdpIo,\r
+                 Mtftp6RrqInput,\r
+                 Instance,\r
+                 0\r
+                 );\r
+    } else {\r
+      Status = UdpIoRecvDatagram (\r
+                 Instance->UdpIo,\r
+                 Mtftp6RrqInput,\r
+                 Instance,\r
+                 0\r
+                 );\r
+    }\r
+  }\r
+  //\r
+  // Clean up the current session if failed to continue.\r
+  //\r
+  if (EFI_ERROR (Status) || IsCompleted) {\r
+    Mtftp6OperationClean (Instance, Status);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Start the Mtftp6 instance to download. It first initializes some\r
+  of the internal states, then builds and sends an RRQ reqeuest packet.\r
+  Finally, it starts receive for the downloading.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  Operation             The operation code of current packet.\r
+\r
+  @retval EFI_SUCCESS           The Mtftp6 is started to download.\r
+  @retval Others                Failed to start to download.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqStart (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 Operation\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // The valid block number range are [1, 0xffff]. For example:\r
+  // the client sends an RRQ request to the server, the server\r
+  // transfers the DATA1 block. If option negoitation is ongoing,\r
+  // the server will send back an OACK, then client will send ACK0.\r
+  //\r
+  Status = Mtftp6InitBlockRange (&Instance->BlkList, 1, 0xffff);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = Mtftp6SendRequest (Instance, Operation);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return UdpIoRecvDatagram (\r
+           Instance->UdpIo,\r
+           Mtftp6RrqInput,\r
+           Instance,\r
+           0\r
+           );\r
+}\r
+\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c
new file mode 100644 (file)
index 0000000..24ce0e8
--- /dev/null
@@ -0,0 +1,1189 @@
+/** @file\r
+  Mtftp6 support functions implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+/**\r
+  Allocate a MTFTP block range, then init it to the range of [Start, End].\r
+\r
+  @param[in]  Start                  The start block number.\r
+  @param[in]  End                    The last block number in the range.\r
+\r
+  @return Range                      The range of the allocated block buffer.\r
+\r
+**/\r
+MTFTP6_BLOCK_RANGE *\r
+Mtftp6AllocateRange (\r
+  IN UINT16                 Start,\r
+  IN UINT16                 End\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE        *Range;\r
+\r
+  Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE));\r
+\r
+  if (Range == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  InitializeListHead (&Range->Link);\r
+  Range->Start  = Start;\r
+  Range->End    = End;\r
+  Range->Bound  = End;\r
+\r
+  return Range;\r
+}\r
+\r
+\r
+/**\r
+  Initialize the block range for either RRQ or WRQ. RRQ and WRQ have\r
+  different requirements for Start and End. For example, during startup,\r
+  WRQ initializes its whole valid block range to [0, 0xffff]. This\r
+  is bacause the server will send an ACK0 to inform the user to start the\r
+  upload. When the client receives an ACK0, it will remove 0 from the range,\r
+  get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
+  without option negotiation, the server will directly send the BLOCK1\r
+  in response to the client's RRQ. When received BLOCK1, the client will\r
+  remove it from the block range and send an ACK. It also works if there\r
+  is option negotiation.\r
+\r
+  @param[in]  Head                   The block range head to initialize.\r
+  @param[in]  Start                  The Start block number.\r
+  @param[in]  End                    The last block number.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for initial block range.\r
+  @retval EFI_SUCCESS            The initial block range is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6InitBlockRange (\r
+  IN LIST_ENTRY             *Head,\r
+  IN UINT16                 Start,\r
+  IN UINT16                 End\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE        *Range;\r
+\r
+  Range = Mtftp6AllocateRange (Start, End);\r
+\r
+  if (Range == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  InsertTailList (Head, &Range->Link);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Get the first valid block number on the range list.\r
+\r
+  @param[in]  Head                   The block range head.\r
+\r
+  @retval     ==-1                   If the block range is empty.\r
+  @retval     >-1                    The first valid block number.\r
+\r
+**/\r
+INTN\r
+Mtftp6GetNextBlockNum (\r
+  IN LIST_ENTRY              *Head\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE  *Range;\r
+\r
+  if (IsListEmpty (Head)) {\r
+    return -1;\r
+  }\r
+\r
+  Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link);\r
+  return Range->Start;\r
+}\r
+\r
+\r
+/**\r
+  Set the last block number of the block range list. It\r
+  removes all the blocks after the Last. MTFTP initialize the\r
+  block range to the maximum possible range, such as [0, 0xffff]\r
+  for WRQ. When it gets the last block number, it calls\r
+  this function to set the last block number.\r
+\r
+  @param[in]  Head                   The block range list.\r
+  @param[in]  Last                   The last block number.\r
+\r
+**/\r
+VOID\r
+Mtftp6SetLastBlockNum (\r
+  IN LIST_ENTRY             *Head,\r
+  IN UINT16                 Last\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE        *Range;\r
+\r
+  //\r
+  // Iterate from the tail to head to remove the block number\r
+  // after the last.\r
+  //\r
+  while (!IsListEmpty (Head)) {\r
+    Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link);\r
+\r
+    if (Range->Start > Last) {\r
+      RemoveEntryList (&Range->Link);\r
+      FreePool (Range);\r
+      continue;\r
+    }\r
+\r
+    if (Range->End > Last) {\r
+      Range->End = Last;\r
+    }\r
+    return ;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Remove the block number from the block range list.\r
+\r
+  @param[in]  Head                   The block range list to remove from.\r
+  @param[in]  Num                    The block number to remove.\r
+  @param[in]  Completed              Whether Num is the last block number\r
+  @param[out] TotalBlock             The continuous block number in all\r
+\r
+  @retval EFI_NOT_FOUND          The block number isn't in the block range list.\r
+  @retval EFI_SUCCESS            The block number has been removed from the list.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RemoveBlockNum (\r
+  IN LIST_ENTRY             *Head,\r
+  IN UINT16                 Num,\r
+  IN BOOLEAN                Completed,\r
+  OUT UINT64                *TotalBlock\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE        *Range;\r
+  MTFTP6_BLOCK_RANGE        *NewRange;\r
+  LIST_ENTRY                *Entry;\r
+\r
+  NET_LIST_FOR_EACH (Entry, Head) {\r
+\r
+    //\r
+    // Each block represents a hole [Start, End] in the file,\r
+    // skip to the first range with End >= Num\r
+    //\r
+    Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);\r
+\r
+    if (Range->End < Num) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // There are three different cases for Start\r
+    // 1. (Start > Num) && (End >= Num):\r
+    //    because all the holes before this one has the condition of\r
+    //    End < Num, so this block number has been removed.\r
+    //\r
+    // 2. (Start == Num) && (End >= Num):\r
+    //    Need to increase the Start by one, and if End == Num, this\r
+    //    hole has been removed completely, remove it.\r
+    //\r
+    // 3. (Start < Num) && (End >= Num):\r
+    //    if End == Num, only need to decrease the End by one because\r
+    //    we have (Start < Num) && (Num == End), so (Start <= End - 1).\r
+    //    if (End > Num), the hold is splited into two holes, with\r
+    //    [Start, Num - 1] and [Num + 1, End].\r
+    //\r
+    if (Range->Start > Num) {\r
+      return EFI_NOT_FOUND;\r
+\r
+    } else if (Range->Start == Num) {\r
+      Range->Start++;\r
+\r
+      //\r
+      // Note that: RFC 1350 does not mention block counter roll-over,\r
+      // but several TFTP hosts implement the roll-over be able to accept\r
+      // transfers of unlimited size. There is no consensus, however, whether\r
+      // the counter should wrap around to zero or to one. Many implementations\r
+      // wrap to zero, because this is the simplest to implement. Here we choose\r
+      // this solution.\r
+      //\r
+      *TotalBlock  = Num;\r
+\r
+      if (Range->Round > 0) {\r
+        *TotalBlock += Range->Bound +  MultU64x32 ((UINT64) (Range->Round -1), (UINT32)(Range->Bound + 1)) + 1;\r
+      }\r
+\r
+      if (Range->Start > Range->Bound) {\r
+        Range->Start = 0;\r
+        Range->Round ++;\r
+      }\r
+\r
+      if ((Range->Start > Range->End) || Completed) {\r
+        RemoveEntryList (&Range->Link);\r
+        FreePool (Range);\r
+      }\r
+\r
+      return EFI_SUCCESS;\r
+\r
+    } else {\r
+      if (Range->End == Num) {\r
+        Range->End--;\r
+      } else {\r
+        NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);\r
+\r
+        if (NewRange == NULL) {\r
+          return EFI_OUT_OF_RESOURCES;\r
+        }\r
+\r
+        Range->End = Num - 1;\r
+        NetListInsertAfter (&Range->Link, &NewRange->Link);\r
+      }\r
+\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+  Configure the opened Udp6 instance until the corresponding Ip6 instance\r
+  has been configured.\r
+\r
+  @param[in]  UdpIo                  The pointer to the Udp6 Io.\r
+  @param[in]  UdpCfgData             The pointer to the Udp6 configure data.\r
+\r
+  @retval EFI_SUCCESS            Configure the Udp6 instance successfully.\r
+  @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not\r
+                                 been configured yet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6GetMapping (\r
+  IN UDP_IO                 *UdpIo,\r
+  IN EFI_UDP6_CONFIG_DATA   *UdpCfgData\r
+  )\r
+{\r
+  EFI_IP6_MODE_DATA         Ip6Mode;\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+  EFI_STATUS                Status;\r
+  EFI_EVENT                 Event;\r
+\r
+  Event  = NULL;\r
+  Udp6   = UdpIo->Protocol.Udp6;\r
+\r
+  //\r
+  // Create a timer to check whether the Ip6 instance configured or not.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  Event,\r
+                  TimerRelative,\r
+                  MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check the Ip6 mode data till timeout.\r
+  //\r
+  while (EFI_ERROR (gBS->CheckEvent (Event))) {\r
+\r
+    Udp6->Poll (Udp6);\r
+\r
+    Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL);\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+\r
+      if  (Ip6Mode.IsConfigured) {\r
+        //\r
+        // Continue to configure the Udp6 instance.\r
+        //\r
+        Status = Udp6->Configure (Udp6, UdpCfgData);\r
+      } else {\r
+        Status = EFI_NO_MAPPING;\r
+      }\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (Event != NULL) {\r
+    gBS->CloseEvent (Event);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The dummy configure routine for create a new Udp6 Io.\r
+\r
+  @param[in]  UdpIo                  The pointer to the Udp6 Io.\r
+  @param[in]  Context                The pointer to the context.\r
+\r
+  @retval EFI_SUCCESS                This value is always returned.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ConfigDummyUdpIo (\r
+  IN UDP_IO                 *UdpIo,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  The configure routine for Mtftp6 instance to transmit/receive.\r
+\r
+  @param[in]  UdpIo                  The pointer to the Udp6 Io.\r
+  @param[in]  ServerIp               The pointer to the server address.\r
+  @param[in]  ServerPort             The pointer to the server port.\r
+  @param[in]  LocalIp                The pointer to the local address.\r
+  @param[in]  LocalPort              The pointer to the local port.\r
+\r
+  @retval EFI_SUCCESS            Configured the Udp6 Io for Mtftp6 successfully.\r
+  @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not been\r
+                                 configured yet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ConfigUdpIo (\r
+  IN UDP_IO                 *UdpIo,\r
+  IN EFI_IPv6_ADDRESS       *ServerIp,\r
+  IN UINT16                 ServerPort,\r
+  IN EFI_IPv6_ADDRESS       *LocalIp,\r
+  IN UINT16                 LocalPort\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+  EFI_UDP6_CONFIG_DATA      *Udp6Cfg;\r
+\r
+  Udp6    = UdpIo->Protocol.Udp6;\r
+  Udp6Cfg = &(UdpIo->Config.Udp6);\r
+\r
+  ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+  //\r
+  // Set the Udp6 Io configure data.\r
+  //\r
+  Udp6Cfg->AcceptPromiscuous  = FALSE;\r
+  Udp6Cfg->AcceptAnyPort      = FALSE;\r
+  Udp6Cfg->AllowDuplicatePort = FALSE;\r
+  Udp6Cfg->TrafficClass       = 0;\r
+  Udp6Cfg->HopLimit           = 128;\r
+  Udp6Cfg->ReceiveTimeout     = 0;\r
+  Udp6Cfg->TransmitTimeout    = 0;\r
+  Udp6Cfg->StationPort        = LocalPort;\r
+  Udp6Cfg->RemotePort         = ServerPort;\r
+\r
+  CopyMem (\r
+    &Udp6Cfg->StationAddress,\r
+    LocalIp,\r
+    sizeof (EFI_IPv6_ADDRESS)\r
+    );\r
+\r
+  CopyMem (\r
+    &Udp6Cfg->RemoteAddress,\r
+    ServerIp,\r
+    sizeof (EFI_IPv6_ADDRESS)\r
+    );\r
+\r
+  //\r
+  // Configure the Udp6 instance with current configure data.\r
+  //\r
+  Status = Udp6->Configure (Udp6, Udp6Cfg);\r
+\r
+  if (Status == EFI_NO_MAPPING) {\r
+\r
+    return Mtftp6GetMapping (UdpIo, Udp6Cfg);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Build and transmit the request packet for the Mtftp6 instance.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  Operation              The operation code of this packet.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the request.\r
+  @retval EFI_SUCCESS            The request is built and sent.\r
+  @retval Others                 Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendRequest (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 Operation\r
+  )\r
+{\r
+  EFI_MTFTP6_PACKET         *Packet;\r
+  EFI_MTFTP6_OPTION         *Options;\r
+  EFI_MTFTP6_TOKEN          *Token;\r
+  NET_BUF                   *Nbuf;\r
+  UINT8                     *Mode;\r
+  UINT8                     *Cur;\r
+  UINT32                    Len1;\r
+  UINT32                    Len2;\r
+  UINT32                    Len;\r
+  UINTN                     Index;\r
+\r
+  Token   = Instance->Token;\r
+  Options = Token->OptionList;\r
+  Mode    = Token->ModeStr;\r
+\r
+  if (Mode == NULL) {\r
+    Mode = (UINT8 *) "octet";\r
+  }\r
+\r
+  //\r
+  // The header format of RRQ/WRQ packet is:\r
+  //\r
+  //   2 bytes     string    1 byte     string   1 byte\r
+  //   ------------------------------------------------\r
+  //  | Opcode |  Filename  |   0  |    Mode    |   0  |\r
+  //   ------------------------------------------------\r
+  //\r
+  // The common option format is:\r
+  //\r
+  //    string     1 byte     string   1 byte\r
+  //   ---------------------------------------\r
+  //  | OptionStr |   0  |  ValueStr  |   0   |\r
+  //   ---------------------------------------\r
+  //\r
+\r
+  //\r
+  // Compute the size of new Mtftp6 packet.\r
+  //\r
+  Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Token->Filename);\r
+  Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Mode);\r
+  Len  = Len1 + Len2 + 4;\r
+\r
+  for (Index = 0; Index < Token->OptionCount; Index++) {\r
+    Len1  = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
+    Len2  = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
+    Len  += Len1 + Len2 + 2;\r
+  }\r
+\r
+  //\r
+  // Allocate a packet then copy the data.\r
+  //\r
+  if ((Nbuf = NetbufAlloc (Len)) == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Copy the opcode, filename and mode into packet.\r
+  //\r
+  Packet         = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);\r
+  ASSERT (Packet != NULL);\r
+\r
+  Packet->OpCode = HTONS (Operation);\r
+  Cur            = Packet->Rrq.Filename;\r
+  Cur            = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Token->Filename);\r
+  Cur           += AsciiStrLen ((CHAR8 *) Token->Filename) + 1;\r
+  Cur            = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Mode);\r
+  Cur           += AsciiStrLen ((CHAR8 *) Mode) + 1;\r
+\r
+  //\r
+  // Copy all the extension options into the packet.\r
+  //\r
+  for (Index = 0; Index < Token->OptionCount; ++Index) {\r
+    Cur  = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].OptionStr);\r
+    Cur += AsciiStrLen ((CHAR8 *) Options[Index].OptionStr) + 1;\r
+    Cur  = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].ValueStr);\r
+    Cur += AsciiStrLen ((CHAR8 *) (CHAR8 *) Options[Index].ValueStr) + 1;\r
+  }\r
+\r
+  //\r
+  // Save the packet buf for retransmit\r
+  //\r
+  if (Instance->LastPacket != NULL) {\r
+    NetbufFree (Instance->LastPacket);\r
+  }\r
+\r
+  Instance->LastPacket = Nbuf;\r
+  Instance->CurRetry   = 0;\r
+\r
+  return Mtftp6TransmitPacket (Instance, Nbuf);\r
+}\r
+\r
+\r
+/**\r
+  Build and send an error packet.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  ErrCode                The error code in the packet.\r
+  @param[in]  ErrInfo                The error message in the packet.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the error packet.\r
+  @retval EFI_SUCCESS            The error packet is transmitted.\r
+  @retval Others                 Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendError (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 ErrCode,\r
+  IN UINT8*                 ErrInfo\r
+  )\r
+{\r
+  NET_BUF                   *Nbuf;\r
+  EFI_MTFTP6_PACKET         *TftpError;\r
+  UINT32                    Len;\r
+\r
+  //\r
+  // Allocate a packet then copy the data.\r
+  //\r
+  Len  = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER));\r
+  Nbuf = NetbufAlloc (Len);\r
+\r
+  if (Nbuf == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);\r
+\r
+  if (TftpError == NULL) {\r
+    NetbufFree (Nbuf);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TftpError->OpCode          = HTONS (EFI_MTFTP6_OPCODE_ERROR);\r
+  TftpError->Error.ErrorCode = HTONS (ErrCode);\r
+\r
+  AsciiStrCpy ((CHAR8 *) TftpError->Error.ErrorMessage, (CHAR8 *) ErrInfo);\r
+\r
+  //\r
+  // Save the packet buf for retransmit\r
+  //\r
+  if (Instance->LastPacket != NULL) {\r
+    NetbufFree (Instance->LastPacket);\r
+  }\r
+\r
+  Instance->LastPacket = Nbuf;\r
+  Instance->CurRetry   = 0;\r
+\r
+  return Mtftp6TransmitPacket (Instance, Nbuf);\r
+}\r
+\r
+\r
+/**\r
+  The callback function called when the packet is transmitted.\r
+\r
+  @param[in]  Packet                 The pointer to the packet.\r
+  @param[in]  UdpEpt                 The pointer to the Udp6 access point.\r
+  @param[in]  IoStatus               The result of the transmission.\r
+  @param[in]  Context                The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6OnPacketSent (\r
+  IN NET_BUF                   *Packet,\r
+  IN UDP_END_POINT             *UdpEpt,\r
+  IN EFI_STATUS                IoStatus,\r
+  IN VOID                      *Context\r
+  )\r
+{\r
+  NetbufFree (Packet);\r
+  *(BOOLEAN *) Context = TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Send the packet for the Mtftp6 instance.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  Packet                 The pointer to the packet to be sent.\r
+\r
+  @retval EFI_SUCCESS            The packet was sent out\r
+  @retval Others                 Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6TransmitPacket (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+  EFI_UDP6_CONFIG_DATA      Udp6CfgData;\r
+  EFI_STATUS                Status;\r
+  UINT16                    *Temp;\r
+  UINT16                    Value;\r
+  UINT16                    OpCode;\r
+\r
+  ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA));\r
+  Udp6 = Instance->UdpIo->Protocol.Udp6;\r
+\r
+  //\r
+  // Set the live time of the packet.\r
+  //\r
+  Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2);\r
+\r
+  Temp   = (UINT16 *) NetbufGetByte (Packet, 0, NULL);\r
+  ASSERT (Temp != NULL);\r
+\r
+  Value  = *Temp;\r
+  OpCode = NTOHS (Value);\r
+\r
+  if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) {\r
+    //\r
+    // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as\r
+    // (serverip, 69, localip, localport) to send.\r
+    // Usually local address and local port are both default as zero.\r
+    //\r
+    Status = Udp6->Configure (Udp6, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    Status = Mtftp6ConfigUdpIo (\r
+               Instance->UdpIo,\r
+               &Instance->ServerIp,\r
+               Instance->ServerCmdPort,\r
+               &Instance->Config->StationIp,\r
+               Instance->Config->LocalPort\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Get the current local address and port by get Udp6 mode data.\r
+    //\r
+    Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    NET_GET_REF (Packet);\r
+\r
+    Instance->IsTransmitted = FALSE;\r
+\r
+    Status = UdpIoSendDatagram (\r
+               Instance->UdpIo,\r
+               Packet,\r
+               NULL,\r
+               NULL,\r
+               Mtftp6OnPacketSent,\r
+               &Instance->IsTransmitted\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      NET_PUT_REF (Packet);\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Poll till the packet sent out from the ip6 queue.\r
+    //\r
+    gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+    while (!Instance->IsTransmitted) {\r
+      Udp6->Poll (Udp6);\r
+    }\r
+\r
+    Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+    //\r
+    // For the subsequent exchange of such requests, reconfigure the Udp6Io as\r
+    // (serverip, 0, localip, localport) to receive.\r
+    // Currently local address and local port are specified by Udp6 mode data.\r
+    //\r
+    Status = Udp6->Configure (Udp6, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    Status = Mtftp6ConfigUdpIo (\r
+               Instance->UdpIo,\r
+               &Instance->ServerIp,\r
+               Instance->ServerDataPort,\r
+               &Udp6CfgData.StationAddress,\r
+               Udp6CfgData.StationPort\r
+               );\r
+  } else {\r
+    //\r
+    // For the data exchange, configure the Udp6Io as (serverip, dataport,\r
+    // localip, localport) to send/receive.\r
+    // Currently local address and local port are specified by Udp6 mode data.\r
+    //\r
+    Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    if (Udp6CfgData.RemotePort != Instance->ServerDataPort) {\r
+\r
+      Status = Udp6->Configure (Udp6, NULL);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+\r
+      Status = Mtftp6ConfigUdpIo (\r
+                 Instance->UdpIo,\r
+                 &Instance->ServerIp,\r
+                 Instance->ServerDataPort,\r
+                 &Udp6CfgData.StationAddress,\r
+                 Udp6CfgData.StationPort\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+    }\r
+\r
+    NET_GET_REF (Packet);\r
+\r
+    Instance->IsTransmitted = FALSE;\r
+\r
+    Status = UdpIoSendDatagram (\r
+               Instance->UdpIo,\r
+               Packet,\r
+               NULL,\r
+               NULL,\r
+               Mtftp6OnPacketSent,\r
+               &Instance->IsTransmitted\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      NET_PUT_REF (Packet);\r
+    }\r
+\r
+    //\r
+    // Poll till the packet sent out from the ip6 queue.\r
+    //\r
+    gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+    while (!Instance->IsTransmitted) {\r
+      Udp6->Poll (Udp6);\r
+    }\r
+\r
+    Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Check packet for GetInfo callback routine.\r
+\r
+  GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect\r
+  the first packet from server, then abort the session.\r
+\r
+  @param[in]  This                   The pointer to the Mtftp6 protocol.\r
+  @param[in]  Token                  The pointer to the Mtftp6 token.\r
+  @param[in]  PacketLen              The length of the packet.\r
+  @param[in]  Packet                 The pointer to the received packet.\r
+\r
+  @retval EFI_ABORTED            Abort the Mtftp6 operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6CheckPacket (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token,\r
+  IN UINT16                 PacketLen,\r
+  IN EFI_MTFTP6_PACKET      *Packet\r
+  )\r
+{\r
+  MTFTP6_GETINFO_CONTEXT    *Context;\r
+  UINT16                    OpCode;\r
+\r
+  Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context;\r
+  OpCode  = NTOHS (Packet->OpCode);\r
+\r
+  //\r
+  // Set the GetInfo's return status according to the OpCode.\r
+  //\r
+  switch (OpCode) {\r
+  case EFI_MTFTP6_OPCODE_ERROR:\r
+    Context->Status = EFI_TFTP_ERROR;\r
+    break;\r
+\r
+  case EFI_MTFTP6_OPCODE_OACK:\r
+    Context->Status = EFI_SUCCESS;\r
+    break;\r
+\r
+  default:\r
+    Context->Status = EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // Allocate buffer then copy the packet over. Use gBS->AllocatePool\r
+  // in case NetAllocatePool will implements something tricky.\r
+  //\r
+  *(Context->Packet) = AllocateZeroPool (PacketLen);\r
+\r
+  if (*(Context->Packet) == NULL) {\r
+    Context->Status = EFI_OUT_OF_RESOURCES;\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  *(Context->PacketLen) = PacketLen;\r
+  CopyMem (*(Context->Packet), Packet, PacketLen);\r
+\r
+  return EFI_ABORTED;\r
+}\r
+\r
+\r
+/**\r
+  Clean up the current Mtftp6 operation.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  Result                 The result to be returned to the user.\r
+\r
+**/\r
+VOID\r
+Mtftp6OperationClean (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN EFI_STATUS             Result\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  MTFTP6_BLOCK_RANGE        *Block;\r
+\r
+  //\r
+  // Clean up the current token and event.\r
+  //\r
+  if (Instance->Token != NULL) {\r
+    Instance->Token->Status = Result;\r
+    if (Instance->Token->Event != NULL) {\r
+      gBS->SignalEvent (Instance->Token->Event);\r
+    }\r
+    Instance->Token = NULL;\r
+  }\r
+\r
+  //\r
+  // Clean up the corresponding Udp6Io.\r
+  //\r
+  if (Instance->UdpIo != NULL) {\r
+    UdpIoCleanIo (Instance->UdpIo);\r
+  }\r
+\r
+  if (Instance->McastUdpIo != NULL) {\r
+    UdpIoFreeIo (Instance->McastUdpIo);\r
+    Instance->McastUdpIo = NULL;\r
+  }\r
+\r
+  //\r
+  // Clean up the stored last packet.\r
+  //\r
+  if (Instance->LastPacket != NULL) {\r
+    NetbufFree (Instance->LastPacket);\r
+    Instance->LastPacket = NULL;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {\r
+    Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);\r
+    RemoveEntryList (Entry);\r
+    FreePool (Block);\r
+  }\r
+\r
+  //\r
+  // Reinitialize the corresponding fields of the Mtftp6 operation.\r
+  //\r
+  ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));\r
+  ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS));\r
+  ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  Instance->ServerCmdPort  = 0;\r
+  Instance->ServerDataPort = 0;\r
+  Instance->McastPort      = 0;\r
+  Instance->BlkSize        = 0;\r
+  Instance->LastBlk        = 0;\r
+  Instance->PacketToLive   = 0;\r
+  Instance->MaxRetry       = 0;\r
+  Instance->CurRetry       = 0;\r
+  Instance->Timeout        = 0;\r
+  Instance->IsMaster       = TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Start the Mtftp6 instance to perform the operation, such as read file,\r
+  write file, and read directory.\r
+\r
+  @param[in]  This                   The MTFTP session.\r
+  @param[in]  Token                  The token than encapsues the user's request.\r
+  @param[in]  OpCode                 The operation to perform.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Some of the parameters are invalid.\r
+  @retval EFI_NOT_STARTED        The MTFTP session hasn't been configured.\r
+  @retval EFI_ALREADY_STARTED    There is pending operation for the session.\r
+  @retval EFI_SUCCESS            The operation is successfully started.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6OperationStart (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token,\r
+  IN UINT16                 OpCode\r
+  )\r
+{\r
+  MTFTP6_INSTANCE           *Instance;\r
+  EFI_STATUS                Status;\r
+\r
+  if (This == NULL ||\r
+      Token == NULL ||\r
+      Token->Filename == NULL ||\r
+      (Token->OptionCount != 0 && Token->OptionList == NULL) ||\r
+      (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp))\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // At least define one method to collect the data for download.\r
+  //\r
+  if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) &&\r
+      Token->Buffer == NULL &&\r
+      Token->CheckPacket == NULL\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // At least define one method to provide the data for upload.\r
+  //\r
+  if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = MTFTP6_INSTANCE_FROM_THIS (This);\r
+\r
+  if (Instance->Config == NULL) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Instance->Token != NULL) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  Status           = EFI_SUCCESS;\r
+  Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // Parse the extension options in the request packet.\r
+  //\r
+  if (Token->OptionCount != 0) {\r
+\r
+    Status = Mtftp6ParseExtensionOption (\r
+               Token->OptionList,\r
+               Token->OptionCount,\r
+               TRUE,\r
+               &Instance->ExtInfo\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Initialize runtime data from config data or override data.\r
+  //\r
+  Instance->Token           = Token;\r
+  Instance->ServerCmdPort   = Instance->Config->InitialServerPort;\r
+  Instance->ServerDataPort  = 0;\r
+  Instance->MaxRetry        = Instance->Config->TryCount;\r
+  Instance->Timeout         = Instance->Config->TimeoutValue;\r
+  Instance->IsMaster        = TRUE;\r
+\r
+  CopyMem (\r
+    &Instance->ServerIp,\r
+    &Instance->Config->ServerIp,\r
+    sizeof (EFI_IPv6_ADDRESS)\r
+    );\r
+\r
+  if (Token->OverrideData != NULL) {\r
+    Instance->ServerCmdPort = Token->OverrideData->ServerPort;\r
+    Instance->MaxRetry      = Token->OverrideData->TryCount;\r
+    Instance->Timeout       = Token->OverrideData->TimeoutValue;\r
+\r
+    CopyMem (\r
+      &Instance->ServerIp,\r
+      &Token->OverrideData->ServerIp,\r
+      sizeof (EFI_IPv6_ADDRESS)\r
+      );\r
+  }\r
+\r
+  //\r
+  // Set default value for undefined parameters.\r
+  //\r
+  if (Instance->ServerCmdPort == 0) {\r
+    Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT;\r
+  }\r
+  if (Instance->BlkSize == 0) {\r
+    Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE;\r
+  }\r
+  if (Instance->MaxRetry == 0) {\r
+    Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY;\r
+  }\r
+  if (Instance->Timeout == 0) {\r
+    Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT;\r
+  }\r
+\r
+  Token->Status = EFI_NOT_READY;\r
+\r
+  //\r
+  // Switch the routines by the operation code.\r
+  //\r
+  switch (OpCode) {\r
+  case EFI_MTFTP6_OPCODE_RRQ:\r
+    Status = Mtftp6RrqStart (Instance, OpCode);\r
+    break;\r
+\r
+  case EFI_MTFTP6_OPCODE_DIR:\r
+    Status = Mtftp6RrqStart (Instance, OpCode);\r
+    break;\r
+\r
+  case EFI_MTFTP6_OPCODE_WRQ:\r
+    Status = Mtftp6WrqStart (Instance, OpCode);\r
+    break;\r
+\r
+  default:\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Return immediately for asynchronous or poll the instance for synchronous.\r
+  //\r
+  gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+  if (Token->Event == NULL) {\r
+    while (Token->Status == EFI_NOT_READY) {\r
+      This->Poll (This);\r
+    }\r
+    return Token->Status;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  Mtftp6OperationClean (Instance, Status);\r
+  gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The timer ticking routine for the Mtftp6 instance.\r
+\r
+  @param[in]  Event                  The pointer to the ticking event.\r
+  @param[in]  Context                The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6OnTimerTick (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  MTFTP6_SERVICE            *Service;\r
+  MTFTP6_INSTANCE           *Instance;\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  EFI_MTFTP6_TOKEN          *Token;\r
+  EFI_STATUS                Status;\r
+\r
+  Service = (MTFTP6_SERVICE *) Context;\r
+\r
+  //\r
+  // Iterate through all the children of the Mtftp service instance. Time\r
+  // out the packet. If maximum retries reached, clean the session up.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) {\r
+\r
+    Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link);\r
+\r
+    if (Instance->Token == NULL) {\r
+      continue;\r
+    }\r
+\r
+    if (Instance->PacketToLive > 0) {\r
+      Instance->PacketToLive--;\r
+      continue;\r
+    }\r
+\r
+    Instance->CurRetry++;\r
+    Token = Instance->Token;\r
+\r
+    if (Token->TimeoutCallback != NULL) {\r
+      //\r
+      // Call the timeout callback routine if has.\r
+      //\r
+      Status = Token->TimeoutCallback (&Instance->Mtftp6, Token);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        Mtftp6SendError (\r
+           Instance,\r
+           EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+           (UINT8 *) "User aborted the transfer in time out"\r
+           );\r
+        Mtftp6OperationClean (Instance, EFI_ABORTED);\r
+        continue;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Retransmit the packet if haven't reach the maxmium retry count,\r
+    // otherwise exit the transfer.\r
+    //\r
+    if (Instance->CurRetry < Instance->MaxRetry) {\r
+      Mtftp6TransmitPacket (Instance, Instance->LastPacket);\r
+    } else {\r
+      Mtftp6OperationClean (Instance, EFI_TIMEOUT);\r
+      continue;\r
+    }\r
+  }\r
+}\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h
new file mode 100644 (file)
index 0000000..37f03fe
--- /dev/null
@@ -0,0 +1,359 @@
+/** @file\r
+  Mtftp6 support functions declaration.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_MTFTP6_SUPPORT_H__\r
+#define __EFI_MTFTP6_SUPPORT_H__\r
+\r
+//\r
+// The structure representing a range of block numbers, [Start, End].\r
+// It is used to remember the holes in the MTFTP block space. If all\r
+// the holes are filled in, then the download or upload has completed.\r
+//\r
+typedef struct {\r
+  LIST_ENTRY                Link;\r
+  INTN                      Start;\r
+  INTN                      End;\r
+  INTN                      Round;\r
+  INTN                      Bound;\r
+} MTFTP6_BLOCK_RANGE;\r
+\r
+\r
+/**\r
+  Initialize the block range for either RRQ or WRQ. RRQ and WRQ have\r
+  different requirements for Start and End. For example, during startup,\r
+  WRQ initializes its whole valid block range to [0, 0xffff]. This\r
+  is because the server will send an ACK0 to inform the user to start the\r
+  upload. When the client receives an ACK0, it will remove 0 from the range,\r
+  get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
+  without option negotiation, the server will directly send us the BLOCK1\r
+  in response to the client's RRQ. When BLOCK1 is received, the client will\r
+  remove it from the block range and send an ACK. It also works if there\r
+  is option negotiation.\r
+\r
+  @param[in]  Head                   The block range head to initialize.\r
+  @param[in]  Start                  The Start block number.\r
+  @param[in]  End                    The last block number.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for initial block range.\r
+  @retval EFI_SUCCESS            The initial block range is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6InitBlockRange (\r
+  IN LIST_ENTRY             *Head,\r
+  IN UINT16                 Start,\r
+  IN UINT16                 End\r
+  );\r
+\r
+\r
+/**\r
+  Get the first valid block number on the range list.\r
+\r
+  @param[in]  Head                   The block range head.\r
+\r
+  @retval     ==-1                   If the block range is empty.\r
+  @retval     >-1                    The first valid block number.\r
+\r
+**/\r
+INTN\r
+Mtftp6GetNextBlockNum (\r
+  IN LIST_ENTRY             *Head\r
+  );\r
+\r
+\r
+/**\r
+  Set the last block number of the block range list. It\r
+  removes all the blocks after the Last. MTFTP initialize the\r
+  block range to the maximum possible range, such as [0, 0xffff]\r
+  for WRQ. When it gets the last block number, it calls\r
+  this function to set the last block number.\r
+\r
+  @param[in]  Head                   The block range list.\r
+  @param[in]  Last                   The last block number.\r
+\r
+**/\r
+VOID\r
+Mtftp6SetLastBlockNum (\r
+  IN LIST_ENTRY             *Head,\r
+  IN UINT16                 Last\r
+  );\r
+\r
+\r
+/**\r
+  Remove the block number from the block range list.\r
+\r
+  @param[in]  Head                   The block range list to remove from.\r
+  @param[in]  Num                    The block number to remove.\r
+  @param[in]  Completed              Whether Num is the last block number\r
+  @param[out] TotalBlock             The continuous block number in all\r
+\r
+  @retval EFI_NOT_FOUND          The block number isn't in the block range list.\r
+  @retval EFI_SUCCESS            The block number has been removed from the list.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RemoveBlockNum (\r
+  IN LIST_ENTRY             *Head,\r
+  IN UINT16                 Num,\r
+  IN BOOLEAN                Completed,\r
+  OUT UINT64                *TotalBlock\r
+  );\r
+\r
+\r
+/**\r
+  Build and transmit the request packet for the Mtftp6 instance.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  Operation              The operation code of this packet.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the request.\r
+  @retval EFI_SUCCESS            The request was built and sent.\r
+  @retval Others                 Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendRequest (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 Operation\r
+  );\r
+\r
+\r
+/**\r
+  Build and send an error packet.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  ErrCode                The error code in the packet.\r
+  @param[in]  ErrInfo                The error message in the packet.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the error packet.\r
+  @retval EFI_SUCCESS            The error packet was transmitted.\r
+  @retval Others                 Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendError (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 ErrCode,\r
+  IN UINT8*                 ErrInfo\r
+  );\r
+\r
+\r
+/**\r
+  Send the packet for the Mtftp6 instance.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  Packet                 The pointer to the packet to be sent.\r
+\r
+  @retval EFI_SUCCESS            The packet was sent out\r
+  @retval Others                 Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6TransmitPacket (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN NET_BUF                *Packet\r
+  );\r
+\r
+\r
+/**\r
+  Check packet for GetInfo callback routine.\r
+\r
+  @param[in]  This                   The pointer to the Mtftp6 protocol.\r
+  @param[in]  Token                  The pointer to the Mtftp6 token.\r
+  @param[in]  PacketLen              The length of the packet\r
+  @param[in]  Packet                 The pointer to the received packet.\r
+\r
+  @retval EFI_SUCCESS            The check process passed successfully.\r
+  @retval EFI_ABORTED            Abort the Mtftp6 operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6CheckPacket (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token,\r
+  IN UINT16                 PacketLen,\r
+  IN EFI_MTFTP6_PACKET      *Packet\r
+  );\r
+\r
+\r
+/**\r
+  The dummy configure routine for create a new Udp6 Io.\r
+\r
+  @param[in]  UdpIo                  The pointer to the Udp6 Io.\r
+  @param[in]  Context                The pointer to the context.\r
+\r
+  @retval EFI_SUCCESS                The value is always returned.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ConfigDummyUdpIo (\r
+  IN UDP_IO                 *UdpIo,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+\r
+/**\r
+  The configure routine for the Mtftp6 instance to transmit/receive.\r
+\r
+  @param[in]  UdpIo                  The pointer to the Udp6 Io.\r
+  @param[in]  ServerIp               The pointer to the server address.\r
+  @param[in]  ServerPort             The pointer to the server port.\r
+  @param[in]  LocalIp                The pointer to the local address.\r
+  @param[in]  LocalPort              The pointer to the local port.\r
+\r
+  @retval EFI_SUCCESS            Configure the Udp6 Io for Mtftp6 successfully.\r
+  @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not been\r
+                                 configured yet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ConfigUdpIo (\r
+  IN UDP_IO                 *UdpIo,\r
+  IN EFI_IPv6_ADDRESS       *ServerIp,\r
+  IN UINT16                 ServerPort,\r
+  IN EFI_IPv6_ADDRESS       *LocalIp,\r
+  IN UINT16                 LocalPort\r
+  );\r
+\r
+\r
+/**\r
+  Clean up the current Mtftp6 operation.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  Result                 The result to be returned to the user.\r
+\r
+**/\r
+VOID\r
+Mtftp6OperationClean (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN EFI_STATUS             Result\r
+  );\r
+\r
+\r
+/**\r
+  Start the Mtftp6 instance to perform the operation, such as read file,\r
+  write file, and read directory.\r
+\r
+  @param[in]  This                   The MTFTP session\r
+  @param[in]  Token                  The token that encapsulates the user's request.\r
+  @param[in]  OpCode                 The operation to perform.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Some of the parameters are invalid.\r
+  @retval EFI_NOT_STARTED        The MTFTP session hasn't been configured.\r
+  @retval EFI_ALREADY_STARTED    There is pending operation for the session.\r
+  @retval EFI_SUCCESS            The operation was successfully started.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6OperationStart (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token,\r
+  IN UINT16                 OpCode\r
+  );\r
+\r
+\r
+/**\r
+  The timer ticking routine for the Mtftp6 instance.\r
+\r
+  @param[in]  Event                  The pointer to the ticking event.\r
+  @param[in]  Context                The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6OnTimerTick (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+\r
+/**\r
+  The packet process callback for Mtftp6 upload.\r
+\r
+  @param[in]  UdpPacket             The pointer to the packet received.\r
+  @param[in]  UdpEpt                The pointer to the Udp6 access point.\r
+  @param[in]  IoStatus              The status from the Udp6 instance.\r
+  @param[in]  Context               The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6WrqInput (\r
+  IN NET_BUF                *UdpPacket,\r
+  IN UDP_END_POINT          *UdpEpt,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+\r
+/**\r
+  Start the Mtftp6 instance to upload. It will first init some states,\r
+  then send the WRQ request packet, and start to receive the packet.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  Operation             The operation code of current packet.\r
+\r
+  @retval EFI_SUCCESS           The Mtftp6 was started to upload.\r
+  @retval Others                Failed to start to upload.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqStart (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 Operation\r
+  );\r
+\r
+\r
+/**\r
+  The packet process callback for Mtftp6 download.\r
+\r
+  @param[in]  UdpPacket             The pointer to the packet received.\r
+  @param[in]  UdpEpt                The pointer to the Udp6 access point.\r
+  @param[in]  IoStatus              The status from Udp6 instance.\r
+  @param[in]  Context               The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6RrqInput (\r
+  IN NET_BUF                *UdpPacket,\r
+  IN UDP_END_POINT          *UdpEpt,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  );\r
+\r
+\r
+/**\r
+  Start the Mtftp6 instance to download. It first initializes some\r
+  of the internal states then builds and sends an RRQ reqeuest packet.\r
+  Finally, it starts receive for the downloading.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  Operation             The operation code of current packet.\r
+\r
+  @retval EFI_SUCCESS           The Mtftp6 was started to download.\r
+  @retval Others                Failed to start to download.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqStart (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 Operation\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
new file mode 100644 (file)
index 0000000..69ef4d2
--- /dev/null
@@ -0,0 +1,602 @@
+/** @file\r
+  Mtftp6 Wrq process functions implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+\r
+/**\r
+  Build and send a Mtftp6 data packet for upload.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  BlockNum              The block num to be sent.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the packet.\r
+  @retval EFI_SUCCESS           The data packet was sent.\r
+  @retval EFI_ABORTED           The user aborted this process.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqSendBlock (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 BlockNum\r
+  )\r
+{\r
+  EFI_MTFTP6_PACKET         *Packet;\r
+  EFI_MTFTP6_TOKEN          *Token;\r
+  NET_BUF                   *UdpPacket;\r
+  EFI_STATUS                Status;\r
+  UINT16                    DataLen;\r
+  UINT8                     *DataBuf;\r
+  UINT64                    Start;\r
+\r
+  //\r
+  // Allocate net buffer to create data packet.\r
+  //\r
+  UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN);\r
+\r
+  if (UdpPacket == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (\r
+                                   UdpPacket,\r
+                                   MTFTP6_DATA_HEAD_LEN,\r
+                                   FALSE\r
+                                   );\r
+  ASSERT (Packet != NULL);\r
+\r
+  Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA);\r
+  Packet->Data.Block  = HTONS (BlockNum);\r
+\r
+  //\r
+  // Read the block from either the buffer or PacketNeeded callback\r
+  //\r
+  Token   = Instance->Token;\r
+  DataLen = Instance->BlkSize;\r
+\r
+  if (Token->Buffer != NULL) {\r
+    Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);\r
+\r
+    if (Token->BufferSize < Start + Instance->BlkSize) {\r
+      DataLen           = (UINT16) (Token->BufferSize - Start);\r
+      Instance->LastBlk = BlockNum;\r
+      Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);\r
+    }\r
+\r
+    if (DataLen > 0) {\r
+      NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
+      CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);\r
+    }\r
+\r
+  } else {\r
+    //\r
+    // Get data from PacketNeeded\r
+    //\r
+    DataBuf = NULL;\r
+    Status  = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf);\r
+\r
+    if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {\r
+      if (DataBuf != NULL) {\r
+        gBS->FreePool (DataBuf);\r
+      }\r
+      //\r
+      // The received packet has already been freed.\r
+      //\r
+      Mtftp6SendError (\r
+        Instance,\r
+        EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+        (UINT8 *) "User aborted the transfer"\r
+        );\r
+\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    if (DataLen < Instance->BlkSize) {\r
+      Instance->LastBlk = BlockNum;\r
+      Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);\r
+    }\r
+\r
+    if (DataLen > 0) {\r
+      NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
+      CopyMem (Packet->Data.Data, DataBuf, DataLen);\r
+      gBS->FreePool (DataBuf);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Reset current retry count of the instance.\r
+  //\r
+  Instance->CurRetry = 0;\r
+\r
+  return Mtftp6TransmitPacket (Instance, UdpPacket);\r
+}\r
+\r
+\r
+/**\r
+  Function to handle received ACK packet. If the ACK number matches the\r
+  expected block number, with more data pending, send the next\r
+  block. Otherwise, tell the caller that we are done.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  Packet                The pointer to the received packet.\r
+  @param[in]  Len                   The length of the packet.\r
+  @param[out] UdpPacket             The net buf of received packet.\r
+  @param[out] IsCompleted           If TRUE, the upload has been completed.\r
+                                    Otherwise, the upload has not been completed.\r
+\r
+  @retval EFI_SUCCESS           The ACK packet successfully processed.\r
+  @retval EFI_TFTP_ERROR        The block number loops back.\r
+  @retval Others                Failed to transmit the next data packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqHandleAck (\r
+  IN  MTFTP6_INSTANCE       *Instance,\r
+  IN  EFI_MTFTP6_PACKET     *Packet,\r
+  IN  UINT32                Len,\r
+  OUT NET_BUF               **UdpPacket,\r
+  OUT BOOLEAN               *IsCompleted\r
+  )\r
+{\r
+  UINT16                    AckNum;\r
+  INTN                      Expected;\r
+  UINT64                    TotalBlock;\r
+\r
+  *IsCompleted = FALSE;\r
+  AckNum       = NTOHS (Packet->Ack.Block[0]);\r
+  Expected     = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+  ASSERT (Expected >= 0);\r
+\r
+  //\r
+  // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput\r
+  // restart receive.\r
+  //\r
+  if (Expected != AckNum) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Remove the acked block number, if this is the last block number,\r
+  // tell the Mtftp6WrqInput to finish the transfer. This is the last\r
+  // block number if the block range are empty..\r
+  //\r
+  Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &TotalBlock);\r
+\r
+  Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+  if (Expected < 0) {\r
+    //\r
+    // The block range is empty. It may either because the the last\r
+    // block has been ACKed, or the sequence number just looped back,\r
+    // that is, there is more than 0xffff blocks.\r
+    //\r
+    if (Instance->LastBlk == AckNum) {\r
+      ASSERT (Instance->LastBlk >= 1);\r
+      *IsCompleted = TRUE;\r
+      return EFI_SUCCESS;\r
+\r
+    } else {\r
+      //\r
+      // Free the received packet before send new packet in ReceiveNotify,\r
+      // since the udpio might need to be reconfigured.\r
+      //\r
+      NetbufFree (*UdpPacket);\r
+      *UdpPacket = NULL;\r
+      //\r
+      // Send the Mtftp6 error message if block number rolls back.\r
+      //\r
+      Mtftp6SendError (\r
+        Instance,\r
+        EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+        (UINT8 *) "Block number rolls back, not supported, try blksize option"\r
+        );\r
+\r
+      return EFI_TFTP_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Free the receive buffer before send new packet since it might need\r
+  // reconfigure udpio.\r
+  //\r
+  NetbufFree (*UdpPacket);\r
+  *UdpPacket = NULL;\r
+\r
+  return Mtftp6WrqSendBlock (Instance, (UINT16) Expected);\r
+}\r
+\r
+\r
+/**\r
+  Check whether the received OACK is valid. The OACK is valid\r
+  only if:\r
+  1. It only include options requested by us.\r
+  2. It can only include a smaller block size.\r
+  3. It can't change the proposed time out value.\r
+  4. Other requirements of the individal MTFTP6 options as required.\r
+\r
+  @param[in]  ReplyInfo             The pointer to options information in reply packet.\r
+  @param[in]  RequestInfo           The pointer to requested options information.\r
+\r
+  @retval     TRUE                  If the option in OACK is valid.\r
+  @retval     FALSE                 If the option is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+Mtftp6WrqOackValid (\r
+  IN MTFTP6_EXT_OPTION_INFO     *ReplyInfo,\r
+  IN MTFTP6_EXT_OPTION_INFO     *RequestInfo\r
+  )\r
+{\r
+  //\r
+  // It is invalid for server to return options we don't request\r
+  //\r
+  if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Server can only specify a smaller block size to be used and\r
+  // return the timeout matches that requested.\r
+  //\r
+  if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||\r
+      (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))\r
+      ) {\r
+\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Process the OACK packet for Wrq.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  Packet                The pointer to the received packet.\r
+  @param[in]  Len                   The length of the packet.\r
+  @param[out] UdpPacket             The net buf of received packet.\r
+  @param[out] IsCompleted           If TRUE, the upload has been completed.\r
+                                    Otherwise, the upload has not been completed.\r
+\r
+  @retval EFI_SUCCESS           The OACK packet successfully processed.\r
+  @retval EFI_TFTP_ERROR        An TFTP communication error happened.\r
+  @retval Others                Failed to process the OACK packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqHandleOack (\r
+  IN  MTFTP6_INSTANCE       *Instance,\r
+  IN  EFI_MTFTP6_PACKET     *Packet,\r
+  IN  UINT32                Len,\r
+  OUT NET_BUF               **UdpPacket,\r
+  OUT BOOLEAN               *IsCompleted\r
+  )\r
+{\r
+  EFI_MTFTP6_OPTION         *Options;\r
+  UINT32                    Count;\r
+  MTFTP6_EXT_OPTION_INFO    ExtInfo;\r
+  EFI_MTFTP6_PACKET         Dummy;\r
+  EFI_STATUS                Status;\r
+  INTN                      Expected;\r
+\r
+  *IsCompleted = FALSE;\r
+\r
+  //\r
+  // Ignore the OACK if already started the upload\r
+  //\r
+  Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+  if (Expected != 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Parse and validate the options from server\r
+  //\r
+  ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));\r
+\r
+  Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);\r
+\r
+  if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) {\r
+    //\r
+    // Don't send a MTFTP error packet when out of resource, it can\r
+    // only make it worse.\r
+    //\r
+    if (Status != EFI_OUT_OF_RESOURCES) {\r
+      //\r
+      // Free the received packet before send new packet in ReceiveNotify,\r
+      // since the udpio might need to be reconfigured.\r
+      //\r
+      NetbufFree (*UdpPacket);\r
+      *UdpPacket = NULL;\r
+      //\r
+      // Send the Mtftp6 error message if invalid Oack packet received.\r
+      //\r
+      Mtftp6SendError (\r
+        Instance,\r
+        EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,\r
+        (UINT8 *) "Mal-formated OACK packet"\r
+        );\r
+    }\r
+\r
+    return EFI_TFTP_ERROR;\r
+  }\r
+\r
+  if (ExtInfo.BlkSize != 0) {\r
+    Instance->BlkSize = ExtInfo.BlkSize;\r
+  }\r
+\r
+  if (ExtInfo.Timeout != 0) {\r
+    Instance->Timeout = ExtInfo.Timeout;\r
+  }\r
+\r
+  //\r
+  // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck,\r
+  // which will start the transmission of the first data block.\r
+  //\r
+  Dummy.Ack.OpCode   = HTONS (EFI_MTFTP6_OPCODE_ACK);\r
+  Dummy.Ack.Block[0] = 0;\r
+\r
+  return Mtftp6WrqHandleAck (\r
+           Instance,\r
+           &Dummy,\r
+           sizeof (EFI_MTFTP6_ACK_HEADER),\r
+           UdpPacket,\r
+           IsCompleted\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  The packet process callback for Mtftp6 upload.\r
+\r
+  @param[in]  UdpPacket             The pointer to the packet received.\r
+  @param[in]  UdpEpt                The pointer to the Udp6 access point.\r
+  @param[in]  IoStatus              The status from Udp6 instance.\r
+  @param[in]  Context               The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6WrqInput (\r
+  IN NET_BUF                *UdpPacket,\r
+  IN UDP_END_POINT          *UdpEpt,\r
+  IN EFI_STATUS             IoStatus,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  MTFTP6_INSTANCE           *Instance;\r
+  EFI_MTFTP6_PACKET         *Packet;\r
+  BOOLEAN                   IsCompleted;\r
+  EFI_STATUS                Status;\r
+  UINT32                    TotalNum;\r
+  UINT32                    Len;\r
+  UINT16                    Opcode;\r
+\r
+  Instance = (MTFTP6_INSTANCE *) Context;\r
+\r
+  NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);\r
+\r
+  IsCompleted = FALSE;\r
+  Packet      = NULL;\r
+  Status      = EFI_SUCCESS;\r
+  TotalNum    = 0;\r
+\r
+  //\r
+  // Return error status if Udp6 instance failed to receive.\r
+  //\r
+  if (EFI_ERROR (IoStatus)) {\r
+    Status = IoStatus;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  ASSERT (UdpPacket != NULL);\r
+\r
+  if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Client send initial request to server's listening port. Server\r
+  // will select a UDP port to communicate with the client.\r
+  //\r
+  if (UdpEpt->RemotePort != Instance->ServerDataPort) {\r
+    if (Instance->ServerDataPort != 0) {\r
+      goto ON_EXIT;\r
+    } else {\r
+      Instance->ServerDataPort = UdpEpt->RemotePort;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
+  //\r
+  Len      = UdpPacket->TotalSize;\r
+  TotalNum = UdpPacket->BlockOpNum;\r
+\r
+  if (TotalNum > 1) {\r
+    Packet = AllocateZeroPool (Len);\r
+\r
+    if (Packet == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
+\r
+  } else {\r
+    Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
+    ASSERT (Packet != NULL);\r
+  }\r
+\r
+  Opcode = NTOHS (Packet->OpCode);\r
+\r
+  //\r
+  // Callback to the user's CheckPacket if provided. Abort the transmission\r
+  // if CheckPacket returns an EFI_ERROR code.\r
+  //\r
+  if (Instance->Token->CheckPacket != NULL &&\r
+      (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)\r
+      ) {\r
+\r
+    Status = Instance->Token->CheckPacket (\r
+                                &Instance->Mtftp6,\r
+                                Instance->Token,\r
+                                (UINT16) Len,\r
+                                Packet\r
+                                );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Send an error message to the server to inform it\r
+      //\r
+      if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {\r
+        //\r
+        // Free the received packet before send new packet in ReceiveNotify,\r
+        // since the udpio might need to be reconfigured.\r
+        //\r
+        NetbufFree (UdpPacket);\r
+        UdpPacket = NULL;\r
+        //\r
+        // Send the Mtftp6 error message if user aborted the current session.\r
+        //\r
+        Mtftp6SendError (\r
+          Instance,\r
+          EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+          (UINT8 *) "User aborted the transfer"\r
+          );\r
+      }\r
+\r
+      Status = EFI_ABORTED;\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Switch the process routines by the operation code.\r
+  //\r
+  switch (Opcode) {\r
+  case EFI_MTFTP6_OPCODE_ACK:\r
+    if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) {\r
+      goto ON_EXIT;\r
+    }\r
+    //\r
+    // Handle the Ack packet of Wrq.\r
+    //\r
+    Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted);\r
+    break;\r
+\r
+  case EFI_MTFTP6_OPCODE_OACK:\r
+    if (Len <= MTFTP6_OPCODE_LEN) {\r
+      goto ON_EXIT;\r
+    }\r
+    //\r
+    // Handle the Oack packet of Wrq.\r
+    //\r
+    Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted);\r
+    break;\r
+\r
+  default:\r
+    //\r
+    // Drop and return eror if received error message.\r
+    //\r
+    Status = EFI_TFTP_ERROR;\r
+    break;\r
+  }\r
+\r
+ON_EXIT:\r
+  //\r
+  // Free the resources, then if !EFI_ERROR (Status) and not completed,\r
+  // restart the receive, otherwise end the session.\r
+  //\r
+  if (Packet != NULL && TotalNum > 1) {\r
+    FreePool (Packet);\r
+  }\r
+\r
+  if (UdpPacket != NULL) {\r
+    NetbufFree (UdpPacket);\r
+  }\r
+\r
+  if (!EFI_ERROR (Status) && !IsCompleted) {\r
+    Status = UdpIoRecvDatagram (\r
+               Instance->UdpIo,\r
+               Mtftp6WrqInput,\r
+               Instance,\r
+               0\r
+               );\r
+  }\r
+  //\r
+  // Clean up the current session if failed to continue.\r
+  //\r
+  if (EFI_ERROR (Status) || IsCompleted) {\r
+    Mtftp6OperationClean (Instance, Status);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Start the Mtftp6 instance to upload. It will first init some states,\r
+  then send the WRQ request packet, and start to receive the packet.\r
+\r
+  @param[in]  Instance              The pointer to the Mtftp6 instance.\r
+  @param[in]  Operation             The operation code of the current packet.\r
+\r
+  @retval EFI_SUCCESS           The Mtftp6 was started to upload.\r
+  @retval Others                Failed to start to upload.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqStart (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 Operation\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // The valid block number range are [0, 0xffff]. For example:\r
+  // the client sends an WRQ request to the server, the server\r
+  // ACK with an ACK0 to let client start transfer the first\r
+  // packet.\r
+  //\r
+  Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = Mtftp6SendRequest (Instance, Operation);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return UdpIoRecvDatagram (\r
+           Instance->UdpIo,\r
+           Mtftp6WrqInput,\r
+           Instance,\r
+           0\r
+           );\r
+}\r
+\r
diff --git a/NetworkPkg/NetworkPkg.dec b/NetworkPkg/NetworkPkg.dec
new file mode 100644 (file)
index 0000000..0c8df4e
--- /dev/null
@@ -0,0 +1,21 @@
+## @file\r
+#\r
+# This package provides network modules that conform to UEFI 2.2 specification.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials are licensed and made available under\r
+# the terms and conditions of the BSD License which accompanies this distribution.\r
+# 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
+#\r
+##\r
+\r
+[Defines]\r
+  DEC_SPECIFICATION              = 0x00010005\r
+  PACKAGE_NAME                   = NetworkPkg\r
+  PACKAGE_GUID                   = 947988BE-8D5C-471a-893D-AD181C46BEBB\r
+  PACKAGE_VERSION                = 0.92\r
diff --git a/NetworkPkg/NetworkPkg.dsc b/NetworkPkg/NetworkPkg.dsc
new file mode 100644 (file)
index 0000000..b12d5fd
--- /dev/null
@@ -0,0 +1,95 @@
+## @file\r
+# UEFI 2.2 Network Module Package for All Architectures\r
+#\r
+# Copyright (c) 2009 - 2010, 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
+#    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
+#\r
+##\r
+\r
+[Defines]\r
+  PLATFORM_NAME                  = NetworkPkg\r
+  PLATFORM_GUID                  = 3FD34E9B-E90C-44e1-B510-1F632A509F10\r
+  PLATFORM_VERSION               = 0.92\r
+  DSC_SPECIFICATION              = 0x00010005\r
+  OUTPUT_DIRECTORY               = Build/NetworkPkg\r
+  SUPPORTED_ARCHITECTURES        = IA32|IPF|X64|EBC|ARM\r
+  BUILD_TARGETS                  = DEBUG|RELEASE\r
+  SKUID_IDENTIFIER               = DEFAULT\r
+\r
+[LibraryClasses]\r
+  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf\r
+  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf\r
+  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf\r
+  HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf\r
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf\r
+  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf\r
+  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf\r
+  UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf\r
+  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf\r
+  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf\r
+  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf\r
+  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf\r
+  UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf\r
+\r
+  DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf\r
+  NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf\r
+  IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf\r
+  UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf\r
+\r
+[LibraryClasses.common.UEFI_DRIVER]\r
+  DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf\r
+\r
+[LibraryClasses.common.UEFI_APPLICATION]\r
+  DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf\r
+  FileHandleLib|ShellPkg/Library/BaseFileHandleLib/BaseFileHandleLib.inf\r
+  ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf\r
+\r
+[PcdsFeatureFlag]\r
+  gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable|TRUE\r
+  gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable|TRUE\r
+\r
+[PcdsFixedAtBuild]\r
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2f\r
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000\r
+\r
+###################################################################################################\r
+#\r
+# Components Section - list of the modules and components that will be processed by compilation\r
+#                      tools and the EDK II tools to generate PE32/PE32+/Coff image files.\r
+#\r
+# Note: The EDK II DSC file is not used to specify how compiled binary images get placed\r
+#       into firmware volume images. This section is just a list of modules to compile from\r
+#       source into UEFI-compliant binaries.\r
+#       It is the FDF file that contains information on combining binary files into firmware\r
+#       volume images, whose concept is beyond UEFI and is described in PI specification.\r
+#       Binary modules do not need to be listed in this section, as they should be\r
+#       specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi),\r
+#       Logo (Logo.bmp), and etc.\r
+#       There may also be modules listed in this section that are not required in the FDF file,\r
+#       When a module listed here is excluded from FDF file, then UEFI-compliant binary will be\r
+#       generated for it, but the binary will not be put into any firmware volume.\r
+#\r
+###################################################################################################\r
+\r
+[Components]\r
+  NetworkPkg/IpSecDxe/IpSecDxe.inf\r
+  NetworkPkg/Ip6Dxe/Ip6Dxe.inf\r
+  NetworkPkg/TcpDxe/TcpDxe.inf\r
+  NetworkPkg/Udp6Dxe/Udp6Dxe.inf\r
+  NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf\r
+  NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf\r
+\r
+\r
+[Components.IA32, Components.X64, Components.IPF]\r
+  NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf\r
+  NetworkPkg/Application/Ping6/Ping6.inf\r
+  NetworkPkg/Application/IfConfig6/IfConfig6.inf\r
+  NetworkPkg/Application/IpsecConfig/IpSecConfig.inf\r
+  NetworkPkg/Application/VConfig/VConfig.inf\r
diff --git a/NetworkPkg/TcpDxe/ComponentName.c b/NetworkPkg/TcpDxe/ComponentName.c
new file mode 100644 (file)
index 0000000..956792a
--- /dev/null
@@ -0,0 +1,304 @@
+/** @file\r
+  Implementation of protocols EFI_COMPONENT_NAME_PROTOCOL and\r
+  EFI_COMPONENT_NAME2_PROTOCOL.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This, and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language or DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language, from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle  OPTIONAL,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  );\r
+\r
+///\r
+/// EFI Component Name Protocol\r
+///\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL     gTcpComponentName = {\r
+  TcpComponentNameGetDriverName,\r
+  TcpComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+///\r
+/// EFI Component Name 2 Protocol\r
+///\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL    gTcpComponentName2 = {\r
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) TcpComponentNameGetDriverName,\r
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) TcpComponentNameGetControllerName,\r
+  "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE        mTcpDriverNameTable[] = {\r
+  {\r
+    "eng;en",\r
+    L"TCP Network Service Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This, and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language or DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+{\r
+  return LookupUnicodeString2 (\r
+           Language,\r
+           This->SupportedLanguages,\r
+           mTcpDriverNameTable,\r
+           DriverName,\r
+           (BOOLEAN) (This == &gTcpComponentName)\r
+           );\r
+}\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language, from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle  OPTIONAL,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/NetworkPkg/TcpDxe/SockImpl.c b/NetworkPkg/TcpDxe/SockImpl.c
new file mode 100644 (file)
index 0000000..7fad042
--- /dev/null
@@ -0,0 +1,1230 @@
+/** @file\r
+  Implementation of the Socket.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "SockImpl.h"\r
+\r
+/**\r
+  Get the first buffer block in the specific socket buffer.\r
+\r
+  @param[in]  Sockbuf               Pointer to the socket buffer.\r
+\r
+  @return Pointer to the first buffer in the queue. NULL if the queue is empty.\r
+\r
+**/\r
+NET_BUF *\r
+SockBufFirst (\r
+  IN SOCK_BUFFER *Sockbuf\r
+  )\r
+{\r
+  LIST_ENTRY  *NetbufList;\r
+\r
+  NetbufList = &(Sockbuf->DataQueue->BufList);\r
+\r
+  if (IsListEmpty (NetbufList)) {\r
+    return NULL;\r
+  }\r
+\r
+  return NET_LIST_HEAD (NetbufList, NET_BUF, List);\r
+}\r
+\r
+/**\r
+  Get the next buffer block in the specific socket buffer.\r
+\r
+  @param[in]  Sockbuf     Pointer to the socket buffer.\r
+  @param[in]  SockEntry   Pointer to the buffer block prior to the required one.\r
+\r
+  @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is\r
+          the tail or head entry.\r
+\r
+**/\r
+NET_BUF *\r
+SockBufNext (\r
+  IN SOCK_BUFFER *Sockbuf,\r
+  IN NET_BUF     *SockEntry\r
+  )\r
+{\r
+  LIST_ENTRY  *NetbufList;\r
+\r
+  NetbufList = &(Sockbuf->DataQueue->BufList);\r
+\r
+  if ((SockEntry->List.ForwardLink == NetbufList) ||\r
+      (SockEntry->List.BackLink == &SockEntry->List) ||\r
+      (SockEntry->List.ForwardLink == &SockEntry->List)\r
+      ) {\r
+\r
+    return NULL;\r
+  }\r
+\r
+  return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List);\r
+}\r
+\r
+/**\r
+  User provided callback function for NetbufFromExt.\r
+\r
+  @param[in] Event    The Event this notify function registered to, ignored.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+SockFreeFoo (\r
+  IN EFI_EVENT Event\r
+  )\r
+{\r
+  return;\r
+}\r
+\r
+/**\r
+  Get the length of the data that can be retrieved from the socket\r
+  receive buffer.\r
+\r
+  @param[in]  SockBuffer            Pointer to the socket receive buffer.\r
+  @param[out] IsUrg                 Pointer to a BOOLEAN variable.\r
+                                    If TRUE the data is OOB.\r
+  @param[in]  BufLen                The maximum length of the data buffer to\r
+                                    store the received data in the socket layer.\r
+\r
+  @return The length of the data can be retreived.\r
+\r
+**/\r
+UINT32\r
+SockTcpDataToRcv (\r
+  IN  SOCK_BUFFER   *SockBuffer,\r
+  OUT BOOLEAN       *IsUrg,\r
+  IN  UINT32        BufLen\r
+  )\r
+{\r
+  NET_BUF       *RcvBufEntry;\r
+  UINT32        DataLen;\r
+  TCP_RSV_DATA  *TcpRsvData;\r
+  BOOLEAN       Urg;\r
+\r
+  ASSERT ((SockBuffer != NULL) && (IsUrg != NULL) && (BufLen > 0));\r
+\r
+  //\r
+  // Get the first socket receive buffer\r
+  //\r
+  RcvBufEntry = SockBufFirst (SockBuffer);\r
+  ASSERT (RcvBufEntry != NULL);\r
+\r
+  TcpRsvData  = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;\r
+\r
+  //\r
+  // Check whether the receive data is out of bound. If yes, calculate the maximum\r
+  // allowed length of the urgent data and output it.\r
+  //\r
+  *IsUrg      = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);\r
+\r
+  if (*IsUrg && (TcpRsvData->UrgLen < RcvBufEntry->TotalSize)) {\r
+\r
+    DataLen = MIN (TcpRsvData->UrgLen, BufLen);\r
+\r
+    if (DataLen < TcpRsvData->UrgLen) {\r
+      TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen;\r
+    } else {\r
+      TcpRsvData->UrgLen = 0;\r
+    }\r
+\r
+    return DataLen;\r
+\r
+  }\r
+\r
+  //\r
+  // Process the next socket receive buffer to get the maximum allowed length\r
+  // of the received data.\r
+  //\r
+  DataLen     = RcvBufEntry->TotalSize;\r
+\r
+  RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);\r
+\r
+  while ((BufLen > DataLen) && (RcvBufEntry != NULL)) {\r
+\r
+    TcpRsvData  = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;\r
+\r
+    Urg         = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);\r
+\r
+    if (*IsUrg != Urg) {\r
+      break;\r
+    }\r
+\r
+    if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) {\r
+\r
+      if (TcpRsvData->UrgLen + DataLen < BufLen) {\r
+        TcpRsvData->UrgLen = 0;\r
+      } else {\r
+        TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen);\r
+      }\r
+\r
+      return MIN (TcpRsvData->UrgLen + DataLen, BufLen);\r
+\r
+    }\r
+\r
+    DataLen += RcvBufEntry->TotalSize;\r
+\r
+    RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);\r
+  }\r
+\r
+  DataLen = MIN (BufLen, DataLen);\r
+  return DataLen;\r
+}\r
+\r
+/**\r
+  Copy data from socket buffer to an application provided receive buffer.\r
+\r
+  @param[in]  Sock        Pointer to the socket.\r
+  @param[in]  TcpRxData   Pointer to the application provided receive buffer.\r
+  @param[in]  RcvdBytes   The maximum length of the data can be copied.\r
+  @param[in]  IsUrg       If TRUE the data is Out of Bound, FALSE the data is normal.\r
+\r
+**/\r
+VOID\r
+SockSetTcpRxData (\r
+  IN SOCKET     *Sock,\r
+  IN VOID       *TcpRxData,\r
+  IN UINT32     RcvdBytes,\r
+  IN BOOLEAN    IsUrg\r
+  )\r
+{\r
+  UINT32                  Index;\r
+  UINT32                  CopyBytes;\r
+  UINT32                  OffSet;\r
+  EFI_TCP4_RECEIVE_DATA   *RxData;\r
+  EFI_TCP4_FRAGMENT_DATA  *Fragment;\r
+\r
+  RxData  = (EFI_TCP4_RECEIVE_DATA *) TcpRxData;\r
+\r
+  OffSet  = 0;\r
+\r
+  ASSERT (RxData->DataLength >= RcvdBytes);\r
+\r
+  RxData->DataLength  = RcvdBytes;\r
+  RxData->UrgentFlag  = IsUrg;\r
+\r
+  //\r
+  // Copy the CopyBytes data from socket receive buffer to RxData.\r
+  //\r
+  for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) {\r
+\r
+    Fragment  = &RxData->FragmentTable[Index];\r
+    CopyBytes = MIN ((UINT32) (Fragment->FragmentLength), RcvdBytes);\r
+\r
+    NetbufQueCopy (\r
+      Sock->RcvBuffer.DataQueue,\r
+      OffSet,\r
+      CopyBytes,\r
+      Fragment->FragmentBuffer\r
+      );\r
+\r
+    Fragment->FragmentLength = CopyBytes;\r
+    RcvdBytes -= CopyBytes;\r
+    OffSet += CopyBytes;\r
+  }\r
+}\r
+\r
+/**\r
+  Process the send token.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockProcessSndToken (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  UINT32                  FreeSpace;\r
+  SOCK_TOKEN              *SockToken;\r
+  UINT32                  DataLen;\r
+  SOCK_IO_TOKEN           *SndToken;\r
+  EFI_TCP4_TRANSMIT_DATA  *TxData;\r
+  EFI_STATUS              Status;\r
+\r
+  ASSERT ((Sock != NULL) && (SockStream == Sock->Type));\r
+\r
+  FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);\r
+\r
+  //\r
+  // to determine if process a send token using\r
+  // socket layer flow control policy\r
+  //\r
+  while ((FreeSpace >= Sock->SndBuffer.LowWater) && !IsListEmpty (&Sock->SndTokenList)) {\r
+\r
+    SockToken = NET_LIST_HEAD (\r
+                  &(Sock->SndTokenList),\r
+                  SOCK_TOKEN,\r
+                  TokenList\r
+                  );\r
+\r
+    //\r
+    // process this token\r
+    //\r
+    RemoveEntryList (&(SockToken->TokenList));\r
+    InsertTailList (\r
+      &(Sock->ProcessingSndTokenList),\r
+      &(SockToken->TokenList)\r
+      );\r
+\r
+    //\r
+    // Proceess it in the light of SockType\r
+    //\r
+    SndToken  = (SOCK_IO_TOKEN *) SockToken->Token;\r
+    TxData    = SndToken->Packet.TxData;\r
+\r
+    DataLen   = TxData->DataLength;\r
+    Status    = SockProcessTcpSndData (Sock, TxData);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto OnError;\r
+    }\r
+\r
+    if (DataLen >= FreeSpace) {\r
+      FreeSpace = 0;\r
+\r
+    } else {\r
+      FreeSpace -= DataLen;\r
+\r
+    }\r
+  }\r
+\r
+  return;\r
+\r
+OnError:\r
+\r
+  RemoveEntryList (&SockToken->TokenList);\r
+  SIGNAL_TOKEN (SockToken->Token, Status);\r
+  FreePool (SockToken);\r
+}\r
+\r
+/**\r
+  Get received data from the socket layer to the receive token.\r
+\r
+  @param[in, out]  Sock       Pointer to the socket.\r
+  @param[in, out]  RcvToken   Pointer to the application provided receive token.\r
+\r
+  @return The length of data received in this token.\r
+\r
+**/\r
+UINT32\r
+SockProcessRcvToken (\r
+  IN OUT SOCKET        *Sock,\r
+  IN OUT SOCK_IO_TOKEN *RcvToken\r
+  )\r
+{\r
+  UINT32                TokenRcvdBytes;\r
+  EFI_TCP4_RECEIVE_DATA *RxData;\r
+  BOOLEAN               IsUrg;\r
+\r
+  ASSERT (Sock != NULL);\r
+\r
+  ASSERT (SockStream == Sock->Type);\r
+\r
+  RxData = RcvToken->Packet.RxData;\r
+\r
+  TokenRcvdBytes = SockTcpDataToRcv (\r
+                     &Sock->RcvBuffer,\r
+                     &IsUrg,\r
+                     RxData->DataLength\r
+                     );\r
+\r
+  //\r
+  // Copy data from RcvBuffer of socket to user\r
+  // provided RxData and set the fields in TCP RxData\r
+  //\r
+  SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg);\r
+\r
+  NetbufQueTrim (Sock->RcvBuffer.DataQueue, TokenRcvdBytes);\r
+  SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS);\r
+\r
+  return TokenRcvdBytes;\r
+}\r
+\r
+/**\r
+  Process the TCP send data, buffer the tcp txdata, and append\r
+  the buffer to socket send buffer, then try to send it.\r
+\r
+  @param[in]  Sock              Pointer to the socket.\r
+  @param[in]  TcpTxData         Pointer to the application provided send buffer.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed due to resource limits.\r
+\r
+**/\r
+EFI_STATUS\r
+SockProcessTcpSndData (\r
+  IN SOCKET   *Sock,\r
+  IN VOID     *TcpTxData\r
+  )\r
+{\r
+  NET_BUF                 *SndData;\r
+  EFI_STATUS              Status;\r
+  EFI_TCP4_TRANSMIT_DATA  *TxData;\r
+\r
+  TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData;\r
+\r
+  //\r
+  // transform this TxData into a NET_BUFFER\r
+  // and insert it into Sock->SndBuffer\r
+  //\r
+  SndData = NetbufFromExt (\r
+              (NET_FRAGMENT *) TxData->FragmentTable,\r
+              TxData->FragmentCount,\r
+              0,\r
+              0,\r
+              SockFreeFoo,\r
+              NULL\r
+              );\r
+\r
+  if (NULL == SndData) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockKProcessSndData: Failed to call NetBufferFromExt\n")\r
+      );\r
+\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData);\r
+\r
+  //\r
+  // notify the low layer protocol to handle this send token\r
+  //\r
+  if (TxData->Urgent) {\r
+    Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  if (TxData->Push) {\r
+    Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  //\r
+  // low layer protocol should really handle the sending\r
+  // process when catching SOCK_SND request\r
+  //\r
+  Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Flush the tokens in the specific token list.\r
+\r
+  @param[in]       Sock                  Pointer to the socket.\r
+  @param[in, out]  PendingTokenList      Pointer to the token list to be flushed.\r
+\r
+**/\r
+VOID\r
+SockFlushPendingToken (\r
+  IN     SOCKET         *Sock,\r
+  IN OUT LIST_ENTRY     *PendingTokenList\r
+  )\r
+{\r
+  SOCK_TOKEN            *SockToken;\r
+  SOCK_COMPLETION_TOKEN *Token;\r
+\r
+  ASSERT ((Sock != NULL) && (PendingTokenList != NULL));\r
+\r
+  while (!IsListEmpty (PendingTokenList)) {\r
+    SockToken = NET_LIST_HEAD (\r
+                  PendingTokenList,\r
+                  SOCK_TOKEN,\r
+                  TokenList\r
+                  );\r
+\r
+    Token = SockToken->Token;\r
+    SIGNAL_TOKEN (Token, Sock->SockError);\r
+\r
+    RemoveEntryList (&(SockToken->TokenList));\r
+    FreePool (SockToken);\r
+  }\r
+}\r
+\r
+/**\r
+  Wake up the connection token while the connection is successfully established,\r
+  then try to process any pending send token.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockWakeConnToken (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  ASSERT (Sock->ConnectionToken != NULL);\r
+\r
+  SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS);\r
+  Sock->ConnectionToken = NULL;\r
+\r
+  //\r
+  // check to see if some pending send token existed?\r
+  //\r
+  SockProcessSndToken (Sock);\r
+}\r
+\r
+/**\r
+  Wake up the listen token while the connection is established successfully.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockWakeListenToken (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  SOCKET                *Parent;\r
+  SOCK_TOKEN            *SockToken;\r
+  EFI_TCP4_LISTEN_TOKEN *ListenToken;\r
+\r
+  Parent = Sock->Parent;\r
+\r
+  ASSERT ((Parent != NULL) && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock));\r
+\r
+  if (!IsListEmpty (&Parent->ListenTokenList)) {\r
+    SockToken = NET_LIST_HEAD (\r
+                  &Parent->ListenTokenList,\r
+                  SOCK_TOKEN,\r
+                  TokenList\r
+                  );\r
+\r
+    ListenToken                 = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token;\r
+    ListenToken->NewChildHandle = Sock->SockHandle;\r
+\r
+    SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);\r
+\r
+    RemoveEntryList (&SockToken->TokenList);\r
+    FreePool (SockToken);\r
+\r
+    RemoveEntryList (&Sock->ConnectionList);\r
+\r
+    Parent->ConnCnt--;\r
+    DEBUG (\r
+      (EFI_D_INFO,\r
+      "SockWakeListenToken: accept a socket, now conncnt is %d",\r
+      Parent->ConnCnt)\r
+      );\r
+\r
+    Sock->Parent = NULL;\r
+  }\r
+}\r
+\r
+/**\r
+  Wake up the receive token while some data is received.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockWakeRcvToken (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  UINT32        RcvdBytes;\r
+  UINT32        TokenRcvdBytes;\r
+  SOCK_TOKEN    *SockToken;\r
+  SOCK_IO_TOKEN *RcvToken;\r
+\r
+  ASSERT (Sock->RcvBuffer.DataQueue != NULL);\r
+\r
+  RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize;\r
+\r
+  ASSERT (RcvdBytes > 0);\r
+\r
+  while (RcvdBytes > 0 && !IsListEmpty (&Sock->RcvTokenList)) {\r
+\r
+    SockToken = NET_LIST_HEAD (\r
+                  &Sock->RcvTokenList,\r
+                  SOCK_TOKEN,\r
+                  TokenList\r
+                  );\r
+\r
+    RcvToken        = (SOCK_IO_TOKEN *) SockToken->Token;\r
+    TokenRcvdBytes  = SockProcessRcvToken (Sock, RcvToken);\r
+\r
+    if (0 == TokenRcvdBytes) {\r
+      return ;\r
+    }\r
+\r
+    RemoveEntryList (&(SockToken->TokenList));\r
+    FreePool (SockToken);\r
+    RcvdBytes -= TokenRcvdBytes;\r
+  }\r
+}\r
+\r
+/**\r
+  Create a socket with initial data SockInitData.\r
+\r
+  @param[in]  SockInitData          Pointer to the initial data of the socket.\r
+\r
+  @return Pointer to the newly created socket, return NULL when an exception occurs.\r
+\r
+**/\r
+SOCKET *\r
+SockCreate (\r
+  IN SOCK_INIT_DATA *SockInitData\r
+  )\r
+{\r
+  SOCKET      *Sock;\r
+  SOCKET      *Parent;\r
+  EFI_STATUS  Status;\r
+  EFI_GUID    *TcpProtocolGuid;\r
+  UINTN       ProtocolLength;\r
+\r
+  ASSERT ((SockInitData != NULL) && (SockInitData->ProtoHandler != NULL));\r
+  ASSERT (SockInitData->Type == SockStream);\r
+  ASSERT ((SockInitData->ProtoData != NULL) && (SockInitData->DataSize <= PROTO_RESERVED_LEN));\r
+\r
+  if (SockInitData->IpVersion == IP_VERSION_4) {\r
+    TcpProtocolGuid = &gEfiTcp4ProtocolGuid;\r
+    ProtocolLength  = sizeof (EFI_TCP4_PROTOCOL);\r
+  } else {\r
+    TcpProtocolGuid = &gEfiTcp6ProtocolGuid;\r
+    ProtocolLength  = sizeof (EFI_TCP6_PROTOCOL);\r
+  }\r
+\r
+\r
+  Parent = SockInitData->Parent;\r
+\r
+  if ((Parent != NULL) && (Parent->ConnCnt == Parent->BackLog)) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockCreate: Socket parent has reached its connection limit with %d ConnCnt and %d BackLog\n",\r
+      Parent->ConnCnt,\r
+      Parent->BackLog)\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+\r
+  Sock = AllocateZeroPool (sizeof (SOCKET));\r
+  if (NULL == Sock) {\r
+\r
+    DEBUG ((EFI_D_ERROR, "SockCreate: No resource to create a new socket\n"));\r
+    return NULL;\r
+  }\r
+\r
+  InitializeListHead (&Sock->Link);\r
+  InitializeListHead (&Sock->ConnectionList);\r
+  InitializeListHead (&Sock->ListenTokenList);\r
+  InitializeListHead (&Sock->RcvTokenList);\r
+  InitializeListHead (&Sock->SndTokenList);\r
+  InitializeListHead (&Sock->ProcessingSndTokenList);\r
+\r
+  EfiInitializeLock (&(Sock->Lock), TPL_CALLBACK);\r
+\r
+  Sock->SndBuffer.DataQueue = NetbufQueAlloc ();\r
+  if (NULL == Sock->SndBuffer.DataQueue) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockCreate: No resource to allocate SndBuffer for new socket\n")\r
+      );\r
+\r
+    goto OnError;\r
+  }\r
+\r
+  Sock->RcvBuffer.DataQueue = NetbufQueAlloc ();\r
+  if (NULL == Sock->RcvBuffer.DataQueue) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockCreate: No resource to allocate RcvBuffer for new socket\n")\r
+      );\r
+\r
+    goto OnError;\r
+  }\r
+\r
+  Sock->Signature           = SOCK_SIGNATURE;\r
+\r
+  Sock->Parent              = Parent;\r
+  Sock->BackLog             = SockInitData->BackLog;\r
+  Sock->ProtoHandler        = SockInitData->ProtoHandler;\r
+  Sock->SndBuffer.HighWater = SockInitData->SndBufferSize;\r
+  Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize;\r
+  Sock->Type                = SockInitData->Type;\r
+  Sock->DriverBinding       = SockInitData->DriverBinding;\r
+  Sock->State               = SockInitData->State;\r
+  Sock->CreateCallback      = SockInitData->CreateCallback;\r
+  Sock->DestroyCallback     = SockInitData->DestroyCallback;\r
+  Sock->Context             = SockInitData->Context;\r
+\r
+  Sock->SockError           = EFI_ABORTED;\r
+  Sock->SndBuffer.LowWater  = SOCK_BUFF_LOW_WATER;\r
+  Sock->RcvBuffer.LowWater  = SOCK_BUFF_LOW_WATER;\r
+\r
+  Sock->IpVersion           = SockInitData->IpVersion;\r
+\r
+  //\r
+  // Install protocol on Sock->SockHandle\r
+  //\r
+  CopyMem (&Sock->NetProtocol, SockInitData->Protocol, ProtocolLength);\r
+\r
+  //\r
+  // copy the protodata into socket\r
+  //\r
+  CopyMem (Sock->ProtoReserved, SockInitData->ProtoData, SockInitData->DataSize);\r
+\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Sock->SockHandle,\r
+                  TcpProtocolGuid,\r
+                  &Sock->NetProtocol,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockCreate: Install TCP protocol in socket failed with %r\n",\r
+      Status)\r
+      );\r
+\r
+    goto OnError;\r
+  }\r
+\r
+  if (Parent != NULL) {\r
+    ASSERT (Parent->BackLog > 0);\r
+    ASSERT (SOCK_IS_LISTENING (Parent));\r
+\r
+    //\r
+    // need to add it into Parent->ConnectionList\r
+    // if the Parent->ConnCnt < Parent->BackLog\r
+    //\r
+    Parent->ConnCnt++;\r
+\r
+    DEBUG (\r
+      (EFI_D_INFO,\r
+      "SockCreate: Create a new socket and add to parent, now conncnt is %d\n",\r
+      Parent->ConnCnt)\r
+      );\r
+\r
+    InsertTailList (&Parent->ConnectionList, &Sock->ConnectionList);\r
+  }\r
+\r
+  if (Sock->CreateCallback != NULL) {\r
+    Status = Sock->CreateCallback (Sock, Sock->Context);\r
+    if (EFI_ERROR (Status)) {\r
+      goto OnError;\r
+    }\r
+  }\r
+\r
+  return Sock;\r
+\r
+OnError:\r
+\r
+  if (Sock->SockHandle != NULL) {\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           Sock->SockHandle,\r
+           TcpProtocolGuid,\r
+           &Sock->NetProtocol,\r
+           NULL\r
+           );\r
+  }\r
+\r
+  if (NULL != Sock->SndBuffer.DataQueue) {\r
+    NetbufQueFree (Sock->SndBuffer.DataQueue);\r
+  }\r
+\r
+  if (NULL != Sock->RcvBuffer.DataQueue) {\r
+    NetbufQueFree (Sock->RcvBuffer.DataQueue);\r
+  }\r
+\r
+  FreePool (Sock);\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Destroy a socket.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockDestroy (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  VOID        *SockProtocol;\r
+  EFI_GUID    *TcpProtocolGuid;\r
+  EFI_STATUS  Status;\r
+\r
+  ASSERT (SockStream == Sock->Type);\r
+\r
+  if (Sock->DestroyCallback != NULL) {\r
+    Sock->DestroyCallback (Sock, Sock->Context);\r
+  }\r
+\r
+  //\r
+  // Flush the completion token buffered\r
+  // by sock and rcv, snd buffer\r
+  //\r
+  if (!SOCK_IS_UNCONFIGURED (Sock)) {\r
+\r
+    SockConnFlush (Sock);\r
+    SockSetState (Sock, SO_CLOSED);\r
+    Sock->ConfigureState = SO_UNCONFIGURED;\r
+\r
+  }\r
+  //\r
+  // Destory the RcvBuffer Queue and SendBuffer Queue\r
+  //\r
+  NetbufQueFree (Sock->RcvBuffer.DataQueue);\r
+  NetbufQueFree (Sock->SndBuffer.DataQueue);\r
+\r
+  //\r
+  // Remove it from parent connection list if needed\r
+  //\r
+  if (Sock->Parent != NULL) {\r
+\r
+    RemoveEntryList (&(Sock->ConnectionList));\r
+    (Sock->Parent->ConnCnt)--;\r
+\r
+    DEBUG (\r
+      (EFI_D_WARN,\r
+      "SockDestory: Delete a unaccepted socket from parent now conncnt is %d\n",\r
+      Sock->Parent->ConnCnt)\r
+      );\r
+\r
+    Sock->Parent = NULL;\r
+  }\r
+\r
+  //\r
+  // Set the protocol guid and driver binding handle\r
+  // in the light of Sock->SockType\r
+  //\r
+  if (Sock->IpVersion == IP_VERSION_4) {\r
+    TcpProtocolGuid = &gEfiTcp4ProtocolGuid;\r
+  } else {\r
+    TcpProtocolGuid = &gEfiTcp6ProtocolGuid;\r
+  }\r
+\r
+  //\r
+  // Retrieve the protocol installed on this sock\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Sock->SockHandle,\r
+                  TcpProtocolGuid,\r
+                  &SockProtocol,\r
+                  Sock->DriverBinding,\r
+                  Sock->SockHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockDestroy: Open protocol installed on socket failed with %r\n",\r
+      Status)\r
+      );\r
+\r
+    goto FreeSock;\r
+  }\r
+\r
+  //\r
+  // Uninstall the protocol installed on this sock\r
+  // in the light of Sock->SockType\r
+  //\r
+  gBS->UninstallMultipleProtocolInterfaces (\r
+        Sock->SockHandle,\r
+        TcpProtocolGuid,\r
+        SockProtocol,\r
+        NULL\r
+        );\r
+\r
+FreeSock:\r
+\r
+  FreePool (Sock);\r
+}\r
+\r
+/**\r
+  Flush the sndBuffer and rcvBuffer of socket.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockConnFlush (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  SOCKET  *Child;\r
+\r
+  ASSERT (Sock != NULL);\r
+\r
+  //\r
+  // Clear the flag in this socket\r
+  //\r
+  Sock->Flag = 0;\r
+\r
+  //\r
+  // Flush the SndBuffer and RcvBuffer of Sock\r
+  //\r
+  NetbufQueFlush (Sock->SndBuffer.DataQueue);\r
+  NetbufQueFlush (Sock->RcvBuffer.DataQueue);\r
+\r
+  //\r
+  // Signal the pending token\r
+  //\r
+  if (Sock->ConnectionToken != NULL) {\r
+    SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError);\r
+    Sock->ConnectionToken = NULL;\r
+  }\r
+\r
+  if (Sock->CloseToken != NULL) {\r
+    SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError);\r
+    Sock->CloseToken = NULL;\r
+  }\r
+\r
+  SockFlushPendingToken (Sock, &(Sock->ListenTokenList));\r
+  SockFlushPendingToken (Sock, &(Sock->RcvTokenList));\r
+  SockFlushPendingToken (Sock, &(Sock->SndTokenList));\r
+  SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList));\r
+\r
+  //\r
+  // Destroy the pending connection, if it is a listening socket\r
+  //\r
+  if (SOCK_IS_LISTENING (Sock)) {\r
+    while (!IsListEmpty (&Sock->ConnectionList)) {\r
+      Child = NET_LIST_HEAD (\r
+                &Sock->ConnectionList,\r
+                SOCKET,\r
+                ConnectionList\r
+                );\r
+\r
+      SockDestroyChild (Child);\r
+    }\r
+\r
+    Sock->ConnCnt = 0;\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Set the state of the socket.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+  @param[in]       State                 The new socket state to be set.\r
+\r
+**/\r
+VOID\r
+SockSetState (\r
+  IN OUT SOCKET     *Sock,\r
+  IN     UINT8      State\r
+  )\r
+{\r
+  Sock->State = State;\r
+}\r
+\r
+/**\r
+  Clone a new socket, including its associated protocol control block.\r
+\r
+  @param[in]  Sock                  Pointer to the socket to be cloned.\r
+\r
+  @return Pointer to the newly cloned socket. If NULL, an error condition occurred.\r
+\r
+**/\r
+SOCKET *\r
+SockClone (\r
+  IN SOCKET *Sock\r
+  )\r
+{\r
+  SOCKET          *ClonedSock;\r
+  SOCK_INIT_DATA  InitData;\r
+\r
+  InitData.BackLog         = Sock->BackLog;\r
+  InitData.Parent          = Sock;\r
+  InitData.State           = Sock->State;\r
+  InitData.ProtoHandler    = Sock->ProtoHandler;\r
+  InitData.Type            = Sock->Type;\r
+  InitData.RcvBufferSize   = Sock->RcvBuffer.HighWater;\r
+  InitData.SndBufferSize   = Sock->SndBuffer.HighWater;\r
+  InitData.DriverBinding   = Sock->DriverBinding;\r
+  InitData.IpVersion       = Sock->IpVersion;\r
+  InitData.Protocol        = &(Sock->NetProtocol);\r
+  InitData.CreateCallback  = Sock->CreateCallback;\r
+  InitData.DestroyCallback = Sock->DestroyCallback;\r
+  InitData.Context         = Sock->Context;\r
+  InitData.ProtoData       = Sock->ProtoReserved;\r
+  InitData.DataSize        = sizeof (Sock->ProtoReserved);\r
+\r
+  ClonedSock               = SockCreate (&InitData);\r
+\r
+  if (NULL == ClonedSock) {\r
+    DEBUG ((EFI_D_ERROR, "SockClone: no resource to create a cloned sock\n"));\r
+    return NULL;\r
+  }\r
+\r
+  SockSetState (ClonedSock, SO_CONNECTING);\r
+  ClonedSock->ConfigureState = Sock->ConfigureState;\r
+\r
+  return ClonedSock;\r
+}\r
+\r
+/**\r
+  Called by the low layer protocol to indicate the socket a connection is\r
+  established.\r
+\r
+  This function just changes the socket's state to SO_CONNECTED\r
+  and signals the token used for connection establishment.\r
+\r
+  @param[in, out]  Sock         Pointer to the socket associated with the\r
+                                established connection.\r
+\r
+**/\r
+VOID\r
+SockConnEstablished (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+\r
+  ASSERT (SO_CONNECTING == Sock->State);\r
+\r
+  SockSetState (Sock, SO_CONNECTED);\r
+\r
+  if (NULL == Sock->Parent) {\r
+    SockWakeConnToken (Sock);\r
+  } else {\r
+    SockWakeListenToken (Sock);\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Called by the low layer protocol to indicate the connection is closed.\r
+\r
+  This function flushes the socket, sets the state to SO_CLOSED, and signals\r
+  the close token.\r
+\r
+  @param[in, out]  Sock         Pointer to the socket associated with the closed\r
+                                connection.\r
+\r
+**/\r
+VOID\r
+SockConnClosed (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  if (Sock->CloseToken != NULL) {\r
+    SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS);\r
+    Sock->CloseToken = NULL;\r
+  }\r
+\r
+  SockConnFlush (Sock);\r
+  SockSetState (Sock, SO_CLOSED);\r
+\r
+  if (Sock->Parent != NULL) {\r
+    SockDestroyChild (Sock);\r
+  }\r
+\r
+}\r
+\r
+/**\r
+  Called by low layer protocol to indicate that some data was sent or processed.\r
+\r
+  This function trims the sent data in the socket send buffer, and signals the data\r
+  token if proper.\r
+\r
+  @param[in, out]  Sock      Pointer to the socket.\r
+  @param[in]       Count     The length of the data processed or sent, in bytes.\r
+\r
+**/\r
+VOID\r
+SockDataSent (\r
+  IN OUT SOCKET     *Sock,\r
+  IN     UINT32     Count\r
+  )\r
+{\r
+  SOCK_TOKEN            *SockToken;\r
+  SOCK_COMPLETION_TOKEN *SndToken;\r
+\r
+  ASSERT (!IsListEmpty (&Sock->ProcessingSndTokenList));\r
+  ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize);\r
+\r
+  NetbufQueTrim (Sock->SndBuffer.DataQueue, Count);\r
+\r
+  //\r
+  // To check if we can signal some snd token in this socket\r
+  //\r
+  while (Count > 0) {\r
+    SockToken = NET_LIST_HEAD (\r
+                  &(Sock->ProcessingSndTokenList),\r
+                  SOCK_TOKEN,\r
+                  TokenList\r
+                  );\r
+\r
+    SndToken = SockToken->Token;\r
+\r
+    if (SockToken->RemainDataLen <= Count) {\r
+\r
+      RemoveEntryList (&(SockToken->TokenList));\r
+      SIGNAL_TOKEN (SndToken, EFI_SUCCESS);\r
+      Count -= SockToken->RemainDataLen;\r
+      FreePool (SockToken);\r
+    } else {\r
+\r
+      SockToken->RemainDataLen -= Count;\r
+      Count = 0;\r
+    }\r
+  }\r
+\r
+  //\r
+  // to judge if we can process some send token in\r
+  // Sock->SndTokenList, if so process those send token\r
+  //\r
+  SockProcessSndToken (Sock);\r
+}\r
+\r
+/**\r
+  Called by the low layer protocol to copy some data in the socket send\r
+  buffer starting from the specific offset to a buffer provided by\r
+  the caller.\r
+\r
+  @param[in]  Sock                  Pointer to the socket.\r
+  @param[in]  Offset                The start point of the data to be copied.\r
+  @param[in]  Len                   The length of the data to be copied.\r
+  @param[out] Dest                  Pointer to the destination to copy the data.\r
+\r
+  @return The data size copied.\r
+\r
+**/\r
+UINT32\r
+SockGetDataToSend (\r
+  IN  SOCKET      *Sock,\r
+  IN  UINT32      Offset,\r
+  IN  UINT32      Len,\r
+  OUT UINT8       *Dest\r
+  )\r
+{\r
+  ASSERT ((Sock != NULL) && SockStream == Sock->Type);\r
+\r
+  return NetbufQueCopy (\r
+          Sock->SndBuffer.DataQueue,\r
+          Offset,\r
+          Len,\r
+          Dest\r
+          );\r
+}\r
+\r
+/**\r
+  Called by the low layer protocol to deliver received data to socket layer.\r
+\r
+  This function will append the data to the socket receive buffer, set the\r
+  urgent data length, and then check if any receive token can be signaled.\r
+\r
+  @param[in, out]  Sock       Pointer to the socket.\r
+  @param[in, out]  NetBuffer  Pointer to the buffer that contains the received data.\r
+  @param[in]       UrgLen     The length of the urgent data in the received data.\r
+\r
+**/\r
+VOID\r
+SockDataRcvd (\r
+  IN OUT SOCKET    *Sock,\r
+  IN OUT NET_BUF   *NetBuffer,\r
+  IN     UINT32    UrgLen\r
+  )\r
+{\r
+  ASSERT ((Sock != NULL) && (Sock->RcvBuffer.DataQueue != NULL) &&\r
+    UrgLen <= NetBuffer->TotalSize);\r
+\r
+  NET_GET_REF (NetBuffer);\r
+\r
+  ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen;\r
+\r
+  NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer);\r
+\r
+  SockWakeRcvToken (Sock);\r
+}\r
+\r
+/**\r
+  Get the length of the free space of the specific socket buffer.\r
+\r
+  @param[in]  Sock              Pointer to the socket.\r
+  @param[in]  Which             Flag to indicate which socket buffer to check:\r
+                                either send buffer or receive buffer.\r
+\r
+  @return The length of the free space, in bytes.\r
+\r
+**/\r
+UINT32\r
+SockGetFreeSpace (\r
+  IN SOCKET  *Sock,\r
+  IN UINT32  Which\r
+  )\r
+{\r
+  UINT32      BufferCC;\r
+  SOCK_BUFFER *SockBuffer;\r
+\r
+  ASSERT (Sock != NULL && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which)));\r
+\r
+  if (SOCK_SND_BUF == Which) {\r
+    SockBuffer = &(Sock->SndBuffer);\r
+  } else {\r
+    SockBuffer = &(Sock->RcvBuffer);\r
+  }\r
+\r
+  BufferCC = (SockBuffer->DataQueue)->BufSize;\r
+\r
+  if (BufferCC >= SockBuffer->HighWater) {\r
+\r
+    return 0;\r
+  }\r
+\r
+  return SockBuffer->HighWater - BufferCC;\r
+}\r
+\r
+/**\r
+  Called by the low layer protocol to indicate that there will be no more data\r
+  from the communication peer.\r
+\r
+  This function sets the socket's state to SO_NO_MORE_DATA and signals all queued\r
+  IO tokens with the error status EFI_CONNECTION_FIN.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockNoMoreData (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  EFI_STATUS  Err;\r
+\r
+  SOCK_NO_MORE_DATA (Sock);\r
+\r
+  if (!IsListEmpty (&Sock->RcvTokenList)) {\r
+\r
+    ASSERT (0 == GET_RCV_DATASIZE (Sock));\r
+\r
+    Err = Sock->SockError;\r
+\r
+    SOCK_ERROR (Sock, EFI_CONNECTION_FIN);\r
+\r
+    SockFlushPendingToken (Sock, &Sock->RcvTokenList);\r
+\r
+    SOCK_ERROR (Sock, Err);\r
+\r
+  }\r
+}\r
+\r
diff --git a/NetworkPkg/TcpDxe/SockImpl.h b/NetworkPkg/TcpDxe/SockImpl.h
new file mode 100644 (file)
index 0000000..bb4f6c2
--- /dev/null
@@ -0,0 +1,103 @@
+/** @file\r
+  The function declaration that provided for Socket Interface.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _SOCK_IMPL_H_\r
+#define _SOCK_IMPL_H_\r
+\r
+#include "Socket.h"\r
+\r
+/**\r
+  Signal a event with the given status.\r
+\r
+  @param[in] Token        The token's event is to be signaled.\r
+  @param[in] TokenStatus  The status to be sent with the event.\r
+\r
+**/\r
+#define SIGNAL_TOKEN(Token, TokenStatus) \\r
+  do { \\r
+    (Token)->Status = (TokenStatus); \\r
+    gBS->SignalEvent ((Token)->Event); \\r
+  } while (0)\r
+\r
+#define SOCK_HEADER_SPACE (60 + 60 + 72)\r
+\r
+/**\r
+  Process the TCP send data, buffer the tcp txdata and append\r
+  the buffer to socket send buffer, then try to send it.\r
+\r
+  @param[in]  Sock              Pointer to the socket.\r
+  @param[in]  TcpTxData         Pointer to the application provided send buffer.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed due to resource limits.\r
+\r
+**/\r
+EFI_STATUS\r
+SockProcessTcpSndData (\r
+  IN SOCKET   *Sock,\r
+  IN VOID     *TcpTxData\r
+  );\r
+\r
+/**\r
+  Get received data from the socket layer to the receive token.\r
+\r
+  @param[in, out]  Sock       Pointer to the socket.\r
+  @param[in, out]  RcvToken   Pointer to the application provided receive token.\r
+\r
+  @return The length of data received in this token.\r
+\r
+**/\r
+UINT32\r
+SockProcessRcvToken (\r
+  IN OUT SOCKET        *Sock,\r
+  IN OUT SOCK_IO_TOKEN *RcvToken\r
+  );\r
+\r
+/**\r
+  Flush the sndBuffer and rcvBuffer of socket.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockConnFlush (\r
+  IN OUT SOCKET *Sock\r
+  );\r
+\r
+/**\r
+  Create a socket with initial data SockInitData.\r
+\r
+  @param[in]  SockInitData          Pointer to the initial data of the socket.\r
+\r
+  @return Pointer to the newly created socket, return NULL when exception occured.\r
+\r
+**/\r
+SOCKET *\r
+SockCreate (\r
+  IN SOCK_INIT_DATA *SockInitData\r
+  );\r
+\r
+/**\r
+  Destroy a socket.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockDestroy (\r
+  IN OUT SOCKET *Sock\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/TcpDxe/SockInterface.c b/NetworkPkg/TcpDxe/SockInterface.c
new file mode 100644 (file)
index 0000000..e36c0e9
--- /dev/null
@@ -0,0 +1,999 @@
+/** @file\r
+  Interface function of the Socket.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "SockImpl.h"\r
+\r
+/**\r
+  Check whether the Event is in the List.\r
+\r
+  @param[in]  List             Pointer to the token list to be searched.\r
+  @param[in]  Event            The event to be checked.\r
+\r
+  @retval  TRUE                The specific Event exists in the List.\r
+  @retval  FALSE               The specific Event is not in the List.\r
+\r
+**/\r
+BOOLEAN\r
+SockTokenExistedInList (\r
+  IN LIST_ENTRY     *List,\r
+  IN EFI_EVENT      Event\r
+  )\r
+{\r
+  LIST_ENTRY      *ListEntry;\r
+  SOCK_TOKEN      *SockToken;\r
+\r
+  NET_LIST_FOR_EACH (ListEntry, List) {\r
+    SockToken = NET_LIST_USER_STRUCT (\r
+                  ListEntry,\r
+                  SOCK_TOKEN,\r
+                  TokenList\r
+                  );\r
+\r
+    if (Event == SockToken->Token->Event) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Call SockTokenExistedInList() to check whether the Event is\r
+  in the related socket's lists.\r
+\r
+  @param[in]  Sock             Pointer to the instance's socket.\r
+  @param[in]  Event            The event to be checked.\r
+\r
+  @retval  TRUE                The Event exists in related socket's lists.\r
+  @retval  FALSE               The Event is not in related socket's lists.\r
+\r
+**/\r
+BOOLEAN\r
+SockTokenExisted (\r
+  IN SOCKET    *Sock,\r
+  IN EFI_EVENT Event\r
+  )\r
+{\r
+\r
+  if (SockTokenExistedInList (&Sock->SndTokenList, Event) ||\r
+      SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) ||\r
+      SockTokenExistedInList (&Sock->RcvTokenList, Event) ||\r
+      SockTokenExistedInList (&Sock->ListenTokenList, Event)\r
+        ) {\r
+\r
+    return TRUE;\r
+  }\r
+\r
+  if ((Sock->ConnectionToken != NULL) && (Sock->ConnectionToken->Event == Event)) {\r
+\r
+    return TRUE;\r
+  }\r
+\r
+  if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Buffer a token into the specific list of the socket Sock.\r
+\r
+  @param[in]  Sock             Pointer to the instance's socket.\r
+  @param[in]  List             Pointer to the list to store the token.\r
+  @param[in]  Token            Pointer to the token to be buffered.\r
+  @param[in]  DataLen          The data length of the buffer contained in Token.\r
+\r
+  @return Pointer to the token that wraps Token. If NULL, an error condition occurred.\r
+\r
+**/\r
+SOCK_TOKEN *\r
+SockBufferToken (\r
+  IN SOCKET         *Sock,\r
+  IN LIST_ENTRY     *List,\r
+  IN VOID           *Token,\r
+  IN UINT32         DataLen\r
+  )\r
+{\r
+  SOCK_TOKEN  *SockToken;\r
+\r
+  SockToken = AllocateZeroPool (sizeof (SOCK_TOKEN));\r
+  if (NULL == SockToken) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockBufferIOToken: No Memory to allocate SockToken\n")\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+\r
+  SockToken->Sock           = Sock;\r
+  SockToken->Token          = (SOCK_COMPLETION_TOKEN *) Token;\r
+  SockToken->RemainDataLen  = DataLen;\r
+  InsertTailList (List, &SockToken->TokenList);\r
+\r
+  return SockToken;\r
+}\r
+\r
+/**\r
+  Destory the socket Sock and its associated protocol control block.\r
+\r
+  @param[in, out]  Sock                 The socket to be destroyed.\r
+\r
+  @retval EFI_SUCCESS          The socket Sock was destroyed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.\r
+\r
+**/\r
+EFI_STATUS\r
+SockDestroyChild (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL));\r
+\r
+  if (Sock->IsDestroyed) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Sock->IsDestroyed = TRUE;\r
+\r
+  Status            = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockDestroyChild: Get the lock to access socket failed with %r\n",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // force protocol layer to detach the PCB\r
+  //\r
+  Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockDestroyChild: Protocol detach socket failed with %r\n",\r
+      Status)\r
+      );\r
+\r
+    Sock->IsDestroyed = FALSE;\r
+  } else if (SOCK_IS_CONFIGURED (Sock)) {\r
+\r
+    SockConnFlush (Sock);\r
+    SockSetState (Sock, SO_CLOSED);\r
+\r
+    Sock->ConfigureState = SO_UNCONFIGURED;\r
+  }\r
+\r
+  EfiReleaseLock (&(Sock->Lock));\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  SockDestroy (Sock);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Create a socket and its associated protocol control block\r
+  with the intial data SockInitData and protocol specific\r
+  data ProtoData.\r
+\r
+  @param[in]  SockInitData         Inital data to setting the socket.\r
+\r
+  @return Pointer to the newly created socket. If NULL, an error condition occured.\r
+\r
+**/\r
+SOCKET *\r
+SockCreateChild (\r
+  IN SOCK_INIT_DATA *SockInitData\r
+  )\r
+{\r
+  SOCKET      *Sock;\r
+  EFI_STATUS  Status;\r
+\r
+  //\r
+  // create a new socket\r
+  //\r
+  Sock = SockCreate (SockInitData);\r
+  if (NULL == Sock) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockCreateChild: No resource to create a new socket\n")\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockCreateChild: Get the lock to access socket failed with %r\n",\r
+      Status)\r
+      );\r
+\r
+    SockDestroy (Sock);\r
+    return NULL;\r
+  }\r
+  //\r
+  // inform the protocol layer to attach the socket\r
+  // with a new protocol control block\r
+  //\r
+  Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockCreateChild: Protocol failed to attach a socket with %r\n",\r
+      Status)\r
+      );\r
+\r
+    SockDestroy (Sock);\r
+    Sock = NULL;\r
+  }\r
+\r
+  EfiReleaseLock (&(Sock->Lock));\r
+  return Sock;\r
+}\r
+\r
+/**\r
+  Configure the specific socket Sock using configuration data ConfigData.\r
+\r
+  @param[in]  Sock             Pointer to the socket to be configured.\r
+  @param[in]  ConfigData       Pointer to the configuration data.\r
+\r
+  @retval EFI_SUCCESS          The socket configured successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is already configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockConfigure (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *ConfigData\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockConfigure: Get the access for socket failed with %r",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (SOCK_IS_CONFIGURED (Sock)) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto OnExit;\r
+  }\r
+\r
+  ASSERT (Sock->State == SO_CLOSED);\r
+\r
+  Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData);\r
+\r
+OnExit:\r
+  EfiReleaseLock (&(Sock->Lock));\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Initiate a connection establishment process.\r
+\r
+  @param[in]  Sock             Pointer to the socket to initiate the initate the\r
+                               connection.\r
+  @param[in]  Token            Pointer to the token used for the connection\r
+                               operation.\r
+\r
+  @retval EFI_SUCCESS          The connection initialized successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not configured to\r
+                               be an active one, or the token is already in one of\r
+                               this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockConnect (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *Token\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  EFI_EVENT   Event;\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockConnect: Get the access for socket failed with %r",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (SOCK_IS_NO_MAPPING (Sock)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto OnExit;\r
+  }\r
+\r
+  if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+\r
+    Status = EFI_NOT_STARTED;\r
+    goto OnExit;\r
+  }\r
+\r
+  if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) {\r
+\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto OnExit;\r
+  }\r
+\r
+  Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;\r
+\r
+  if (SockTokenExisted (Sock, Event)) {\r
+\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto OnExit;\r
+  }\r
+\r
+  Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token;\r
+  SockSetState (Sock, SO_CONNECTING);\r
+  Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL);\r
+\r
+OnExit:\r
+  EfiReleaseLock (&(Sock->Lock));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Issue a listen token to get an existed connected network instance\r
+  or wait for a connection if there is none.\r
+\r
+  @param[in]  Sock             Pointer to the socket to accept connections.\r
+  @param[in]  Token            The token to accept a connection.\r
+\r
+  @retval EFI_SUCCESS          Either a connection is accpeted or the Token is\r
+                               buffered for further acception.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not configured to\r
+                               be a passive one, or the token is already in one of\r
+                               this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the Token due to memory limits.\r
+\r
+**/\r
+EFI_STATUS\r
+SockAccept (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *Token\r
+  )\r
+{\r
+  EFI_TCP4_LISTEN_TOKEN *ListenToken;\r
+  LIST_ENTRY            *ListEntry;\r
+  EFI_STATUS            Status;\r
+  SOCKET                *Socket;\r
+  EFI_EVENT             Event;\r
+\r
+  ASSERT (SockStream == Sock->Type);\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockAccept: Get the access for socket failed with %r",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (SOCK_IS_NO_MAPPING (Sock)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto Exit;\r
+  }\r
+\r
+  if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  if (!SOCK_IS_LISTENING (Sock)) {\r
+\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;\r
+\r
+  if (SockTokenExisted (Sock, Event)) {\r
+\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token;\r
+\r
+  //\r
+  // Check if a connection has already in this Sock->ConnectionList\r
+  //\r
+  NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) {\r
+\r
+    Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList);\r
+\r
+    if (SOCK_IS_CONNECTED (Socket)) {\r
+      ListenToken->NewChildHandle = Socket->SockHandle;\r
+      SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);\r
+\r
+      RemoveEntryList (ListEntry);\r
+\r
+      ASSERT (Socket->Parent != NULL);\r
+\r
+      Socket->Parent->ConnCnt--;\r
+\r
+      DEBUG (\r
+        (EFI_D_INFO,\r
+        "SockAccept: Accept a socket, now conncount is %d",\r
+        Socket->Parent->ConnCnt)\r
+        );\r
+      Socket->Parent = NULL;\r
+\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Buffer this token for latter incoming connection request\r
+  //\r
+  if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) {\r
+\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+Exit:\r
+  EfiReleaseLock (&(Sock->Lock));\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Issue a token with data to the socket to send out.\r
+\r
+  @param[in]  Sock             Pointer to the socket to process the token with\r
+                               data.\r
+  @param[in]  Token            The token with data that needs to send out.\r
+\r
+  @retval EFI_SUCCESS          The token processed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not in a\r
+                               synchronized state , or the token is already in one\r
+                               of this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the token due to memory limits.\r
+\r
+**/\r
+EFI_STATUS\r
+SockSend (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *Token\r
+  )\r
+{\r
+  SOCK_IO_TOKEN           *SndToken;\r
+  EFI_EVENT               Event;\r
+  UINT32                  FreeSpace;\r
+  EFI_TCP4_TRANSMIT_DATA  *TxData;\r
+  EFI_STATUS              Status;\r
+  SOCK_TOKEN              *SockToken;\r
+  UINT32                  DataLen;\r
+\r
+  ASSERT (SockStream == Sock->Type);\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockSend: Get the access for socket failed with %r",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (SOCK_IS_NO_MAPPING (Sock)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto Exit;\r
+  }\r
+\r
+  SndToken  = (SOCK_IO_TOKEN *) Token;\r
+  TxData    = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData;\r
+\r
+  if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) {\r
+\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // check if a token is already in the token buffer\r
+  //\r
+  Event = SndToken->Token.Event;\r
+\r
+  if (SockTokenExisted (Sock, Event)) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  DataLen = TxData->DataLength;\r
+\r
+  //\r
+  // process this sending token now or buffer it only?\r
+  //\r
+  FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);\r
+\r
+  if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) {\r
+\r
+    SockToken = SockBufferToken (\r
+                  Sock,\r
+                  &Sock->SndTokenList,\r
+                  SndToken,\r
+                  DataLen\r
+                  );\r
+\r
+    if (NULL == SockToken) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+    }\r
+  } else {\r
+\r
+    SockToken = SockBufferToken (\r
+                  Sock,\r
+                  &Sock->ProcessingSndTokenList,\r
+                  SndToken,\r
+                  DataLen\r
+                  );\r
+\r
+    if (NULL == SockToken) {\r
+      DEBUG (\r
+        (EFI_D_ERROR,\r
+        "SockSend: Failed to buffer IO token into socket processing SndToken List\n",\r
+        Status)\r
+        );\r
+\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto Exit;\r
+    }\r
+\r
+    Status = SockProcessTcpSndData (Sock, TxData);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG (\r
+        (EFI_D_ERROR,\r
+        "SockSend: Failed to process Snd Data\n",\r
+        Status)\r
+        );\r
+\r
+      RemoveEntryList (&(SockToken->TokenList));\r
+      FreePool (SockToken);\r
+    }\r
+  }\r
+\r
+Exit:\r
+  EfiReleaseLock (&(Sock->Lock));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Issue a token to get data from the socket.\r
+\r
+  @param[in]  Sock             Pointer to the socket to get data from.\r
+  @param[in]  Token            The token to store the received data from the\r
+                               socket.\r
+\r
+  @retval EFI_SUCCESS          The token processed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not in a\r
+                               synchronized state , or the token is already in one\r
+                               of this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+  @retval EFI_CONNECTION_FIN   The connection is closed and there is no more data.\r
+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the token due to memory limit.\r
+\r
+**/\r
+EFI_STATUS\r
+SockRcv (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *Token\r
+  )\r
+{\r
+  SOCK_IO_TOKEN *RcvToken;\r
+  UINT32        RcvdBytes;\r
+  EFI_STATUS    Status;\r
+  EFI_EVENT     Event;\r
+\r
+  ASSERT (SockStream == Sock->Type);\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockRcv: Get the access for socket failed with %r",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (SOCK_IS_NO_MAPPING (Sock)) {\r
+\r
+    Status = EFI_NO_MAPPING;\r
+    goto Exit;\r
+  }\r
+\r
+  if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) {\r
+\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  RcvToken = (SOCK_IO_TOKEN *) Token;\r
+\r
+  //\r
+  // check if a token is already in the token buffer of this socket\r
+  //\r
+  Event = RcvToken->Token.Event;\r
+  if (SockTokenExisted (Sock, Event)) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  RcvToken  = (SOCK_IO_TOKEN *) Token;\r
+  RcvdBytes = GET_RCV_DATASIZE (Sock);\r
+\r
+  //\r
+  // check whether an error has happened before\r
+  //\r
+  if (EFI_ABORTED != Sock->SockError) {\r
+\r
+    SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError);\r
+    Sock->SockError = EFI_ABORTED;\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // check whether can not receive and there is no any\r
+  // data buffered in Sock->RcvBuffer\r
+  //\r
+  if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) {\r
+\r
+    Status = EFI_CONNECTION_FIN;\r
+    goto Exit;\r
+  }\r
+\r
+  if (RcvdBytes != 0) {\r
+    Status = SockProcessRcvToken (Sock, RcvToken);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL);\r
+  } else {\r
+\r
+    if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+Exit:\r
+  EfiReleaseLock (&(Sock->Lock));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Reset the socket and its associated protocol control block.\r
+\r
+  @param[in, out]  Sock        Pointer to the socket to be flushed.\r
+\r
+  @retval EFI_SUCCESS          The socket is flushed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.\r
+\r
+**/\r
+EFI_STATUS\r
+SockFlush (\r
+  IN OUT SOCKET *Sock\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  ASSERT (SockStream == Sock->Type);\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockFlush: Get the access for socket failed with %r",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (!SOCK_IS_CONFIGURED (Sock)) {\r
+\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockFlush: Protocol failed handling SOCK_FLUSH with %r",\r
+      Status)\r
+      );\r
+\r
+    goto Exit;\r
+  }\r
+\r
+  SOCK_ERROR (Sock, EFI_ABORTED);\r
+  SockConnFlush (Sock);\r
+  SockSetState (Sock, SO_CLOSED);\r
+\r
+  Sock->ConfigureState = SO_UNCONFIGURED;\r
+\r
+Exit:\r
+  EfiReleaseLock (&(Sock->Lock));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Close or abort the socket associated connection.\r
+\r
+  @param[in, out]  Sock        Pointer to the socket of the connection to close\r
+                               or abort.\r
+  @param[in]  Token            The token for a close operation.\r
+  @param[in]  OnAbort          TRUE for aborting the connection; FALSE to close it.\r
+\r
+  @retval EFI_SUCCESS          The close or abort operation initialized\r
+                               successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not in a\r
+                               synchronized state , or the token is already in one\r
+                               of this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockClose (\r
+  IN OUT SOCKET  *Sock,\r
+  IN     VOID    *Token,\r
+  IN     BOOLEAN OnAbort\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  EFI_EVENT   Event;\r
+\r
+  ASSERT (SockStream == Sock->Type);\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockClose: Get the access for socket failed with %r",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (SOCK_IS_NO_MAPPING (Sock)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto Exit;\r
+  }\r
+\r
+  if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  if (SOCK_IS_DISCONNECTING (Sock)) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;\r
+\r
+  if (SockTokenExisted (Sock, Event)) {\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto Exit;\r
+  }\r
+\r
+  Sock->CloseToken = Token;\r
+  SockSetState (Sock, SO_DISCONNECTING);\r
+\r
+  if (OnAbort) {\r
+    Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL);\r
+  } else {\r
+    Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL);\r
+  }\r
+\r
+Exit:\r
+  EfiReleaseLock (&(Sock->Lock));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get the mode data of the low layer protocol.\r
+\r
+  @param[in]       Sock        Pointer to the socket to get mode data from.\r
+  @param[in, out]  Mode        Pointer to the data to store the low layer mode\r
+                               information.\r
+\r
+  @retval EFI_SUCCESS          The mode data was obtained successfully.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockGetMode (\r
+  IN     SOCKET *Sock,\r
+  IN OUT VOID   *Mode\r
+  )\r
+{\r
+  return Sock->ProtoHandler (Sock, SOCK_MODE, Mode);\r
+}\r
+\r
+/**\r
+  Configure the low level protocol to join a multicast group for\r
+  this socket's connection.\r
+\r
+  @param[in]  Sock             Pointer to the socket of the connection to join the\r
+                               specific multicast group.\r
+  @param[in]  GroupInfo        Pointer to the multicast group info.\r
+\r
+  @retval EFI_SUCCESS          The configuration completed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockGroup (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *GroupInfo\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+\r
+  if (EFI_ERROR (Status)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockGroup: Get the access for socket failed with %r",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo);\r
+\r
+Exit:\r
+  EfiReleaseLock (&(Sock->Lock));\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Add or remove route information in IP route table associated\r
+  with this socket.\r
+\r
+  @param[in]  Sock             Pointer to the socket associated with the IP route\r
+                               table to operate on.\r
+  @param[in]  RouteInfo        Pointer to the route information to be processed.\r
+\r
+  @retval EFI_SUCCESS          The route table updated successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is  not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockRoute (\r
+  IN SOCKET    *Sock,\r
+  IN VOID      *RouteInfo\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "SockRoute: Get the access for socket failed with %r",\r
+      Status)\r
+      );\r
+\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  if (SOCK_IS_NO_MAPPING (Sock)) {\r
+    Status = EFI_NO_MAPPING;\r
+    goto Exit;\r
+  }\r
+\r
+  if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  }\r
+\r
+  Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo);\r
+\r
+Exit:\r
+  EfiReleaseLock (&(Sock->Lock));\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/TcpDxe/Socket.h b/NetworkPkg/TcpDxe/Socket.h
new file mode 100644 (file)
index 0000000..a006252
--- /dev/null
@@ -0,0 +1,924 @@
+/** @file\r
+  Common head file for TCP socket.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _SOCKET_H_\r
+#define _SOCKET_H_\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Tcp4.h>\r
+#include <Protocol/Tcp6.h>\r
+\r
+#include <Library/NetLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/DpcLib.h>\r
+\r
+#define SOCK_SND_BUF        0\r
+#define SOCK_RCV_BUF        1\r
+\r
+#define SOCK_BUFF_LOW_WATER (2 * 1024)\r
+#define SOCK_RCV_BUFF_SIZE  (8 * 1024)\r
+#define SOCK_SND_BUFF_SIZE  (8 * 1024)\r
+#define SOCK_BACKLOG        5\r
+\r
+#define PROTO_RESERVED_LEN  20\r
+\r
+#define SO_NO_MORE_DATA     0x0001\r
+\r
+//\r
+//\r
+//\r
+// When a socket is created it enters into SO_UNCONFIGURED,\r
+// no actions can be taken on this socket, only after calling\r
+// SockConfigure. The state transition diagram of socket is\r
+// as following:\r
+//\r
+// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING\r
+//  ^      |                                    |\r
+//  |      --->  SO_LISTENING                   |\r
+//  |                                           |\r
+//  |------------------SO_DISCONNECTING<-- SO_CONNECTED\r
+//\r
+// A passive socket can only go into SO_LISTENING and\r
+// SO_UNCONFIGURED state. SO_XXXING state is a middle state\r
+// when a socket is undergoing a protocol procedure such\r
+// as requesting a TCP connection.\r
+//\r
+//\r
+//\r
+\r
+///\r
+/// Socket state\r
+///\r
+#define SO_CLOSED        0\r
+#define SO_LISTENING     1\r
+#define SO_CONNECTING    2\r
+#define SO_CONNECTED     3\r
+#define SO_DISCONNECTING 4\r
+\r
+///\r
+/// Socket configure state\r
+///\r
+#define SO_UNCONFIGURED        0\r
+#define SO_CONFIGURED_ACTIVE   1\r
+#define SO_CONFIGURED_PASSIVE  2\r
+#define SO_NO_MAPPING          3\r
+\r
+///\r
+///  The request issued from socket layer to protocol layer.\r
+///\r
+#define SOCK_ATTACH     0    ///< Attach current socket to a new PCB\r
+#define SOCK_DETACH     1    ///< Detach current socket from the PCB\r
+#define SOCK_CONFIGURE  2    ///< Configure attached PCB\r
+#define SOCK_FLUSH      3    ///< Flush attached PCB\r
+#define SOCK_SND        4    ///< Need protocol to send something\r
+#define SOCK_SNDPUSH    5    ///< Need protocol to send pushed data\r
+#define SOCK_SNDURG     6    ///< Need protocol to send urgent data\r
+#define SOCK_CONSUMED   7    ///< Application has retrieved data from socket\r
+#define SOCK_CONNECT    8    ///< Need to connect to a peer\r
+#define SOCK_CLOSE      9    ///< Need to close the protocol process\r
+#define SOCK_ABORT      10   ///< Need to reset the protocol process\r
+#define SOCK_POLL       11   ///< Need to poll to the protocol layer\r
+#define SOCK_ROUTE      12   ///< Need to add a route information\r
+#define SOCK_MODE       13   ///< Need to get the mode data of the protocol\r
+#define SOCK_GROUP      14   ///< Need to join a mcast group\r
+\r
+/**\r
+  Set socket SO_NO_MORE_DATA flag.\r
+\r
+  @param[in] Sock            Pointer to the socket\r
+\r
+**/\r
+#define SOCK_NO_MORE_DATA(Sock)     ((Sock)->Flag |= SO_NO_MORE_DATA)\r
+\r
+/**\r
+  Check whether the socket is unconfigured.\r
+\r
+  @param[in]  Sock           Pointer to the socket.\r
+\r
+  @retval TRUE               The socket is unconfigued.\r
+  @retval FALSE              The socket is not unconfigued.\r
+\r
+**/\r
+#define SOCK_IS_UNCONFIGURED(Sock)  ((Sock)->ConfigureState == SO_UNCONFIGURED)\r
+\r
+/**\r
+  Check whether the socket is configured.\r
+\r
+  @param[in] Sock            Pointer to the socket\r
+\r
+  @retval TRUE               The socket is configued\r
+  @retval FALSE              The socket is not configued\r
+\r
+**/\r
+#define SOCK_IS_CONFIGURED(Sock) \\r
+    (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \\r
+    ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE))\r
+\r
+/**\r
+  Check whether the socket is configured to active mode.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @retval TRUE               The socket is configued to active mode.\r
+  @retval FALSE              The socket is not configued to active mode.\r
+\r
+**/\r
+#define SOCK_IS_CONFIGURED_ACTIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE)\r
+\r
+/**\r
+  Check whether the socket is configured to passive mode.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @retval TRUE               The socket is configued to passive mode.\r
+  @retval FALSE              The socket is not configued to passive mode.\r
+\r
+**/\r
+#define SOCK_IS_CONNECTED_PASSIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)\r
+\r
+/**\r
+  Check whether the socket is mapped.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @retval TRUE               The socket is not mapping.\r
+  @retval FALSE              The socket is mapped.\r
+\r
+**/\r
+#define SOCK_IS_NO_MAPPING(Sock)  ((Sock)->ConfigureState == SO_NO_MAPPING)\r
+\r
+/**\r
+  Check whether the socket is closed.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @retval TRUE               The socket is closed.\r
+  @retval FALSE              The socket is not closed.\r
+\r
+**/\r
+#define SOCK_IS_CLOSED(Sock)          ((Sock)->State == SO_CLOSED)\r
+\r
+/**\r
+  Check whether the socket is listening.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @retval TRUE               The socket is listening.\r
+  @retval FALSE              The socket is not listening.\r
+\r
+**/\r
+#define SOCK_IS_LISTENING(Sock)       ((Sock)->State == SO_LISTENING)\r
+\r
+/**\r
+  Check whether the socket is connecting.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @retval TRUE               The socket is connecting.\r
+  @retval FALSE              The socket is not connecting.\r
+\r
+**/\r
+#define SOCK_IS_CONNECTING(Sock)      ((Sock)->State == SO_CONNECTING)\r
+\r
+/**\r
+  Check whether the socket has connected.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @retval TRUE               The socket has connected.\r
+  @retval FALSE              The socket has not connected.\r
+\r
+**/\r
+#define SOCK_IS_CONNECTED(Sock)       ((Sock)->State == SO_CONNECTED)\r
+\r
+/**\r
+  Check whether the socket is disconnecting.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @retval TRUE               The socket is disconnecting.\r
+  @retval FALSE              The socket is not disconnecting.\r
+\r
+**/\r
+#define SOCK_IS_DISCONNECTING(Sock)   ((Sock)->State == SO_DISCONNECTING)\r
+\r
+/**\r
+  Check whether the socket is no more data.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @retval TRUE               The socket is no more data.\r
+  @retval FALSE              The socket still has data.\r
+\r
+**/\r
+#define SOCK_IS_NO_MORE_DATA(Sock)    (0 != ((Sock)->Flag & SO_NO_MORE_DATA))\r
+\r
+/**\r
+  Set the size of the receive buffer.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+  @param[in] Size            The size to set.\r
+\r
+**/\r
+#define SET_RCV_BUFFSIZE(Sock, Size)  ((Sock)->RcvBuffer.HighWater = (Size))\r
+\r
+/**\r
+  Get the size of the receive buffer.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @return The receive buffer size.\r
+\r
+**/\r
+#define GET_RCV_BUFFSIZE(Sock)        ((Sock)->RcvBuffer.HighWater)\r
+\r
+/**\r
+  Get the size of the receive data.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @return The received data size.\r
+\r
+**/\r
+#define GET_RCV_DATASIZE(Sock)        (((Sock)->RcvBuffer.DataQueue)->BufSize)\r
+\r
+/**\r
+  Set the size of the send buffer.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+  @param[in] Size            The size to set.\r
+\r
+**/\r
+#define SET_SND_BUFFSIZE(Sock, Size)  ((Sock)->SndBuffer.HighWater = (Size))\r
+\r
+/**\r
+  Get the size of the send buffer.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @return The send buffer size.\r
+\r
+**/\r
+#define GET_SND_BUFFSIZE(Sock)        ((Sock)->SndBuffer.HighWater)\r
+\r
+/**\r
+  Get the size of the send data.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @return The send data size.\r
+\r
+**/\r
+#define GET_SND_DATASIZE(Sock)        (((Sock)->SndBuffer.DataQueue)->BufSize)\r
+\r
+/**\r
+  Set the backlog value of the socket.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+  @param[in] Value           The value to set.\r
+\r
+**/\r
+#define SET_BACKLOG(Sock, Value)      ((Sock)->BackLog = (Value))\r
+\r
+/**\r
+  Get the backlog value of the socket.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+\r
+  @return The backlog value.\r
+\r
+**/\r
+#define GET_BACKLOG(Sock)             ((Sock)->BackLog)\r
+\r
+/**\r
+  Set the socket with error state.\r
+\r
+  @param[in] Sock            Pointer to the socket.\r
+  @param[in] Error           The error state.\r
+\r
+**/\r
+#define SOCK_ERROR(Sock, Error)       ((Sock)->SockError = (Error))\r
+\r
+#define SOCK_SIGNATURE                SIGNATURE_32 ('S', 'O', 'C', 'K')\r
+\r
+#define SOCK_FROM_THIS(a)             CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE)\r
+\r
+#define SOCK_FROM_TOKEN(Token)        (((SOCK_TOKEN *) (Token))->Sock)\r
+\r
+#define PROTO_TOKEN_FORM_SOCK(SockToken, Type)  ((Type *) (((SOCK_TOKEN *) (SockToken))->Token))\r
+\r
+typedef struct _TCP_SOCKET SOCKET;\r
+\r
+///\r
+/// Socket completion token\r
+///\r
+typedef struct _SOCK_COMPLETION_TOKEN {\r
+  EFI_EVENT   Event;            ///< The event to be issued\r
+  EFI_STATUS  Status;           ///< The status to be issued\r
+} SOCK_COMPLETION_TOKEN;\r
+\r
+typedef union {\r
+  VOID  *RxData;\r
+  VOID  *TxData;\r
+} SOCK_IO_DATA;\r
+\r
+///\r
+/// The application token with data packet\r
+///\r
+typedef struct _SOCK_IO_TOKEN {\r
+  SOCK_COMPLETION_TOKEN Token;\r
+  SOCK_IO_DATA          Packet;\r
+} SOCK_IO_TOKEN;\r
+\r
+///\r
+///  The socket type.\r
+///\r
+typedef enum {\r
+  SockDgram, ///< This socket providing datagram service\r
+  SockStream ///< This socket providing stream service\r
+} SOCK_TYPE;\r
+\r
+///\r
+///  The buffer structure of rcvd data and send data used by socket.\r
+///\r
+typedef struct _SOCK_BUFFER {\r
+  UINT32        HighWater;  ///< The buffersize upper limit of sock_buffer\r
+  UINT32        LowWater;   ///< The low warter mark of sock_buffer\r
+  NET_BUF_QUEUE *DataQueue; ///< The queue to buffer data\r
+} SOCK_BUFFER;\r
+\r
+/**\r
+  The handler of protocol for request from socket.\r
+\r
+  @param[in] Socket          The socket issuing the request to protocol.\r
+  @param[in] Request         The request issued by socket.\r
+  @param[in] RequestData     The request related data.\r
+\r
+  @retval EFI_SUCCESS        The socket request is completed successfully.\r
+  @retval other              The error status returned by the corresponding TCP\r
+                             layer function.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*SOCK_PROTO_HANDLER) (\r
+  IN SOCKET       *Socket,\r
+  IN UINT8        Request,\r
+  IN VOID         *RequestData\r
+  );\r
+\r
+/**\r
+  The Callback funtion called after the TCP socket is created.\r
+\r
+  @param[in]  This            Pointer to the socket just created.\r
+  @param[in]  Context         Context of the socket.\r
+\r
+  @retval EFI_SUCCESS         This protocol installed successfully.\r
+  @retval other               Some error occured.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*SOCK_CREATE_CALLBACK) (\r
+  IN SOCKET  *This,\r
+  IN VOID    *Context\r
+  );\r
+\r
+/**\r
+  The callback function called before the TCP socket is to be destroyed.\r
+\r
+  @param[in]  This                   The TCP socket to be destroyed.\r
+  @param[in]  Context                The context.\r
+\r
+**/\r
+typedef\r
+VOID\r
+(*SOCK_DESTROY_CALLBACK) (\r
+  IN SOCKET  *This,\r
+  IN VOID    *Context\r
+  );\r
+\r
+///\r
+///  The initialize data for create a new socket.\r
+///\r
+typedef struct _SOCK_INIT_DATA {\r
+  SOCK_TYPE              Type;\r
+  UINT8                  State;\r
+\r
+  SOCKET                 *Parent;        ///< The parent of this socket\r
+  UINT32                 BackLog;        ///< The connection limit for listening socket\r
+  UINT32                 SndBufferSize;  ///< The high warter mark of send buffer\r
+  UINT32                 RcvBufferSize;  ///< The high warter mark of receive buffer\r
+  UINT8                  IpVersion;\r
+  VOID                   *Protocol;      ///< The pointer to protocol function template\r
+                                         ///< wanted to install on socket\r
+\r
+  //\r
+  // Callbacks after socket is created and before socket is to be destroyed.\r
+  //\r
+  SOCK_CREATE_CALLBACK   CreateCallback;  ///< Callback after created\r
+  SOCK_DESTROY_CALLBACK  DestroyCallback; ///< Callback before destroied\r
+  VOID                   *Context;        ///< The context of the callback\r
+\r
+  //\r
+  // Opaque protocol data.\r
+  //\r
+  VOID                   *ProtoData;\r
+  UINT32                 DataSize;\r
+\r
+  SOCK_PROTO_HANDLER     ProtoHandler;    ///< The handler of protocol for socket request\r
+\r
+  EFI_HANDLE             DriverBinding;   ///< The driver binding handle\r
+} SOCK_INIT_DATA;\r
+\r
+///\r
+///  The union type of TCP and UDP protocol.\r
+///\r
+typedef union _NET_PROTOCOL {\r
+  EFI_TCP4_PROTOCOL      Tcp4Protocol;    ///< Tcp4 protocol\r
+  EFI_TCP6_PROTOCOL      Tcp6Protocol;    ///< Tcp6 protocol\r
+} NET_PROTOCOL;\r
+///\r
+/// The socket structure representing a network service access point.\r
+///\r
+struct _TCP_SOCKET {\r
+  //\r
+  // Socket description information\r
+  //\r
+  UINT32                    Signature;      ///< Signature of the socket\r
+  EFI_HANDLE                SockHandle;     ///< The virtual handle of the socket\r
+  EFI_HANDLE                DriverBinding;  ///< Socket's driver binding protocol\r
+  EFI_DEVICE_PATH_PROTOCOL  *ParentDevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
+  LIST_ENTRY                Link;\r
+  UINT8                     ConfigureState;\r
+  SOCK_TYPE                 Type;\r
+  UINT8                     State;\r
+  UINT16                    Flag;\r
+  EFI_LOCK                  Lock;           ///< The lock of socket\r
+  SOCK_BUFFER               SndBuffer;      ///< Send buffer of application's data\r
+  SOCK_BUFFER               RcvBuffer;      ///< Receive buffer of received data\r
+  EFI_STATUS                SockError;      ///< The error returned by low layer protocol\r
+  BOOLEAN                   IsDestroyed;\r
+\r
+  //\r
+  // Fields used to manage the connection request\r
+  //\r
+  UINT32                    BackLog;        ///< the limit of connection to this socket\r
+  UINT32                    ConnCnt;        ///< the current count of connections to it\r
+  SOCKET                    *Parent;        ///< listening parent that accept the connection\r
+  LIST_ENTRY                ConnectionList; ///< the connections maintained by this socket\r
+  //\r
+  // The queue to buffer application's asynchronous token\r
+  //\r
+  LIST_ENTRY                ListenTokenList;\r
+  LIST_ENTRY                RcvTokenList;\r
+  LIST_ENTRY                SndTokenList;\r
+  LIST_ENTRY                ProcessingSndTokenList;\r
+\r
+  SOCK_COMPLETION_TOKEN     *ConnectionToken; ///< app's token to signal if connected\r
+  SOCK_COMPLETION_TOKEN     *CloseToken;      ///< app's token to signal if closed\r
+  //\r
+  // Interface for low level protocol\r
+  //\r
+  SOCK_PROTO_HANDLER        ProtoHandler;     ///< The request handler of protocol\r
+  UINT8                     ProtoReserved[PROTO_RESERVED_LEN];  ///< Data fields reserved for protocol\r
+  UINT8                     IpVersion;\r
+  NET_PROTOCOL              NetProtocol;                        ///< TCP or UDP protocol socket used\r
+  //\r
+  // Callbacks after socket is created and before socket is to be destroyed.\r
+  //\r
+  SOCK_CREATE_CALLBACK      CreateCallback;   ///< Callback after created\r
+  SOCK_DESTROY_CALLBACK     DestroyCallback;  ///< Callback before destroied\r
+  VOID                      *Context;         ///< The context of the callback\r
+};\r
+\r
+///\r
+///  The token structure buffered in socket layer.\r
+///\r
+typedef struct _SOCK_TOKEN {\r
+  LIST_ENTRY            TokenList;      ///< The entry to add in the token list\r
+  SOCK_COMPLETION_TOKEN *Token;         ///< The application's token\r
+  UINT32                RemainDataLen;  ///< Unprocessed data length\r
+  SOCKET                *Sock;          ///< The poninter to the socket this token\r
+                                        ///< belongs to\r
+} SOCK_TOKEN;\r
+\r
+///\r
+/// Reserved data to access the NET_BUF delivered by TCP driver.\r
+///\r
+typedef struct _TCP_RSV_DATA {\r
+  UINT32 UrgLen;\r
+} TCP_RSV_DATA;\r
+\r
+//\r
+// Socket provided oprerations for low layer protocol implemented in SockImpl.c\r
+//\r
+\r
+/**\r
+  Set the state of the socket.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+  @param[in]       State                 The new socket state to be set.\r
+\r
+**/\r
+VOID\r
+SockSetState (\r
+  IN OUT SOCKET     *Sock,\r
+  IN     UINT8      State\r
+  );\r
+\r
+/**\r
+  Clone a new socket including its associated protocol control block.\r
+\r
+  @param[in]  Sock                  Pointer to the socket to be cloned.\r
+\r
+  @return Pointer to the newly cloned socket. If NULL, an error condition occurred.\r
+\r
+**/\r
+SOCKET *\r
+SockClone (\r
+  IN SOCKET *Sock\r
+  );\r
+\r
+/**\r
+  Called by the low layer protocol to indicate the socket a connection is\r
+  established.\r
+\r
+  This function just changes the socket's state to SO_CONNECTED\r
+  and signals the token used for connection establishment.\r
+\r
+  @param[in, out]  Sock         Pointer to the socket associated with the\r
+                                established connection.\r
+\r
+**/\r
+VOID\r
+SockConnEstablished (\r
+  IN OUT SOCKET *Sock\r
+  );\r
+\r
+/**\r
+  Called by the low layer protocol to indicate that the connection is closed.\r
+\r
+  This function flushes the socket, sets the state to SO_CLOSED, and signals\r
+  the close token.\r
+\r
+  @param[in, out]  Sock         Pointer to the socket associated with the closed\r
+                                connection.\r
+\r
+**/\r
+VOID\r
+SockConnClosed (\r
+  IN OUT SOCKET *Sock\r
+  );\r
+\r
+/**\r
+  Called by low layer protocol to indicate that some data is sent or processed.\r
+\r
+  This function trims the sent data in the socket send buffer and signals the data\r
+  token, if proper.\r
+\r
+  @param[in, out]  Sock      Pointer to the socket.\r
+  @param[in]       Count     The length of the data processed or sent, in bytes.\r
+\r
+**/\r
+VOID\r
+SockDataSent (\r
+  IN OUT SOCKET     *Sock,\r
+  IN     UINT32     Count\r
+  );\r
+\r
+/**\r
+  Called by the low layer protocol to copy some data in socket send\r
+  buffer starting from the specific offset to a buffer provided by\r
+  the caller.\r
+\r
+  @param[in]  Sock                  Pointer to the socket.\r
+  @param[in]  Offset                The start point of the data to be copied.\r
+  @param[in]  Len                   The length of the data to be copied.\r
+  @param[out] Dest                  Pointer to the destination to copy the data.\r
+\r
+  @return The data size copied.\r
+\r
+**/\r
+UINT32\r
+SockGetDataToSend (\r
+  IN  SOCKET      *Sock,\r
+  IN  UINT32      Offset,\r
+  IN  UINT32      Len,\r
+  OUT UINT8       *Dest\r
+  );\r
+\r
+/**\r
+  Called by the low layer protocol to deliver received data to socket layer.\r
+\r
+  This function appends the data to the socket receive buffer, set the\r
+  urgent data length, then checks if any receive token can be signaled.\r
+\r
+  @param[in, out]  Sock       Pointer to the socket.\r
+  @param[in, out]  NetBuffer  Pointer to the buffer that contains the received data.\r
+  @param[in]       UrgLen     The length of the urgent data in the received data.\r
+\r
+**/\r
+VOID\r
+SockDataRcvd (\r
+  IN OUT SOCKET    *Sock,\r
+  IN OUT NET_BUF   *NetBuffer,\r
+  IN     UINT32    UrgLen\r
+  );\r
+\r
+/**\r
+  Get the length of the free space of the specific socket buffer.\r
+\r
+  @param[in]  Sock              Pointer to the socket.\r
+  @param[in]  Which             Flag to indicate which socket buffer to check:\r
+                                either send buffer or receive buffer.\r
+\r
+  @return The length of the free space, in bytes.\r
+\r
+**/\r
+UINT32\r
+SockGetFreeSpace (\r
+  IN SOCKET  *Sock,\r
+  IN UINT32  Which\r
+  );\r
+\r
+/**\r
+  Called by the low layer protocol to indicate that there will be no more data\r
+  from the communication peer.\r
+\r
+  This function sets the socket's state to SO_NO_MORE_DATA and signals all queued\r
+  IO tokens with the error status EFI_CONNECTION_FIN.\r
+\r
+  @param[in, out]  Sock                  Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockNoMoreData (\r
+  IN OUT SOCKET *Sock\r
+  );\r
+\r
+//\r
+// Socket provided operations for user interface implemented in SockInterface.c\r
+//\r
+\r
+/**\r
+  Create a socket and its associated protocol control block\r
+  with the intial data SockInitData and protocol specific\r
+  data ProtoData.\r
+\r
+  @param[in]  SockInitData         Inital data to setting the socket.\r
+\r
+  @return Pointer to the newly created socket. If NULL, an error condition occured.\r
+\r
+**/\r
+SOCKET *\r
+SockCreateChild (\r
+  IN SOCK_INIT_DATA *SockInitData\r
+  );\r
+\r
+/**\r
+  Destory the socket Sock and its associated protocol control block.\r
+\r
+  @param[in, out]  Sock                 The socket to be destroyed.\r
+\r
+  @retval EFI_SUCCESS          The socket Sock was destroyed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.\r
+\r
+**/\r
+EFI_STATUS\r
+SockDestroyChild (\r
+  IN OUT SOCKET *Sock\r
+  );\r
+\r
+/**\r
+  Configure the specific socket Sock using configuration data ConfigData.\r
+\r
+  @param[in]  Sock             Pointer to the socket to be configured.\r
+  @param[in]  ConfigData       Pointer to the configuration data.\r
+\r
+  @retval EFI_SUCCESS          The socket configured successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is already configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockConfigure (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *ConfigData\r
+  );\r
+\r
+/**\r
+  Initiate a connection establishment process.\r
+\r
+  @param[in]  Sock             Pointer to the socket to initiate the initate the\r
+                               connection.\r
+  @param[in]  Token            Pointer to the token used for the connection\r
+                               operation.\r
+\r
+  @retval EFI_SUCCESS          The connection initialized successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not configured to\r
+                               be an active one, or the token is already in one of\r
+                               this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockConnect (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *Token\r
+  );\r
+\r
+/**\r
+  Issue a listen token to get an existed connected network instance,\r
+  or wait for a connection if there is none.\r
+\r
+  @param[in]  Sock             Pointer to the socket to accept connections.\r
+  @param[in]  Token            The token to accept a connection.\r
+\r
+  @retval EFI_SUCCESS          Either a connection is accepted or the Token is\r
+                               buffered for further acceptance.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not configured to\r
+                               be a passive one, or the token is already in one of\r
+                               this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the Token due to memory limit.\r
+\r
+**/\r
+EFI_STATUS\r
+SockAccept (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *Token\r
+  );\r
+\r
+/**\r
+  Issue a token with data to the socket to send out.\r
+\r
+  @param[in]  Sock             Pointer to the socket to process the token with\r
+                               data.\r
+  @param[in]  Token            The token with data that needs to send out.\r
+\r
+  @retval EFI_SUCCESS          The token processed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not in a\r
+                               synchronized state , or the token is already in one\r
+                               of this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the token due to a memory limit.\r
+\r
+**/\r
+EFI_STATUS\r
+SockSend (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *Token\r
+  );\r
+\r
+/**\r
+  Issue a token to get data from the socket.\r
+\r
+  @param[in]  Sock             Pointer to the socket to get data from.\r
+  @param[in]  Token            The token to store the received data from the\r
+                               socket.\r
+\r
+  @retval EFI_SUCCESS          The token processed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not in a\r
+                               synchronized state , or the token is already in one\r
+                               of this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+  @retval EFI_CONNECTION_FIN   The connection is closed and there is no more data.\r
+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the token due to a memory limit.\r
+\r
+**/\r
+EFI_STATUS\r
+SockRcv (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *Token\r
+  );\r
+\r
+/**\r
+  Reset the socket and its associated protocol control block.\r
+\r
+  @param[in, out]  Sock        Pointer to the socket to be flushed.\r
+\r
+  @retval EFI_SUCCESS          The socket flushed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.\r
+\r
+**/\r
+EFI_STATUS\r
+SockFlush (\r
+  IN OUT SOCKET *Sock\r
+  );\r
+\r
+/**\r
+  Close or abort the socket associated connection.\r
+\r
+  @param[in, out]  Sock        Pointer to the socket of the connection to close\r
+                               or abort.\r
+  @param[in]  Token            The token for close operation.\r
+  @param[in]  OnAbort          TRUE for aborting the connection, FALSE to close it.\r
+\r
+  @retval EFI_SUCCESS          The close or abort operation initialized\r
+                               successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the\r
+                               socket is closed, or the socket is not in a\r
+                               synchronized state , or the token is already in one\r
+                               of this socket's lists.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockClose (\r
+  IN OUT SOCKET  *Sock,\r
+  IN     VOID    *Token,\r
+  IN     BOOLEAN OnAbort\r
+  );\r
+\r
+/**\r
+  Get the mode data of the low layer protocol.\r
+\r
+  @param[in]       Sock        Pointer to the socket to get mode data from.\r
+  @param[in, out]  Mode        Pointer to the data to store the low layer mode\r
+                               information.\r
+\r
+  @retval EFI_SUCCESS          The mode data was obtained successfully.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockGetMode (\r
+  IN     SOCKET *Sock,\r
+  IN OUT VOID   *Mode\r
+  );\r
+\r
+/**\r
+  Configure the low level protocol to join a multicast group for\r
+  this socket's connection.\r
+\r
+  @param[in]  Sock             Pointer to the socket of the connection to join the\r
+                               specific multicast group.\r
+  @param[in]  GroupInfo        Pointer to the multicast group information.\r
+\r
+  @retval EFI_SUCCESS          The configuration completed successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockGroup (\r
+  IN SOCKET *Sock,\r
+  IN VOID   *GroupInfo\r
+  );\r
+\r
+/**\r
+  Add or remove route information in IP route table associated\r
+  with this socket.\r
+\r
+  @param[in]  Sock             Pointer to the socket associated with the IP route\r
+                               table to operate on.\r
+  @param[in]  RouteInfo        Pointer to the route information to be processed.\r
+\r
+  @retval EFI_SUCCESS          The route table updated successfully.\r
+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.\r
+  @retval EFI_NO_MAPPING       The IP address configuration operation is  not\r
+                               finished.\r
+  @retval EFI_NOT_STARTED      The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockRoute (\r
+  IN SOCKET    *Sock,\r
+  IN VOID      *RouteInfo\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/TcpDxe/TcpDispatcher.c b/NetworkPkg/TcpDxe/TcpDispatcher.c
new file mode 100644 (file)
index 0000000..eaa75a4
--- /dev/null
@@ -0,0 +1,861 @@
+/** @file\r
+  The implementation of a dispatch routine for processing TCP requests.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+  Add or remove a route entry in the IP route table associated with this TCP instance.\r
+\r
+  @param[in]  Tcb               Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  RouteInfo         Pointer to the route information to be processed.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval EFI_NOT_STARTED       The driver instance has not been started.\r
+  @retval EFI_NO_MAPPING        When using the default address, configuration(DHCP,\r
+                                BOOTP, RARP, etc.) is not finished yet.\r
+  @retval EFI_OUT_OF_RESOURCES  Could not add the entry to the routing table.\r
+  @retval EFI_NOT_FOUND         This route is not in the routing table\r
+                                (when RouteInfo->DeleteRoute is TRUE).\r
+  @retval EFI_ACCESS_DENIED     The route is already defined in the routing table\r
+                                (when RouteInfo->DeleteRoute is FALSE).\r
+**/\r
+EFI_STATUS\r
+Tcp4Route (\r
+  IN TCP_CB           *Tcb,\r
+  IN TCP4_ROUTE_INFO  *RouteInfo\r
+  )\r
+{\r
+  IP_IO_IP_PROTOCOL   Ip;\r
+\r
+  Ip = Tcb->IpInfo->Ip;\r
+\r
+  ASSERT (Ip.Ip4!= NULL);\r
+\r
+  return Ip.Ip4->Routes (\r
+                   Ip.Ip4,\r
+                   RouteInfo->DeleteRoute,\r
+                   RouteInfo->SubnetAddress,\r
+                   RouteInfo->SubnetMask,\r
+                   RouteInfo->GatewayAddress\r
+                   );\r
+\r
+}\r
+\r
+/**\r
+  Get the operational settings of this TCPv4 instance.\r
+\r
+  @param[in]       Tcb           Pointer to the TCP_CB of this TCP instance.\r
+  @param[in, out]  Mode          Pointer to the buffer to store the operational\r
+                                 settings.\r
+\r
+  @retval EFI_SUCCESS            The mode data was read.\r
+  @retval EFI_NOT_STARTED        No configuration data is available because this\r
+                                 instance hasn't been started.\r
+\r
+**/\r
+EFI_STATUS\r
+Tcp4GetMode (\r
+  IN     TCP_CB         *Tcb,\r
+  IN OUT TCP4_MODE_DATA *Mode\r
+  )\r
+{\r
+  SOCKET                *Sock;\r
+  EFI_TCP4_CONFIG_DATA  *ConfigData;\r
+  EFI_TCP4_ACCESS_POINT *AccessPoint;\r
+  EFI_TCP4_OPTION       *Option;\r
+  EFI_IP4_PROTOCOL      *Ip;\r
+\r
+  Sock = Tcb->Sk;\r
+\r
+  if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Mode->Tcp4State != NULL) {\r
+    *(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State;\r
+  }\r
+\r
+  if (Mode->Tcp4ConfigData != NULL) {\r
+\r
+    ConfigData                       = Mode->Tcp4ConfigData;\r
+    AccessPoint                      = &(ConfigData->AccessPoint);\r
+    Option                           = ConfigData->ControlOption;\r
+\r
+    ConfigData->TypeOfService        = Tcb->Tos;\r
+    ConfigData->TimeToLive           = Tcb->Ttl;\r
+\r
+    AccessPoint->UseDefaultAddress   = Tcb->UseDefaultAddr;\r
+\r
+    CopyMem (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+    AccessPoint->SubnetMask          = Tcb->SubnetMask;\r
+    AccessPoint->StationPort         = NTOHS (Tcb->LocalEnd.Port);\r
+\r
+    CopyMem (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+    AccessPoint->RemotePort          = NTOHS (Tcb->RemoteEnd.Port);\r
+    AccessPoint->ActiveFlag          = (BOOLEAN) (Tcb->State != TCP_LISTEN);\r
+\r
+    if (Option != NULL) {\r
+      Option->ReceiveBufferSize      = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+      Option->SendBufferSize         = GET_SND_BUFFSIZE (Tcb->Sk);\r
+      Option->MaxSynBackLog          = GET_BACKLOG (Tcb->Sk);\r
+\r
+      Option->ConnectionTimeout      = Tcb->ConnectTimeout / TCP_TICK_HZ;\r
+      Option->DataRetries            = Tcb->MaxRexmit;\r
+      Option->FinTimeout             = Tcb->FinWait2Timeout / TCP_TICK_HZ;\r
+      Option->TimeWaitTimeout        = Tcb->TimeWaitTimeout / TCP_TICK_HZ;\r
+      Option->KeepAliveProbes        = Tcb->MaxKeepAlive;\r
+      Option->KeepAliveTime          = Tcb->KeepAliveIdle / TCP_TICK_HZ;\r
+      Option->KeepAliveInterval      = Tcb->KeepAlivePeriod / TCP_TICK_HZ;\r
+\r
+      Option->EnableNagle            = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));\r
+      Option->EnableTimeStamp        = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));\r
+      Option->EnableWindowScaling    = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));\r
+\r
+      Option->EnableSelectiveAck     = FALSE;\r
+      Option->EnablePathMtuDiscovery = FALSE;\r
+    }\r
+  }\r
+\r
+  Ip = Tcb->IpInfo->Ip.Ip4;\r
+  ASSERT (Ip != NULL);\r
+\r
+  return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData);\r
+}\r
+\r
+/**\r
+  Get the operational settings of this TCPv6 instance.\r
+\r
+  @param[in]       Tcb           Pointer to the TCP_CB of this TCP instance.\r
+  @param[in, out]  Mode          Pointer to the buffer to store the operational\r
+                                 settings.\r
+\r
+  @retval EFI_SUCCESS            The mode data was read.\r
+  @retval EFI_NOT_STARTED        No configuration data is available because this\r
+                                 instance hasn't been started.\r
+\r
+**/\r
+EFI_STATUS\r
+Tcp6GetMode (\r
+  IN     TCP_CB         *Tcb,\r
+  IN OUT TCP6_MODE_DATA *Mode\r
+  )\r
+{\r
+  SOCKET                *Sock;\r
+  EFI_TCP6_CONFIG_DATA  *ConfigData;\r
+  EFI_TCP6_ACCESS_POINT *AccessPoint;\r
+  EFI_TCP6_OPTION       *Option;\r
+  EFI_IP6_PROTOCOL      *Ip;\r
+\r
+  Sock = Tcb->Sk;\r
+\r
+  if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp6ConfigData != NULL)) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Mode->Tcp6State != NULL) {\r
+    *(Mode->Tcp6State) = (EFI_TCP6_CONNECTION_STATE) (Tcb->State);\r
+  }\r
+\r
+  if (Mode->Tcp6ConfigData != NULL) {\r
+\r
+    ConfigData                       = Mode->Tcp6ConfigData;\r
+    AccessPoint                      = &(ConfigData->AccessPoint);\r
+    Option                           = ConfigData->ControlOption;\r
+\r
+    ConfigData->TrafficClass         = Tcb->Tos;\r
+    ConfigData->HopLimit             = Tcb->Ttl;\r
+\r
+    AccessPoint->StationPort         = NTOHS (Tcb->LocalEnd.Port);\r
+    AccessPoint->RemotePort          = NTOHS (Tcb->RemoteEnd.Port);\r
+    AccessPoint->ActiveFlag          = (BOOLEAN) (Tcb->State != TCP_LISTEN);\r
+\r
+    IP6_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip);\r
+    IP6_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip);\r
+\r
+    if (Option != NULL) {\r
+      Option->ReceiveBufferSize      = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+      Option->SendBufferSize         = GET_SND_BUFFSIZE (Tcb->Sk);\r
+      Option->MaxSynBackLog          = GET_BACKLOG (Tcb->Sk);\r
+\r
+      Option->ConnectionTimeout      = Tcb->ConnectTimeout / TCP_TICK_HZ;\r
+      Option->DataRetries            = Tcb->MaxRexmit;\r
+      Option->FinTimeout             = Tcb->FinWait2Timeout / TCP_TICK_HZ;\r
+      Option->TimeWaitTimeout        = Tcb->TimeWaitTimeout / TCP_TICK_HZ;\r
+      Option->KeepAliveProbes        = Tcb->MaxKeepAlive;\r
+      Option->KeepAliveTime          = Tcb->KeepAliveIdle / TCP_TICK_HZ;\r
+      Option->KeepAliveInterval      = Tcb->KeepAlivePeriod / TCP_TICK_HZ;\r
+\r
+      Option->EnableNagle            = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));\r
+      Option->EnableTimeStamp        = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));\r
+      Option->EnableWindowScaling    = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));\r
+\r
+      Option->EnableSelectiveAck     = FALSE;\r
+      Option->EnablePathMtuDiscovery = FALSE;\r
+    }\r
+  }\r
+\r
+  Ip = Tcb->IpInfo->Ip.Ip6;\r
+  ASSERT (Ip != NULL);\r
+\r
+  return Ip->GetModeData (Ip, Mode->Ip6ModeData, Mode->MnpConfigData, Mode->SnpModeData);\r
+}\r
+\r
+/**\r
+  If TcpAp->StationPort isn't zero, check whether the access point\r
+  is registered, else generate a random station port for this\r
+  access point.\r
+\r
+  @param[in]  TcpAp              Pointer to the access point.\r
+  @param[in]  IpVersion          IP_VERSION_4 or IP_VERSION_6\r
+\r
+  @retval EFI_SUCCESS            The check passed or the port is assigned.\r
+  @retval EFI_INVALID_PARAMETER  The non-zero station port is already used.\r
+  @retval EFI_OUT_OF_RESOURCES   No port can be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpBind (\r
+  IN TCP_ACCESS_POINT  *TcpAp,\r
+  IN UINT8             IpVersion\r
+  )\r
+{\r
+  BOOLEAN         Cycle;\r
+  EFI_IP_ADDRESS  Local;\r
+  UINT16          *Port;\r
+  UINT16          *RandomPort;\r
+\r
+  if (IpVersion == IP_VERSION_4) {\r
+    CopyMem (&Local, &TcpAp->Tcp4Ap.StationAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    Port       = &TcpAp->Tcp4Ap.StationPort;\r
+    RandomPort = &mTcp4RandomPort;\r
+  } else {\r
+    IP6_COPY_ADDRESS (&Local, &TcpAp->Tcp6Ap.StationAddress);\r
+    Port       = &TcpAp->Tcp6Ap.StationPort;\r
+    RandomPort = &mTcp6RandomPort;\r
+  }\r
+\r
+  if (0 != *Port) {\r
+    //\r
+    // Check if a same endpoing is bound.\r
+    //\r
+    if (TcpFindTcbByPeer (&Local, *Port, IpVersion)) {\r
+\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  } else {\r
+    //\r
+    // generate a random port\r
+    //\r
+    Cycle = FALSE;\r
+\r
+    if (TCP_PORT_USER_RESERVED == *RandomPort) {\r
+      *RandomPort = TCP_PORT_KNOWN;\r
+    }\r
+\r
+    (*RandomPort)++;\r
+\r
+    while (TcpFindTcbByPeer (&Local, *RandomPort, IpVersion)) {\r
+      (*RandomPort)++;\r
+\r
+      if (*RandomPort <= TCP_PORT_KNOWN) {\r
+        if (Cycle) {\r
+          DEBUG (\r
+            (EFI_D_ERROR,\r
+            "TcpBind: no port can be allocated for this pcb\n")\r
+            );\r
+          return EFI_OUT_OF_RESOURCES;\r
+        }\r
+\r
+        *RandomPort = TCP_PORT_KNOWN + 1;\r
+\r
+        Cycle       = TRUE;\r
+      }\r
+    }\r
+\r
+    *Port = *RandomPort;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Flush the Tcb add its associated protocols.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB to be flushed.\r
+\r
+**/\r
+VOID\r
+TcpFlushPcb (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  SOCKET                    *Sock;\r
+  TCP_PROTO_DATA            *TcpProto;\r
+\r
+  IpIoConfigIp (Tcb->IpInfo, NULL);\r
+\r
+  Sock     = Tcb->Sk;\r
+  TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+\r
+  if (SOCK_IS_CONFIGURED (Sock)) {\r
+    RemoveEntryList (&Tcb->List);\r
+\r
+    if (Sock->DevicePath != NULL) {\r
+      //\r
+      // Uninstall the device path protocl.\r
+      //\r
+      gBS->UninstallProtocolInterface (\r
+             Sock->SockHandle,\r
+             &gEfiDevicePathProtocolGuid,\r
+             Sock->DevicePath\r
+             );\r
+\r
+      FreePool (Sock->DevicePath);\r
+      Sock->DevicePath = NULL;\r
+    }\r
+\r
+    TcpSetVariableData (TcpProto->TcpService);\r
+  }\r
+\r
+  NetbufFreeList (&Tcb->SndQue);\r
+  NetbufFreeList (&Tcb->RcvQue);\r
+  Tcb->State = TCP_CLOSED;\r
+}\r
+\r
+/**\r
+  Attach a Pcb to the socket.\r
+\r
+  @param[in]  Sk                 Pointer to the socket of this TCP instance.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed due to resource limits.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpAttachPcb (\r
+  IN SOCKET  *Sk\r
+  )\r
+{\r
+  TCP_CB          *Tcb;\r
+  TCP_PROTO_DATA  *ProtoData;\r
+  IP_IO           *IpIo;\r
+\r
+  Tcb = AllocateZeroPool (sizeof (TCP_CB));\r
+\r
+  if (Tcb == NULL) {\r
+\r
+    DEBUG ((EFI_D_ERROR, "TcpConfigurePcb: failed to allocate a TCB\n"));\r
+\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;\r
+  IpIo      = ProtoData->TcpService->IpIo;\r
+\r
+  //\r
+  // Create an IpInfo for this Tcb.\r
+  //\r
+  Tcb->IpInfo = IpIoAddIp (IpIo);\r
+  if (Tcb->IpInfo == NULL) {\r
+\r
+    FreePool (Tcb);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  InitializeListHead (&Tcb->List);\r
+  InitializeListHead (&Tcb->SndQue);\r
+  InitializeListHead (&Tcb->RcvQue);\r
+\r
+  Tcb->State        = TCP_CLOSED;\r
+  Tcb->Sk           = Sk;\r
+  ProtoData->TcpPcb = Tcb;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Detach the Pcb of the socket.\r
+\r
+  @param[in, out]  Sk           Pointer to the socket of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpDetachPcb (\r
+  IN OUT SOCKET    *Sk\r
+  )\r
+{\r
+  TCP_PROTO_DATA   *ProtoData;\r
+  TCP_CB           *Tcb;\r
+\r
+  ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;\r
+  Tcb       = ProtoData->TcpPcb;\r
+\r
+  ASSERT (Tcb != NULL);\r
+\r
+  TcpFlushPcb (Tcb);\r
+\r
+  IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo);\r
+\r
+  FreePool (Tcb);\r
+\r
+  ProtoData->TcpPcb = NULL;\r
+}\r
+\r
+/**\r
+  Configure the Pcb using CfgData.\r
+\r
+  @param[in]  Sk                 Pointer to the socket of this TCP instance.\r
+  @param[in]  CfgData            Pointer to the TCP configuration data.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_INVALID_PARAMETER  A same access point has been configured in\r
+                                 another TCP instance.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed due to resource limits.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpConfigurePcb (\r
+  IN SOCKET           *Sk,\r
+  IN TCP_CONFIG_DATA  *CfgData\r
+  )\r
+{\r
+  IP_IO_IP_CONFIG_DATA IpCfgData;\r
+  EFI_STATUS           Status;\r
+  EFI_TCP4_OPTION      *Option;\r
+  TCP_PROTO_DATA       *TcpProto;\r
+  TCP_CB               *Tcb;\r
+  TCP_ACCESS_POINT     *TcpAp;\r
+\r
+  ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL));\r
+\r
+  TcpProto = (TCP_PROTO_DATA *) Sk->ProtoReserved;\r
+  Tcb      = TcpProto->TcpPcb;\r
+\r
+  ASSERT (Tcb != NULL);\r
+\r
+  if (Sk->IpVersion == IP_VERSION_4) {\r
+    //\r
+    // Add Ip for send pkt to the peer\r
+    //\r
+    CopyMem (&IpCfgData.Ip4CfgData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA));\r
+    IpCfgData.Ip4CfgData.DefaultProtocol    = EFI_IP_PROTO_TCP;\r
+    IpCfgData.Ip4CfgData.TypeOfService      = CfgData->Tcp4CfgData.TypeOfService;\r
+    IpCfgData.Ip4CfgData.TimeToLive         = CfgData->Tcp4CfgData.TimeToLive;\r
+    IpCfgData.Ip4CfgData.UseDefaultAddress  = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;\r
+    IpCfgData.Ip4CfgData.SubnetMask         = CfgData->Tcp4CfgData.AccessPoint.SubnetMask;\r
+    IpCfgData.Ip4CfgData.ReceiveTimeout     = (UINT32) (-1);\r
+    CopyMem (\r
+      &IpCfgData.Ip4CfgData.StationAddress,\r
+      &CfgData->Tcp4CfgData.AccessPoint.StationAddress,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+\r
+  } else {\r
+    ASSERT (Sk->IpVersion == IP_VERSION_6);\r
+\r
+    CopyMem (&IpCfgData.Ip6CfgData, &mIp6IoDefaultIpConfigData, sizeof (EFI_IP6_CONFIG_DATA));\r
+    IpCfgData.Ip6CfgData.DefaultProtocol    = EFI_IP_PROTO_TCP;\r
+    IpCfgData.Ip6CfgData.TrafficClass       = CfgData->Tcp6CfgData.TrafficClass;\r
+    IpCfgData.Ip6CfgData.HopLimit           = CfgData->Tcp6CfgData.HopLimit;\r
+    IpCfgData.Ip6CfgData.ReceiveTimeout     = (UINT32) (-1);\r
+    IP6_COPY_ADDRESS (\r
+      &IpCfgData.Ip6CfgData.StationAddress,\r
+      &CfgData->Tcp6CfgData.AccessPoint.StationAddress\r
+      );\r
+    IP6_COPY_ADDRESS (\r
+      &IpCfgData.Ip6CfgData.DestinationAddress,\r
+      &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress\r
+      );\r
+  }\r
+\r
+  //\r
+  // Configure the IP instance this Tcb consumes.\r
+  //\r
+  Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData);\r
+  if (EFI_ERROR (Status)) {\r
+    goto OnExit;\r
+  }\r
+\r
+  if (Sk->IpVersion == IP_VERSION_4) {\r
+    //\r
+    // Get the default address information if the instance is configured to use default address.\r
+    //\r
+    CfgData->Tcp4CfgData.AccessPoint.StationAddress = IpCfgData.Ip4CfgData.StationAddress;\r
+    CfgData->Tcp4CfgData.AccessPoint.SubnetMask     = IpCfgData.Ip4CfgData.SubnetMask;\r
+\r
+    TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp4CfgData.AccessPoint;\r
+  } else {\r
+    IP6_COPY_ADDRESS (\r
+      &CfgData->Tcp6CfgData.AccessPoint.StationAddress,\r
+      &IpCfgData.Ip6CfgData.StationAddress\r
+      );\r
+\r
+    TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp6CfgData.AccessPoint;\r
+  }\r
+\r
+  //\r
+  // check if we can bind this endpoint in CfgData\r
+  //\r
+  Status = TcpBind (TcpAp, Sk->IpVersion);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "TcpConfigurePcb: Bind endpoint failed with %r\n",\r
+      Status)\r
+      );\r
+\r
+    goto OnExit;\r
+  }\r
+\r
+  //\r
+  // Initalize the operating information in this Tcb\r
+  //\r
+  ASSERT (Tcb->State == TCP_CLOSED &&\r
+    IsListEmpty (&Tcb->SndQue) &&\r
+    IsListEmpty (&Tcb->RcvQue));\r
+\r
+  TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);\r
+  Tcb->State            = TCP_CLOSED;\r
+\r
+  Tcb->SndMss           = 536;\r
+  Tcb->RcvMss           = TcpGetRcvMss (Sk);\r
+\r
+  Tcb->SRtt             = 0;\r
+  Tcb->Rto              = 3 * TCP_TICK_HZ;\r
+\r
+  Tcb->CWnd             = Tcb->SndMss;\r
+  Tcb->Ssthresh         = 0xffffffff;\r
+\r
+  Tcb->CongestState     = TCP_CONGEST_OPEN;\r
+\r
+  Tcb->KeepAliveIdle    = TCP_KEEPALIVE_IDLE_MIN;\r
+  Tcb->KeepAlivePeriod  = TCP_KEEPALIVE_PERIOD;\r
+  Tcb->MaxKeepAlive     = TCP_MAX_KEEPALIVE;\r
+  Tcb->MaxRexmit        = TCP_MAX_LOSS;\r
+  Tcb->FinWait2Timeout  = TCP_FIN_WAIT2_TIME;\r
+  Tcb->TimeWaitTimeout  = TCP_TIME_WAIT_TIME;\r
+  Tcb->ConnectTimeout   = TCP_CONNECT_TIME;\r
+\r
+  if (Sk->IpVersion == IP_VERSION_4) {\r
+    //\r
+    // initialize Tcb in the light of CfgData\r
+    //\r
+    Tcb->Ttl            = CfgData->Tcp4CfgData.TimeToLive;\r
+    Tcb->Tos            = CfgData->Tcp4CfgData.TypeOfService;\r
+\r
+    Tcb->UseDefaultAddr = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;\r
+\r
+    CopyMem (&Tcb->LocalEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.StationAddress, sizeof (IP4_ADDR));\r
+    Tcb->LocalEnd.Port  = HTONS (CfgData->Tcp4CfgData.AccessPoint.StationPort);\r
+    Tcb->SubnetMask     = CfgData->Tcp4CfgData.AccessPoint.SubnetMask;\r
+\r
+    CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.RemoteAddress, sizeof (IP4_ADDR));\r
+    Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.RemotePort);\r
+\r
+    Option              = CfgData->Tcp4CfgData.ControlOption;\r
+  } else {\r
+    Tcb->Ttl            = CfgData->Tcp6CfgData.HopLimit;\r
+    Tcb->Tos            = CfgData->Tcp6CfgData.TrafficClass;\r
+\r
+    IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.StationAddress);\r
+    Tcb->LocalEnd.Port  = HTONS (CfgData->Tcp6CfgData.AccessPoint.StationPort);\r
+\r
+    IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress);\r
+    Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.RemotePort);\r
+\r
+    //\r
+    // Type EFI_TCP4_OPTION and EFI_TCP6_OPTION are the same.\r
+    //\r
+    Option              = (EFI_TCP4_OPTION *) CfgData->Tcp6CfgData.ControlOption;\r
+  }\r
+\r
+  if (Option != NULL) {\r
+    SET_RCV_BUFFSIZE (\r
+      Sk,\r
+      (UINT32) (TCP_COMP_VAL (\r
+                  TCP_RCV_BUF_SIZE_MIN,\r
+                  TCP_RCV_BUF_SIZE,\r
+                  TCP_RCV_BUF_SIZE,\r
+                  Option->ReceiveBufferSize\r
+                  )\r
+               )\r
+      );\r
+    SET_SND_BUFFSIZE (\r
+      Sk,\r
+      (UINT32) (TCP_COMP_VAL (\r
+                  TCP_SND_BUF_SIZE_MIN,\r
+                  TCP_SND_BUF_SIZE,\r
+                  TCP_SND_BUF_SIZE,\r
+                  Option->SendBufferSize\r
+                  )\r
+               )\r
+      );\r
+\r
+    SET_BACKLOG (\r
+      Sk,\r
+      (UINT32) (TCP_COMP_VAL (\r
+                  TCP_BACKLOG_MIN,\r
+                  TCP_BACKLOG,\r
+                  TCP_BACKLOG,\r
+                  Option->MaxSynBackLog\r
+                  )\r
+               )\r
+      );\r
+\r
+    Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL (\r
+                                TCP_MAX_LOSS_MIN,\r
+                                TCP_MAX_LOSS,\r
+                                TCP_MAX_LOSS,\r
+                                Option->DataRetries\r
+                                );\r
+    Tcb->FinWait2Timeout = TCP_COMP_VAL (\r
+                              TCP_FIN_WAIT2_TIME,\r
+                              TCP_FIN_WAIT2_TIME_MAX,\r
+                              TCP_FIN_WAIT2_TIME,\r
+                              (UINT32) (Option->FinTimeout * TCP_TICK_HZ)\r
+                              );\r
+\r
+    if (Option->TimeWaitTimeout != 0) {\r
+      Tcb->TimeWaitTimeout = TCP_COMP_VAL (\r
+                               TCP_TIME_WAIT_TIME,\r
+                               TCP_TIME_WAIT_TIME_MAX,\r
+                               TCP_TIME_WAIT_TIME,\r
+                               (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ)\r
+                               );\r
+    } else {\r
+      Tcb->TimeWaitTimeout = 0;\r
+    }\r
+\r
+    if (Option->KeepAliveProbes != 0) {\r
+      TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);\r
+\r
+      Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL (\r
+                                    TCP_MAX_KEEPALIVE_MIN,\r
+                                    TCP_MAX_KEEPALIVE,\r
+                                    TCP_MAX_KEEPALIVE,\r
+                                    Option->KeepAliveProbes\r
+                                    );\r
+      Tcb->KeepAliveIdle = TCP_COMP_VAL (\r
+                             TCP_KEEPALIVE_IDLE_MIN,\r
+                             TCP_KEEPALIVE_IDLE_MAX,\r
+                             TCP_KEEPALIVE_IDLE_MIN,\r
+                             (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ)\r
+                             );\r
+      Tcb->KeepAlivePeriod = TCP_COMP_VAL (\r
+                               TCP_KEEPALIVE_PERIOD_MIN,\r
+                               TCP_KEEPALIVE_PERIOD,\r
+                               TCP_KEEPALIVE_PERIOD,\r
+                               (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ)\r
+                               );\r
+    }\r
+\r
+    Tcb->ConnectTimeout = TCP_COMP_VAL (\r
+                            TCP_CONNECT_TIME_MIN,\r
+                            TCP_CONNECT_TIME,\r
+                            TCP_CONNECT_TIME,\r
+                            (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ)\r
+                            );\r
+\r
+    if (!Option->EnableNagle) {\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE);\r
+    }\r
+\r
+    if (!Option->EnableTimeStamp) {\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS);\r
+    }\r
+\r
+    if (!Option->EnableWindowScaling) {\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS);\r
+    }\r
+  }\r
+\r
+  //\r
+  // The socket is bound, the <SrcIp, SrcPort, DstIp, DstPort> is\r
+  // determined, construct the IP device path and install it.\r
+  //\r
+  Status = TcpInstallDevicePath (Sk);\r
+  if (EFI_ERROR (Status)) {\r
+    goto OnExit;\r
+  }\r
+\r
+  //\r
+  // update state of Tcb and socket\r
+  //\r
+  if (((Sk->IpVersion == IP_VERSION_4) && !CfgData->Tcp4CfgData.AccessPoint.ActiveFlag) ||\r
+      ((Sk->IpVersion == IP_VERSION_6) && !CfgData->Tcp6CfgData.AccessPoint.ActiveFlag)\r
+      ) {\r
+\r
+    TcpSetState (Tcb, TCP_LISTEN);\r
+    SockSetState (Sk, SO_LISTENING);\r
+\r
+    Sk->ConfigureState = SO_CONFIGURED_PASSIVE;\r
+  } else {\r
+\r
+    Sk->ConfigureState = SO_CONFIGURED_ACTIVE;\r
+  }\r
+\r
+  if (Sk->IpVersion == IP_VERSION_6) {\r
+    Tcb->Tick          = TCP6_REFRESH_NEIGHBOR_TICK;\r
+  }\r
+\r
+  TcpInsertTcb (Tcb);\r
+\r
+OnExit:\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The procotol handler provided to the socket layer, which is used to\r
+  dispatch the socket level requests by calling the corresponding\r
+  TCP layer functions.\r
+\r
+  @param[in]  Sock               Pointer to the socket of this TCP instance.\r
+  @param[in]  Request            The code of this operation request.\r
+  @param[in]  Data               Pointer to the operation specific data passed in\r
+                                 together with the operation request. This is an\r
+                                 optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The socket request completed successfully.\r
+  @retval other                  The error status returned by the corresponding TCP\r
+                                 layer function.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpDispatcher (\r
+  IN SOCKET                  *Sock,\r
+  IN UINT8                   Request,\r
+  IN VOID                    *Data    OPTIONAL\r
+  )\r
+{\r
+  TCP_CB          *Tcb;\r
+  TCP_PROTO_DATA  *ProtoData;\r
+\r
+  ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+  Tcb       = ProtoData->TcpPcb;\r
+\r
+  switch (Request) {\r
+  case SOCK_POLL:\r
+    if (Tcb->Sk->IpVersion == IP_VERSION_4) {\r
+      ProtoData->TcpService->IpIo->Ip.Ip4->Poll (ProtoData->TcpService->IpIo->Ip.Ip4);\r
+    } else {\r
+      ProtoData->TcpService->IpIo->Ip.Ip6->Poll (ProtoData->TcpService->IpIo->Ip.Ip6);\r
+    }\r
+\r
+    break;\r
+\r
+  case SOCK_CONSUMED:\r
+    //\r
+    // After user received data from socket buffer, socket will\r
+    // notify TCP using this message to give it a chance to send out\r
+    // window update information\r
+    //\r
+    ASSERT (Tcb != NULL);\r
+    TcpOnAppConsume (Tcb);\r
+    break;\r
+\r
+  case SOCK_SND:\r
+\r
+    ASSERT (Tcb != NULL);\r
+    TcpOnAppSend (Tcb);\r
+    break;\r
+\r
+  case SOCK_CLOSE:\r
+\r
+    TcpOnAppClose (Tcb);\r
+\r
+    break;\r
+\r
+  case SOCK_ABORT:\r
+\r
+    TcpOnAppAbort (Tcb);\r
+\r
+    break;\r
+\r
+  case SOCK_SNDPUSH:\r
+    Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk);\r
+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);\r
+\r
+    break;\r
+\r
+  case SOCK_SNDURG:\r
+    Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1;\r
+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);\r
+\r
+    break;\r
+\r
+  case SOCK_CONNECT:\r
+\r
+    TcpOnAppConnect (Tcb);\r
+\r
+    break;\r
+\r
+  case SOCK_ATTACH:\r
+\r
+    return TcpAttachPcb (Sock);\r
+\r
+    break;\r
+\r
+  case SOCK_FLUSH:\r
+\r
+    TcpFlushPcb (Tcb);\r
+\r
+    break;\r
+\r
+  case SOCK_DETACH:\r
+\r
+    TcpDetachPcb (Sock);\r
+\r
+    break;\r
+\r
+  case SOCK_CONFIGURE:\r
+\r
+    return TcpConfigurePcb (\r
+            Sock,\r
+            (TCP_CONFIG_DATA *) Data\r
+            );\r
+\r
+    break;\r
+\r
+  case SOCK_MODE:\r
+\r
+    ASSERT ((Data != NULL) && (Tcb != NULL));\r
+\r
+    if (Tcb->Sk->IpVersion == IP_VERSION_4) {\r
+\r
+      return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data);\r
+    } else {\r
+\r
+      return Tcp6GetMode (Tcb, (TCP6_MODE_DATA *) Data);\r
+    }\r
+\r
+    break;\r
+\r
+  case SOCK_ROUTE:\r
+\r
+    ASSERT ((Data != NULL) && (Tcb != NULL) && (Tcb->Sk->IpVersion == IP_VERSION_4));\r
+\r
+    return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data);\r
+\r
+  default:\r
+\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/NetworkPkg/TcpDxe/TcpDriver.c b/NetworkPkg/TcpDxe/TcpDriver.c
new file mode 100644 (file)
index 0000000..37e53af
--- /dev/null
@@ -0,0 +1,891 @@
+/** @file\r
+  The driver binding and service binding protocol for the TCP driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+UINT16                        mTcp4RandomPort;\r
+UINT16                        mTcp6RandomPort;\r
+\r
+TCP_HEARTBEAT_TIMER           mTcpTimer = {\r
+  NULL,\r
+  0\r
+};\r
+\r
+EFI_TCP4_PROTOCOL             gTcp4ProtocolTemplate = {\r
+  Tcp4GetModeData,\r
+  Tcp4Configure,\r
+  Tcp4Routes,\r
+  Tcp4Connect,\r
+  Tcp4Accept,\r
+  Tcp4Transmit,\r
+  Tcp4Receive,\r
+  Tcp4Close,\r
+  Tcp4Cancel,\r
+  Tcp4Poll\r
+};\r
+\r
+EFI_TCP6_PROTOCOL             gTcp6ProtocolTemplate = {\r
+  Tcp6GetModeData,\r
+  Tcp6Configure,\r
+  Tcp6Connect,\r
+  Tcp6Accept,\r
+  Tcp6Transmit,\r
+  Tcp6Receive,\r
+  Tcp6Close,\r
+  Tcp6Cancel,\r
+  Tcp6Poll\r
+};\r
+\r
+SOCK_INIT_DATA                mTcpDefaultSockData = {\r
+  SockStream,\r
+  SO_CLOSED,\r
+  NULL,\r
+  TCP_BACKLOG,\r
+  TCP_SND_BUF_SIZE,\r
+  TCP_RCV_BUF_SIZE,\r
+  IP_VERSION_4,\r
+  NULL,\r
+  TcpCreateSocketCallback,\r
+  TcpDestroySocketCallback,\r
+  NULL,\r
+  NULL,\r
+  0,\r
+  TcpDispatcher,\r
+  NULL,\r
+};\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL   gTcpDriverBinding = {\r
+  TcpDriverBindingSupported,\r
+  TcpDriverBindingStart,\r
+  TcpDriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL  gTcpServiceBinding = {\r
+  TcpServiceBindingCreateChild,\r
+  TcpServiceBindingDestroyChild\r
+};\r
+\r
+\r
+/**\r
+  Create and start the heartbeat timer for the TCP driver.\r
+\r
+  @retval EFI_SUCCESS            The timer was successfully created and started.\r
+  @retval other                  The timer was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpCreateTimer (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  if (mTcpTimer.RefCnt == 0) {\r
+\r
+    Status = gBS->CreateEvent (\r
+                    EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                    TPL_NOTIFY,\r
+                    TcpTicking,\r
+                    NULL,\r
+                    &mTcpTimer.TimerEvent\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+\r
+      Status = gBS->SetTimer (\r
+                      mTcpTimer.TimerEvent,\r
+                      TimerPeriodic,\r
+                      (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ)\r
+                      );\r
+    }\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+\r
+    mTcpTimer.RefCnt++;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Stop and destroy the heartbeat timer for TCP driver.\r
+\r
+**/\r
+VOID\r
+TcpDestroyTimer (\r
+  VOID\r
+  )\r
+{\r
+  ASSERT (mTcpTimer.RefCnt > 0);\r
+\r
+  mTcpTimer.RefCnt--;\r
+\r
+  if (mTcpTimer.RefCnt > 0) {\r
+    return;\r
+  }\r
+\r
+  gBS->SetTimer (mTcpTimer.TimerEvent, TimerCancel, 0);\r
+  gBS->CloseEvent (mTcpTimer.TimerEvent);\r
+  mTcpTimer.TimerEvent = NULL;\r
+}\r
+\r
+/**\r
+  The entry point for Tcp driver, which is used to install Tcp driver on the ImageHandle.\r
+\r
+  @param[in]  ImageHandle   The firmware allocated handle for this driver image.\r
+  @param[in]  SystemTable   Pointer to the EFI system table.\r
+\r
+  @retval EFI_SUCCESS   The driver loaded.\r
+  @retval other         The driver did not load.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverEntryPoint (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT32      Seed;\r
+\r
+  //\r
+  // Install the TCP Driver Binding Protocol\r
+  //\r
+  Status = EfiLibInstallDriverBindingComponentName2 (\r
+             ImageHandle,\r
+             SystemTable,\r
+             &gTcpDriverBinding,\r
+             ImageHandle,\r
+             &gTcpComponentName,\r
+             &gTcpComponentName2\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Initialize ISS and random port.\r
+  //\r
+  Seed            = NetRandomInitSeed ();\r
+  mTcpGlobalIss   = NET_RANDOM (Seed) % mTcpGlobalIss;\r
+  mTcp4RandomPort = (UINT16) (TCP_PORT_KNOWN + (NET_RANDOM (Seed) % TCP_PORT_KNOWN));\r
+  mTcp6RandomPort = mTcp4RandomPort;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Create a new TCP4 or TCP6 driver service binding protocol\r
+\r
+  @param[in]  Controller         Controller handle of device to bind driver to.\r
+  @param[in]  ImageHandle        The TCP driver's image handle.\r
+  @param[in]  IpVersion          IP_VERSION_4 or IP_VERSION_6.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.\r
+  @retval EFI_SUCCESS            A new IP6 service binding private was created.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpCreateService (\r
+  IN EFI_HANDLE  Controller,\r
+  IN EFI_HANDLE  Image,\r
+  IN UINT8       IpVersion\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  EFI_GUID           *IpServiceBindingGuid;\r
+  EFI_GUID           *TcpServiceBindingGuid;\r
+  TCP_SERVICE_DATA   *TcpServiceData;\r
+  IP_IO_OPEN_DATA    OpenData;\r
+\r
+  if (IpVersion == IP_VERSION_4) {\r
+    IpServiceBindingGuid  = &gEfiIp4ServiceBindingProtocolGuid;\r
+    TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+  } else {\r
+    IpServiceBindingGuid  = &gEfiIp6ServiceBindingProtocolGuid;\r
+    TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  TcpServiceBindingGuid,\r
+                  NULL,\r
+                  Image,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  IpServiceBindingGuid,\r
+                  NULL,\r
+                  Image,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Create the TCP service data.\r
+  //\r
+  TcpServiceData = AllocateZeroPool (sizeof (TCP_SERVICE_DATA));\r
+  if (TcpServiceData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TcpServiceData->Signature            = TCP_DRIVER_SIGNATURE;\r
+  TcpServiceData->ControllerHandle     = Controller;\r
+  TcpServiceData->DriverBindingHandle  = Image;\r
+  TcpServiceData->IpVersion            = IpVersion;\r
+  CopyMem (\r
+    &TcpServiceData->ServiceBinding,\r
+    &gTcpServiceBinding,\r
+    sizeof (EFI_SERVICE_BINDING_PROTOCOL)\r
+    );\r
+\r
+  TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion);\r
+  if (TcpServiceData->IpIo == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+\r
+  InitializeListHead (&TcpServiceData->SocketList);\r
+  ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));\r
+\r
+  if (IpVersion == IP_VERSION_4) {\r
+    CopyMem (\r
+      &OpenData.IpConfigData.Ip4CfgData,\r
+      &mIp4IoDefaultIpConfigData,\r
+      sizeof (EFI_IP4_CONFIG_DATA)\r
+      );\r
+    OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;\r
+  } else {\r
+    CopyMem (\r
+      &OpenData.IpConfigData.Ip6CfgData,\r
+      &mIp6IoDefaultIpConfigData,\r
+      sizeof (EFI_IP6_CONFIG_DATA)\r
+      );\r
+    OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;\r
+  }\r
+\r
+  OpenData.PktRcvdNotify  = TcpRxCallback;\r
+  Status                  = IpIoOpen (TcpServiceData->IpIo, &OpenData);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = TcpCreateTimer ();\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Controller,\r
+                  TcpServiceBindingGuid,\r
+                  &TcpServiceData->ServiceBinding,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    TcpDestroyTimer ();\r
+\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  TcpSetVariableData (TcpServiceData);\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  if (TcpServiceData->IpIo != NULL) {\r
+    IpIoDestroy (TcpServiceData->IpIo);\r
+  }\r
+\r
+  FreePool (TcpServiceData);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Destroy a TCP6 or TCP4 service binding instance. It will release all\r
+  the resources allocated by the instance.\r
+\r
+  @param[in]  Controller         Controller handle of device to bind driver to.\r
+  @param[in]  ImageHandle        The TCP driver's image handle.\r
+  @param[in]  NumberOfChildren    Number of Handles in ChildHandleBuffer. If number\r
+                                 of children is zero stop the entire bus driver.\r
+  @param[in]  IpVersion          IP_VERSION_4 or IP_VERSION_6\r
+\r
+  @retval EFI_SUCCESS            The resources used by the instance were cleaned up.\r
+  @retval Others                 Failed to clean up some of the resources.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpDestroyService (\r
+  IN EFI_HANDLE  Controller,\r
+  IN EFI_HANDLE  ImageHandle,\r
+  IN UINTN       NumberOfChildren,\r
+  IN UINT8       IpVersion\r
+  )\r
+{\r
+  EFI_HANDLE                    NicHandle;\r
+  EFI_GUID                      *IpProtocolGuid;\r
+  EFI_GUID                      *ServiceBindingGuid;\r
+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
+  TCP_SERVICE_DATA              *TcpServiceData;\r
+  EFI_STATUS                    Status;\r
+  SOCKET                        *Sock;\r
+\r
+  ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));\r
+\r
+  if (IpVersion == IP_VERSION_4) {\r
+    IpProtocolGuid     = &gEfiIp4ProtocolGuid;\r
+    ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+  } else {\r
+    IpProtocolGuid     = &gEfiIp6ProtocolGuid;\r
+    ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+  }\r
+\r
+  NicHandle = NetLibGetNicHandle (Controller, IpProtocolGuid);\r
+  if (NicHandle == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  NicHandle,\r
+                  ServiceBindingGuid,\r
+                  (VOID **) &ServiceBinding,\r
+                  ImageHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  TcpServiceData = TCP_SERVICE_FROM_THIS (ServiceBinding);\r
+\r
+  if (NumberOfChildren == 0) {\r
+    //\r
+    // Uninstall TCP servicebinding protocol\r
+    //\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           NicHandle,\r
+           ServiceBindingGuid,\r
+           ServiceBinding,\r
+           NULL\r
+           );\r
+\r
+    //\r
+    // Destroy the IpIO consumed by TCP driver\r
+    //\r
+    IpIoDestroy (TcpServiceData->IpIo);\r
+\r
+    //\r
+    // Destroy the heartbeat timer.\r
+    //\r
+    TcpDestroyTimer ();\r
+\r
+    //\r
+    // Clear the variable.\r
+    //\r
+    TcpClearVariableData (TcpServiceData);\r
+\r
+    //\r
+    // Release the TCP service data\r
+    //\r
+    FreePool (TcpServiceData);\r
+  } else {\r
+\r
+    while (!IsListEmpty (&TcpServiceData->SocketList)) {\r
+      Sock = NET_LIST_HEAD (&TcpServiceData->SocketList, SOCKET, Link);\r
+\r
+      ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle.\r
+\r
+  @param[in]  This                Protocol instance pointer.\r
+  @param[in]  ControllerHandle    Handle of device to test.\r
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific\r
+                                  child device to start.\r
+\r
+  @retval EFI_SUCCESS             This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED     This driver is already running on this device.\r
+  @retval other                   This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  BOOLEAN     IsTcp4Started;\r
+\r
+  //\r
+  // Test for the Tcp4ServiceBinding Protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiTcp4ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // Test for the Ip4ServiceBinding Protocol\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    ControllerHandle,\r
+                    &gEfiIp4ServiceBindingProtocolGuid,\r
+                    NULL,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    IsTcp4Started = FALSE;\r
+  } else {\r
+    IsTcp4Started = TRUE;\r
+  }\r
+\r
+  //\r
+  // Check the Tcp6ServiceBinding Protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiTcp6ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // Test for the Ip6ServiceBinding Protocol\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    ControllerHandle,\r
+                    &gEfiIp6ServiceBindingProtocolGuid,\r
+                    NULL,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+  } else if (IsTcp4Started) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  @param[in]  This                   Protocol instance pointer.\r
+  @param[in]  ControllerHandle       Handle of device to bind driver to.\r
+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                     device to start.\r
+\r
+  @retval EFI_SUCCESS            The driver is added to ControllerHandle.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources to start the\r
+                                 driver.\r
+  @retval other                  The driver cannot be added to ControllerHandle.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS  Tcp4Status;\r
+  EFI_STATUS  Tcp6Status;\r
+\r
+  Tcp4Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4);\r
+  if ((Tcp4Status == EFI_ALREADY_STARTED) || (Tcp4Status == EFI_UNSUPPORTED)) {\r
+    Tcp4Status = EFI_SUCCESS;\r
+  }\r
+\r
+  Tcp6Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6);\r
+  if ((Tcp6Status == EFI_ALREADY_STARTED) || (Tcp6Status == EFI_UNSUPPORTED)) {\r
+    Tcp6Status = EFI_SUCCESS;\r
+  }\r
+\r
+  if (!EFI_ERROR (Tcp4Status) || !EFI_ERROR (Tcp6Status)) {\r
+    return EFI_SUCCESS;\r
+  } else if (EFI_ERROR (Tcp4Status)) {\r
+    return Tcp4Status;\r
+  } else {\r
+    return Tcp6Status;\r
+  }\r
+}\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must\r
+                                support a bus specific I/O protocol for the driver\r
+                                to use to stop the device.\r
+  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.\r
+  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL\r
+                                if NumberOfChildren is 0.\r
+\r
+  @retval EFI_SUCCESS           The device was stopped.\r
+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS  Tcp4Status;\r
+  EFI_STATUS  Tcp6Status;\r
+\r
+  Tcp4Status = TcpDestroyService (\r
+                 ControllerHandle,\r
+                 This->DriverBindingHandle,\r
+                 NumberOfChildren,\r
+                 IP_VERSION_4\r
+                 );\r
+\r
+  Tcp6Status = TcpDestroyService (\r
+                 ControllerHandle,\r
+                 This->DriverBindingHandle,\r
+                 NumberOfChildren,\r
+                 IP_VERSION_6\r
+                 );\r
+\r
+  if (EFI_ERROR (Tcp4Status) && EFI_ERROR (Tcp6Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  } else {\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+/**\r
+  The Callback funtion called after the TCP socket was created.\r
+\r
+  @param[in]  This            Pointer to the socket just created\r
+  @param[in]  Context         Context of the socket\r
+\r
+  @retval EFI_SUCCESS         This protocol installed successfully.\r
+  @retval other               An error occured.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpCreateSocketCallback (\r
+  IN SOCKET  *This,\r
+  IN VOID    *Context\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  TCP_SERVICE_DATA  *TcpServiceData;\r
+  EFI_GUID          *IpProtocolGuid;\r
+  VOID              *Ip;\r
+\r
+  if (This->IpVersion == IP_VERSION_4) {\r
+    IpProtocolGuid = &gEfiIp4ProtocolGuid;\r
+  } else {\r
+    IpProtocolGuid = &gEfiIp6ProtocolGuid;\r
+  }\r
+\r
+  TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;\r
+\r
+  //\r
+  // Open the default IP protocol of IP_IO BY_DRIVER.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  TcpServiceData->IpIo->ChildHandle,\r
+                  IpProtocolGuid,\r
+                  &Ip,\r
+                  TcpServiceData->DriverBindingHandle,\r
+                  This->SockHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Open the device path on the handle where service binding resides on.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  TcpServiceData->ControllerHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **) &This->ParentDevicePath,\r
+                  TcpServiceData->DriverBindingHandle,\r
+                  This->SockHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseProtocol (\r
+           TcpServiceData->IpIo->ChildHandle,\r
+           IpProtocolGuid,\r
+           TcpServiceData->DriverBindingHandle,\r
+           This->SockHandle\r
+           );\r
+  } else {\r
+    //\r
+    // Insert this socket into the SocketList.\r
+    //\r
+    InsertTailList (&TcpServiceData->SocketList, &This->Link);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The callback function called before the TCP socket was to be destroyed.\r
+\r
+  @param[in]  This                   The TCP socket to be destroyed.\r
+  @param[in]  Context                The context of the socket.\r
+\r
+**/\r
+VOID\r
+TcpDestroySocketCallback (\r
+  IN SOCKET  *This,\r
+  IN VOID    *Context\r
+  )\r
+{\r
+  TCP_SERVICE_DATA  *TcpServiceData;\r
+  EFI_GUID          *IpProtocolGuid;\r
+\r
+  if (This->IpVersion == IP_VERSION_4) {\r
+    IpProtocolGuid = &gEfiIp4ProtocolGuid;\r
+  } else {\r
+    IpProtocolGuid = &gEfiIp6ProtocolGuid;\r
+  }\r
+\r
+  TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;\r
+\r
+  //\r
+  // Remove this node from the list.\r
+  //\r
+  RemoveEntryList (&This->Link);\r
+\r
+  //\r
+  // Close the device path protocol\r
+  //\r
+  gBS->CloseProtocol (\r
+         TcpServiceData->ControllerHandle,\r
+         &gEfiDevicePathProtocolGuid,\r
+         TcpServiceData->DriverBindingHandle,\r
+         This->SockHandle\r
+         );\r
+\r
+  //\r
+  // Close the IP protocol.\r
+  //\r
+  gBS->CloseProtocol (\r
+         TcpServiceData->IpIo->ChildHandle,\r
+         IpProtocolGuid,\r
+         TcpServiceData->DriverBindingHandle,\r
+         This->SockHandle\r
+         );\r
+}\r
+\r
+/**\r
+  Creates a child handle with a set of TCP services.\r
+\r
+  The CreateChild() function installs a protocol on ChildHandle.\r
+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+  @param[in]      This          Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in, out] ChildHandle   Pointer to the handle of the child to create.\r
+                                If it is NULL, then a new handle is created.\r
+                                If it is a pointer to an existing UEFI handle,\r
+                                then the protocol is added to the existing UEFI handle.\r
+\r
+  @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create\r
+                                the child.\r
+  @retval other                 The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpServiceBindingCreateChild (\r
+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN OUT EFI_HANDLE                    *ChildHandle\r
+  )\r
+{\r
+  SOCKET            *Sock;\r
+  TCP_SERVICE_DATA  *TcpServiceData;\r
+  TCP_PROTO_DATA    TcpProto;\r
+  EFI_STATUS        Status;\r
+  EFI_TPL           OldTpl;\r
+\r
+  if (NULL == This || NULL == ChildHandle) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  Status              = EFI_SUCCESS;\r
+  TcpServiceData      = TCP_SERVICE_FROM_THIS (This);\r
+  TcpProto.TcpService = TcpServiceData;\r
+  TcpProto.TcpPcb     = NULL;\r
+\r
+  //\r
+  // Create a tcp instance with defualt Tcp default\r
+  // sock init data and TcpProto\r
+  //\r
+  mTcpDefaultSockData.ProtoData     = &TcpProto;\r
+  mTcpDefaultSockData.DataSize      = sizeof (TCP_PROTO_DATA);\r
+  mTcpDefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle;\r
+  mTcpDefaultSockData.IpVersion     = TcpServiceData->IpVersion;\r
+\r
+  if (TcpServiceData->IpVersion == IP_VERSION_4) {\r
+    mTcpDefaultSockData.Protocol = &gTcp4ProtocolTemplate;\r
+  } else {\r
+    mTcpDefaultSockData.Protocol = &gTcp6ProtocolTemplate;\r
+  }\r
+\r
+  Sock = SockCreateChild (&mTcpDefaultSockData);\r
+  if (NULL == Sock) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "TcpDriverBindingCreateChild: No resource to create a Tcp Child\n")\r
+      );\r
+\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+  } else {\r
+    *ChildHandle = Sock->SockHandle;\r
+  }\r
+\r
+  mTcpDefaultSockData.ProtoData  = NULL;\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Destroys a child handle with a set of TCP services.\r
+\r
+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+  last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+  @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param  ChildHandle Handle of the child to be destroyed.\r
+\r
+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.\r
+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.\r
+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+  @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle\r
+                                because its services are being used.\r
+  @retval other                 The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  VOID        *Tcp;\r
+  SOCKET      *Sock;\r
+  EFI_TPL     OldTpl;\r
+\r
+  if (NULL == This || NULL == ChildHandle) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // retrieve the Tcp4 protocol from ChildHandle\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ChildHandle,\r
+                  &gEfiTcp4ProtocolGuid,\r
+                  &Tcp,\r
+                  gTcpDriverBinding.DriverBindingHandle,\r
+                  ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // No Tcp4, try the Tcp6 protocol\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    ChildHandle,\r
+                    &gEfiTcp6ProtocolGuid,\r
+                    &Tcp,\r
+                    gTcpDriverBinding.DriverBindingHandle,\r
+                    ChildHandle,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_UNSUPPORTED;\r
+    }\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // destroy this sock and related Tcp protocol control\r
+    // block\r
+    //\r
+    Sock = SOCK_FROM_THIS (Tcp);\r
+\r
+    SockDestroyChild (Sock);\r
+  }\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/TcpDxe/TcpDriver.h b/NetworkPkg/TcpDxe/TcpDriver.h
new file mode 100644 (file)
index 0000000..9de4be6
--- /dev/null
@@ -0,0 +1,230 @@
+/** @file\r
+  The prototype of driver binding and service binding protocol for TCP driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _TCP_DRIVER_H_\r
+#define _TCP_DRIVER_H_\r
+\r
+#define TCP_DRIVER_SIGNATURE   SIGNATURE_32 ('T', 'C', 'P', 'D')\r
+\r
+#define TCP_PORT_KNOWN         1024\r
+#define TCP_PORT_USER_RESERVED 65535\r
+\r
+typedef struct _TCP_HEARTBEAT_TIMER {\r
+  EFI_EVENT  TimerEvent;\r
+  INTN       RefCnt;\r
+} TCP_HEARTBEAT_TIMER;\r
+\r
+typedef struct _TCP_SERVICE_DATA {\r
+  UINT32                        Signature;\r
+  EFI_HANDLE                    ControllerHandle;\r
+  EFI_HANDLE                    DriverBindingHandle;\r
+  UINT8                         IpVersion;\r
+  IP_IO                         *IpIo;\r
+  EFI_SERVICE_BINDING_PROTOCOL  ServiceBinding;\r
+  CHAR16                        *MacString;\r
+  LIST_ENTRY                    SocketList;\r
+} TCP_SERVICE_DATA;\r
+\r
+typedef struct _TCP_PROTO_DATA {\r
+  TCP_SERVICE_DATA  *TcpService;\r
+  TCP_CB            *TcpPcb;\r
+} TCP_PROTO_DATA;\r
+\r
+#define TCP_SERVICE_FROM_THIS(a) \\r
+  CR ( \\r
+  (a), \\r
+  TCP_SERVICE_DATA, \\r
+  ServiceBinding, \\r
+  TCP_DRIVER_SIGNATURE \\r
+  )\r
+\r
+//\r
+// Function prototype for the driver's entry point\r
+//\r
+\r
+/**\r
+  The entry point for Tcp driver, used to install Tcp driver on the ImageHandle.\r
+\r
+  @param[in]  ImageHandle   The firmware allocated handle for this driver image.\r
+  @param[in]  SystemTable   Pointer to the EFI system table.\r
+\r
+  @retval EFI_SUCCESS   The driver loaded.\r
+  @retval other         The driver did not load.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverEntryPoint (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  );\r
+\r
+//\r
+// Function prototypes for the Driver Binding Protocol\r
+//\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle.\r
+\r
+  @param[in]  This                Protocol instance pointer.\r
+  @param[in]  ControllerHandle    Handle of the device to test.\r
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific\r
+                                  child device to start.\r
+\r
+  @retval EFI_SUCCESS             This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED     This driver is already running on this device.\r
+  @retval other                   This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  @param[in]  This                   Protocol instance pointer.\r
+  @param[in]  ControllerHandle       Handle of device to bind driver to.\r
+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                     device to start.\r
+\r
+  @retval EFI_SUCCESS            The driver was added to ControllerHandle.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources to start the\r
+                                 driver.\r
+  @retval other                  The driver cannot be added to ControllerHandle.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must\r
+                                support a bus specific I/O protocol for the driver\r
+                                to use to stop the device.\r
+  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.\r
+  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL\r
+                                if NumberOfChildren is 0.\r
+\r
+  @retval EFI_SUCCESS           The device was stopped.\r
+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL\r
+  );\r
+\r
+/**\r
+  The Callback funtion called after the TCP socket is created.\r
+\r
+  @param[in]  This            Pointer to the socket just created.\r
+  @param[in]  Context         The context of the socket.\r
+\r
+  @retval EFI_SUCCESS         This protocol is installed successfully.\r
+  @retval other               An error occured.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpCreateSocketCallback (\r
+  IN SOCKET  *This,\r
+  IN VOID    *Context\r
+  );\r
+\r
+/**\r
+  The callback function called before the TCP socket is to be destroyed.\r
+\r
+  @param[in]  This                   The TCP socket to be destroyed.\r
+  @param[in]  Context                The context of the socket.\r
+\r
+**/\r
+VOID\r
+TcpDestroySocketCallback (\r
+  IN SOCKET  *This,\r
+  IN VOID    *Context\r
+  );\r
+\r
+//\r
+// Function ptototypes for the ServiceBinding Prococol\r
+//\r
+\r
+/**\r
+  Creates a child handle with a set of TCP services.\r
+\r
+  The CreateChild() function installs a protocol on ChildHandle.\r
+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+  @param[in]      This          Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in, out] ChildHandle   Pointer to the handle of the child to create.\r
+                                If it is NULL, then a new handle is created.\r
+                                If it is a pointer to an existing UEFI handle,\r
+                                then the protocol is added to the existing UEFI handle.\r
+\r
+  @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources available to create\r
+                                the child.\r
+  @retval other                 The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpServiceBindingCreateChild (\r
+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN OUT EFI_HANDLE                    *ChildHandle\r
+  );\r
+\r
+/**\r
+  Destroys a child handle with a set of TCP services.\r
+\r
+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+  last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+  @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param  ChildHandle Handle of the child to destroy.\r
+\r
+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.\r
+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.\r
+  @retval EFI_INVALID_PARAMETER The child handle is not a valid UEFI Handle.\r
+  @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle\r
+                                because its services are being used.\r
+  @retval other                 The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/TcpDxe/TcpDxe.inf b/NetworkPkg/TcpDxe/TcpDxe.inf
new file mode 100644 (file)
index 0000000..6761538
--- /dev/null
@@ -0,0 +1,83 @@
+## @file TcpDxe.inf\r
+#  Component description file for Tcp module.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = TcpDxe\r
+  FILE_GUID                      = 1A7E4468-2F55-4a56-903C-01265EB7622B\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = TcpDriverEntryPoint\r
+  UNLOAD_IMAGE                   = NetLibDefaultUnload\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+  TcpDriver.c\r
+  SockImpl.c\r
+  SockInterface.c\r
+  TcpDispatcher.c\r
+  TcpOutput.c\r
+  TcpMain.c\r
+  SockImpl.h\r
+  TcpMisc.c\r
+  TcpProto.h\r
+  TcpOption.c\r
+  TcpInput.c\r
+  TcpFunc.h\r
+  TcpOption.h\r
+  TcpTimer.c\r
+  TcpMain.h\r
+  Socket.h\r
+  ComponentName.c\r
+  TcpIo.c\r
+  TcpDriver.h\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  BaseMemoryLib\r
+  DevicePathLib\r
+  DebugLib\r
+  MemoryAllocationLib\r
+  UefiLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  UefiRuntimeServicesTableLib\r
+  DpcLib\r
+  NetLib\r
+  IpIoLib\r
+\r
+\r
+[Protocols]\r
+  gEfiDevicePathProtocolGuid                    # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiIp4ProtocolGuid                           # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiIp4ServiceBindingProtocolGuid             # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiTcp4ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiTcp4ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiIp6ProtocolGuid                           # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiIp6ServiceBindingProtocolGuid             # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiTcp6ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiTcp6ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED\r
+\r
diff --git a/NetworkPkg/TcpDxe/TcpFunc.h b/NetworkPkg/TcpDxe/TcpFunc.h
new file mode 100644 (file)
index 0000000..c23ba94
--- /dev/null
@@ -0,0 +1,724 @@
+/** @file\r
+  Declaration of external functions shared in TCP driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _TCP_FUNC_H_\r
+#define _TCP_FUNC_H_\r
+\r
+#include "TcpOption.h"\r
+\r
+#define TCP_COMP_VAL(Min, Max, Default, Val) \\r
+  ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default))\r
+\r
+/**\r
+  Timeout handler prototype.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+typedef\r
+VOID\r
+(*TCP_TIMER_HANDLER) (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+//\r
+// Functions in TcpMisc.c\r
+//\r
+\r
+/**\r
+  Initialize the Tcb locally related members.\r
+\r
+  @param[in, out]  Tcb               Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbLocal (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Initialize the peer related members.\r
+\r
+  @param[in, out]  Tcb    Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Seg    Pointer to the segment that contains the peer's intial information.\r
+  @param[in]       Opt    Pointer to the options announced by the peer.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbPeer (\r
+  IN OUT TCP_CB     *Tcb,\r
+  IN     TCP_SEG    *Seg,\r
+  IN     TCP_OPTION *Opt\r
+  );\r
+\r
+/**\r
+  Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.\r
+\r
+  @param[in]  Addr     Pointer to the IP address needs to match.\r
+  @param[in]  Port     The port number needs to match.\r
+  @param[in]  Version  IP_VERSION_4 indicates TCP is running on IP4 stack.\r
+                       IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+\r
+  @retval     TRUE     The Tcb which matches the <Addr Port> pairs exists.\r
+  @retval     FALSE    Otherwise\r
+\r
+**/\r
+BOOLEAN\r
+TcpFindTcbByPeer (\r
+  IN EFI_IP_ADDRESS  *Addr,\r
+  IN TCP_PORTNO      Port,\r
+  IN UINT8           Version\r
+  );\r
+\r
+/**\r
+  Locate the TCP_CB related to the socket pair.\r
+\r
+  @param[in]  LocalPort      The local port number.\r
+  @param[in]  LocalIp        The local IP address.\r
+  @param[in]  RemotePort     The remote port number.\r
+  @param[in]  RemoteIp       The remote IP address.\r
+  @param[in]  Version        IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+                             IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+  @param[in]  Syn            If TRUE, the listen sockets are searched.\r
+\r
+  @return Pointer to the related TCP_CB. If NULL, no match is found.\r
+\r
+**/\r
+TCP_CB *\r
+TcpLocateTcb (\r
+  IN TCP_PORTNO      LocalPort,\r
+  IN EFI_IP_ADDRESS  *LocalIp,\r
+  IN TCP_PORTNO      RemotePort,\r
+  IN EFI_IP_ADDRESS  *RemoteIp,\r
+  IN UINT8           Version,\r
+  IN BOOLEAN         Syn\r
+  );\r
+\r
+/**\r
+  Insert a Tcb into the proper queue.\r
+\r
+  @param[in]  Tcb               Pointer to the TCP_CB to be inserted.\r
+\r
+  @retval 0                     The Tcb was inserted successfully.\r
+  @retval -1                    An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpInsertTcb (\r
+  IN TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Clone a TCP_CB from Tcb.\r
+\r
+  @param[in]  Tcb                   Pointer to the TCP_CB to be cloned.\r
+\r
+  @return Pointer to the new cloned TCP_CB. If NULL, an error condition occurred.\r
+\r
+**/\r
+TCP_CB *\r
+TcpCloneTcb (\r
+  IN TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Compute an ISS to be used by a new connection.\r
+\r
+  @return The result ISS.\r
+\r
+**/\r
+TCP_SEQNO\r
+TcpGetIss (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Get the local mss.\r
+\r
+  @param[in]  Sock        Pointer to the socket to get mss.\r
+\r
+  @return The mss size.\r
+\r
+**/\r
+UINT16\r
+TcpGetRcvMss (\r
+  IN SOCKET  *Sock\r
+  );\r
+\r
+/**\r
+  Set the Tcb's state.\r
+\r
+  @param[in]  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  State                 The state to be set.\r
+\r
+**/\r
+VOID\r
+TcpSetState (\r
+  IN TCP_CB *Tcb,\r
+  IN UINT8  State\r
+  );\r
+\r
+/**\r
+  Compute the TCP segment's checksum.\r
+\r
+  @param[in]  Nbuf       Pointer to the buffer that contains the TCP segment.\r
+  @param[in]  HeadSum    The checksum value of the fixed part of pseudo header.\r
+\r
+  @return The checksum value.\r
+\r
+**/\r
+UINT16\r
+TcpChecksum (\r
+  IN NET_BUF *Nbuf,\r
+  IN UINT16  HeadSum\r
+  );\r
+\r
+/**\r
+  Translate the information from the head of the received TCP\r
+  segment Nbuf contains, and fill it into a TCP_SEG structure.\r
+\r
+  @param[in]       Tcb           Pointer to the TCP_CB of this TCP instance.\r
+  @param[in, out]  Nbuf          Pointer to the buffer contains the TCP segment.\r
+\r
+  @return Pointer to the TCP_SEG that contains the translated TCP head information.\r
+\r
+**/\r
+TCP_SEG *\r
+TcpFormatNetbuf (\r
+  IN     TCP_CB  *Tcb,\r
+  IN OUT NET_BUF *Nbuf\r
+  );\r
+\r
+/**\r
+  Initialize an active connection,\r
+\r
+  @param[in, out]  Tcb          Pointer to the TCP_CB that wants to initiate a\r
+                                connection.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConnect (\r
+  IN OUT TCP_CB  *Tcb\r
+  );\r
+\r
+/**\r
+  Application has consumed some data, check whether\r
+  to send a window update ack or a delayed ack.\r
+\r
+  @param[in]  Tcb        Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConsume (\r
+  IN TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Initiate the connection close procedure, called when\r
+  applications want to close the connection.\r
+\r
+  @param[in, out]  Tcb          Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppClose (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Check whether the application's newly delivered data can be sent out.\r
+\r
+  @param[in, out]  Tcb          Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @retval 0                     The data has been sent out successfully.\r
+  @retval -1                    The Tcb is not in a state that data is permitted to\r
+                                be sent out.\r
+\r
+**/\r
+INTN\r
+TcpOnAppSend (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Abort the connection by sending a reset segment: called\r
+  when the application wants to abort the connection.\r
+\r
+  @param[in]  Tcb                   Pointer to the TCP_CB of the TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppAbort (\r
+  IN TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Reset the connection related with Tcb.\r
+\r
+  @param[in]  Tcb         Pointer to the TCP_CB of the connection to be reset.\r
+\r
+**/\r
+VOID\r
+TcpResetConnection (\r
+  IN TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Set the Tcp variable data.\r
+\r
+  @param[in]  TcpService        Tcp service data.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.\r
+  @retval other                 Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpSetVariableData (\r
+  IN TCP_SERVICE_DATA  *TcpService\r
+  );\r
+\r
+/**\r
+  Clear the variable and free the resource.\r
+\r
+  @param[in]  TcpService            Tcp service data.\r
+\r
+**/\r
+VOID\r
+TcpClearVariableData (\r
+  IN TCP_SERVICE_DATA  *TcpService\r
+  );\r
+\r
+/**\r
+  Install the device path protocol on the TCP instance.\r
+\r
+  @param[in]  Sock          Pointer to the socket representing the TCP instance.\r
+\r
+  @retval EFI_SUCCESS           The device path protocol installed.\r
+  @retval other                 Failed to install the device path protocol.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpInstallDevicePath (\r
+  IN SOCKET *Sock\r
+  );\r
+\r
+\r
+//\r
+// Functions in TcpOutput.c\r
+//\r
+\r
+/**\r
+  Compute the sequence space left in the old receive window.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @return The sequence space left in the old receive window.\r
+\r
+**/\r
+UINT32\r
+TcpRcvWinOld (\r
+  IN TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Compute the current receive window.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @return The size of the current receive window, in bytes.\r
+\r
+**/\r
+UINT32\r
+TcpRcvWinNow (\r
+  IN TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Get the maximum SndNxt.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @return The sequence number of the maximum SndNxt.\r
+\r
+**/\r
+TCP_SEQNO\r
+TcpGetMaxSndNxt (\r
+  IN TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Compute how much data to send.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Force   If TRUE, ignore the sender's SWS avoidance algorithm\r
+                      and send out data by force.\r
+\r
+  @return The length of the data that can be sent. If 0, no data can be sent.\r
+\r
+**/\r
+UINT32\r
+TcpDataToSend (\r
+  IN TCP_CB *Tcb,\r
+  IN INTN   Force\r
+  );\r
+\r
+/**\r
+  Retransmit the segment from sequence Seq.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Seq     The sequence number of the segment to be retransmitted.\r
+\r
+  @retval 0       The retransmission succeeded.\r
+  @retval -1      An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpRetransmit (\r
+  IN TCP_CB    *Tcb,\r
+  IN TCP_SEQNO Seq\r
+  );\r
+\r
+/**\r
+  Check whether to send data/SYN/FIN and piggyback an ACK.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Force   If TRUE, ignore the sender's SWS avoidance algorithm\r
+                           and send out data by force.\r
+\r
+  @return The number of bytes sent.\r
+\r
+**/\r
+INTN\r
+TcpToSendData (\r
+  IN OUT TCP_CB *Tcb,\r
+  IN     INTN   Force\r
+  );\r
+\r
+/**\r
+  Check whether to send an ACK or delayed ACK.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpToSendAck (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Send an ACK immediately.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSendAck (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Send a zero probe segment. It can be used by keepalive and zero window probe.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @retval 0       The zero probe segment was sent out successfully.\r
+  @retval other   An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpSendZeroProbe (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Send a RESET segment in response to the segment received.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance, may be NULL.\r
+  @param[in]  Head    TCP header of the segment that triggers the reset.\r
+  @param[in]  Len     Length of the segment that triggers the reset.\r
+  @param[in]  Local   Local IP address.\r
+  @param[in]  Remote  Remote peer's IP address.\r
+  @param[in]  Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+                      IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+  @retval     0       A reset is sent or no need to send it.\r
+  @retval     -1      No reset is sent.\r
+\r
+**/\r
+INTN\r
+TcpSendReset (\r
+  IN TCP_CB          *Tcb,\r
+  IN TCP_HEAD        *Head,\r
+  IN INT32           Len,\r
+  IN EFI_IP_ADDRESS  *Local,\r
+  IN EFI_IP_ADDRESS  *Remote,\r
+  IN UINT8           Version\r
+  );\r
+\r
+/**\r
+  Verify that the segment is in good shape.\r
+\r
+  @param[in]  Nbuf    Buffer that contains the segment to be checked.\r
+\r
+  @retval     0       The segment is broken.\r
+  @retval     1       The segment is in good shape.\r
+\r
+**/\r
+INTN\r
+TcpVerifySegment (\r
+  IN NET_BUF *Nbuf\r
+  );\r
+\r
+//\r
+// Functions from TcpInput.c\r
+//\r
+\r
+/**\r
+  Process the received ICMP error messages for TCP.\r
+\r
+  @param[in]  Nbuf     Buffer that contains part of the TCP segment without IP header\r
+                       truncated from the ICMP error packet.\r
+  @param[in]  IcmpErr  The ICMP error code interpreted from an ICMP error packet.\r
+  @param[in]  Src      Source address of the ICMP error message.\r
+  @param[in]  Dst      Destination address of the ICMP error message.\r
+  @param[in]  Version  IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates\r
+                       IP6 stack.\r
+\r
+**/\r
+VOID\r
+TcpIcmpInput (\r
+  IN NET_BUF         *Nbuf,\r
+  IN UINT8           IcmpErr,\r
+  IN EFI_IP_ADDRESS  *Src,\r
+  IN EFI_IP_ADDRESS  *Dst,\r
+  IN UINT8           Version\r
+  );\r
+\r
+/**\r
+  Process the received TCP segments.\r
+\r
+  @param[in]  Nbuf     Buffer that contains received TCP segment without an IP header.\r
+  @param[in]  Src      Source address of the segment, or the peer's IP address.\r
+  @param[in]  Dst      Destination address of the segment, or the local end's IP\r
+                       address.\r
+  @param[in]  Version  IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates\r
+                       IP6 stack.\r
+\r
+  @retval 0        The segment processed successfully. It is either accepted or\r
+                   discarded. But no connection is reset by the segment.\r
+  @retval -1       A connection is reset by the segment.\r
+\r
+**/\r
+INTN\r
+TcpInput (\r
+  IN NET_BUF         *Nbuf,\r
+  IN EFI_IP_ADDRESS  *Src,\r
+  IN EFI_IP_ADDRESS  *Dst,\r
+  IN UINT8           Version\r
+  );\r
+\r
+//\r
+// Functions in TcpTimer.c\r
+//\r
+\r
+/**\r
+  Close the TCP connection.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpClose (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Heart beat timer handler, queues the DPC at TPL_CALLBACK.\r
+\r
+  @param[in]  Event    Timer event signaled, ignored.\r
+  @param[in]  Context  Context of the timer event, ignored.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpTicking (\r
+  IN EFI_EVENT Event,\r
+  IN VOID      *Context\r
+  );\r
+\r
+/**\r
+  Enable a TCP timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Timer    The index of the timer to be enabled.\r
+  @param[in]       TimeOut  The timeout value of this timer.\r
+\r
+**/\r
+VOID\r
+TcpSetTimer (\r
+  IN OUT TCP_CB *Tcb,\r
+  IN     UINT16 Timer,\r
+  IN     UINT32 TimeOut\r
+  );\r
+\r
+/**\r
+  Clear one TCP timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Timer    The index of the timer to be cleared.\r
+\r
+**/\r
+VOID\r
+TcpClearTimer (\r
+  IN OUT TCP_CB *Tcb,\r
+  IN     UINT16 Timer\r
+  );\r
+\r
+/**\r
+  Clear all TCP timers.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpClearAllTimer (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Enable the window prober timer and set the timeout value.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSetProbeTimer (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Enable the keepalive timer and set the timeout value.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSetKeepaliveTimer (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+//\r
+// Functions in TcpIo.c\r
+//\r
+\r
+/**\r
+  Packet receive callback function provided to IP_IO. Used to call\r
+  the proper function to handle the packet received by IP.\r
+\r
+  @param[in] Status        Result of the receive request.\r
+  @param[in] IcmpErr       Valid when Status is EFI_ICMP_ERROR.\r
+  @param[in] NetSession    The IP session for the received packet.\r
+  @param[in] Pkt           Packet received.\r
+  @param[in] Context       The data provided by the user for the received packet when\r
+                           the callback is registered in IP_IO_OPEN_DATA::RcvdContext.\r
+                           This is an optional parameter that may be NULL.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpRxCallback (\r
+  IN EFI_STATUS                       Status,\r
+  IN UINT8                            IcmpErr,\r
+  IN EFI_NET_SESSION_DATA             *NetSession,\r
+  IN NET_BUF                          *Pkt,\r
+  IN VOID                             *Context    OPTIONAL\r
+  );\r
+\r
+/**\r
+  Send the segment to IP via IpIo function.\r
+\r
+  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Nbuf               Pointer to the TCP segment to be sent.\r
+  @param[in]  Src                Source address of the TCP segment.\r
+  @param[in]  Dest               Destination address of the TCP segment.\r
+  @param[in]  Version            IP_VERSION_4 or IP_VERSION_6\r
+\r
+  @retval 0                      The segment was sent out successfully.\r
+  @retval -1                     The segment failed to be sent.\r
+\r
+**/\r
+INTN\r
+TcpSendIpPacket (\r
+  IN TCP_CB          *Tcb,\r
+  IN NET_BUF         *Nbuf,\r
+  IN EFI_IP_ADDRESS  *Src,\r
+  IN EFI_IP_ADDRESS  *Dest,\r
+  IN UINT8           Version\r
+  );\r
+\r
+/**\r
+  Refresh the remote peer's Neighbor Cache State if already exists.\r
+\r
+  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Neighbor           Source address of the TCP segment.\r
+  @param[in]  Timeout            Time in 100-ns units that this entry will remain\r
+                                 in the neighbor cache. A value of zero means that\r
+                                 the entry  is permanent. A value of non-zero means\r
+                                 that the entry is  dynamic and will be deleted\r
+                                 after Timeout.\r
+\r
+  @retval EFI_SUCCESS            Successfully updated the neighbor relationship.\r
+  @retval EFI_NOT_STARTED        The IpIo is not configured.\r
+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.\r
+  @retval EFI_NOT_FOUND          This entry is not in the neighbor table.\r
+\r
+**/\r
+EFI_STATUS\r
+Tcp6RefreshNeighbor (\r
+  IN TCP_CB          *Tcb,\r
+  IN EFI_IP_ADDRESS  *Neighbor,\r
+  IN UINT32          Timeout\r
+  );\r
+\r
+//\r
+// Functions in TcpDispatcher.c\r
+//\r
+\r
+/**\r
+  The procotol handler provided to the socket layer, used to\r
+  dispatch the socket level requests by calling the corresponding\r
+  TCP layer functions.\r
+\r
+  @param[in]  Sock               Pointer to the socket of this TCP instance.\r
+  @param[in]  Request            The code of this operation request.\r
+  @param[in]  Data               Pointer to the operation specific data passed in\r
+                                 together with the operation request. This is an\r
+                                 optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The socket request completed successfully.\r
+  @retval other                  The error status returned by the corresponding TCP\r
+                                 layer function.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpDispatcher (\r
+  IN SOCKET                  *Sock,\r
+  IN UINT8                   Request,\r
+  IN VOID                    *Data    OPTIONAL\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/TcpDxe/TcpInput.c b/NetworkPkg/TcpDxe/TcpInput.c
new file mode 100644 (file)
index 0000000..e63469a
--- /dev/null
@@ -0,0 +1,1592 @@
+/** @file\r
+  TCP input process routines.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+  Check whether the sequence number of the incoming segment is acceptable.\r
+\r
+  @param[in]  Tcb Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Seg Pointer to the incoming segment.\r
+\r
+  @retval 1       The sequence number is acceptable.\r
+  @retval 0       The sequence number is not acceptable.\r
+\r
+**/\r
+INTN\r
+TcpSeqAcceptable (\r
+  IN TCP_CB  *Tcb,\r
+  IN TCP_SEG *Seg\r
+  )\r
+{\r
+  return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) &&\r
+          TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd));\r
+}\r
+\r
+/**\r
+  NewReno fast recovery defined in RFC3782.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Seg      Segment that triggers the fast recovery.\r
+\r
+**/\r
+VOID\r
+TcpFastRecover (\r
+  IN OUT TCP_CB  *Tcb,\r
+  IN     TCP_SEG *Seg\r
+  )\r
+{\r
+  UINT32  FlightSize;\r
+  UINT32  Acked;\r
+\r
+  //\r
+  // Step 1: Three duplicate ACKs and not in fast recovery\r
+  //\r
+  if (Tcb->CongestState != TCP_CONGEST_RECOVER) {\r
+\r
+    //\r
+    // Step 1A: Invoking fast retransmission.\r
+    //\r
+    FlightSize        = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
+\r
+    Tcb->Ssthresh     = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss));\r
+    Tcb->Recover      = Tcb->SndNxt;\r
+\r
+    Tcb->CongestState = TCP_CONGEST_RECOVER;\r
+    TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+\r
+    //\r
+    // Step 2: Entering fast retransmission\r
+    //\r
+    TcpRetransmit (Tcb, Tcb->SndUna);\r
+    Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss;\r
+\r
+    DEBUG (\r
+      (EFI_D_INFO,\r
+      "TcpFastRecover: enter fast retransmission for TCB %p, recover point is %d\n",\r
+      Tcb,\r
+      Tcb->Recover)\r
+      );\r
+    return;\r
+  }\r
+\r
+  //\r
+  // During fast recovery, execute Step 3, 4, 5 of RFC3782\r
+  //\r
+  if (Seg->Ack == Tcb->SndUna) {\r
+\r
+    //\r
+    // Step 3: Fast Recovery,\r
+    // If this is a duplicated ACK, increse Cwnd by SMSS.\r
+    //\r
+\r
+    // Step 4 is skipped here only to be executed later\r
+    // by TcpToSendData\r
+    //\r
+    Tcb->CWnd += Tcb->SndMss;\r
+    DEBUG (\r
+      (EFI_D_INFO,\r
+      "TcpFastRecover: received another duplicated ACK (%d) for TCB %p\n",\r
+      Seg->Ack,\r
+      Tcb)\r
+      );\r
+\r
+  } else {\r
+\r
+    //\r
+    // New data is ACKed, check whether it is a\r
+    // full ACK or partial ACK\r
+    //\r
+    if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) {\r
+\r
+      //\r
+      // Step 5 - Full ACK:\r
+      // deflate the congestion window, and exit fast recovery\r
+      //\r
+      FlightSize        = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
+\r
+      Tcb->CWnd         = MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss);\r
+\r
+      Tcb->CongestState = TCP_CONGEST_OPEN;\r
+      DEBUG (\r
+        (EFI_D_INFO,\r
+        "TcpFastRecover: received a full ACK(%d) for TCB %p, exit fast recovery\n",\r
+        Seg->Ack,\r
+        Tcb)\r
+        );\r
+\r
+    } else {\r
+\r
+      //\r
+      // Step 5 - Partial ACK:\r
+      // fast retransmit the first unacknowledge field\r
+      // , then deflate the CWnd\r
+      //\r
+      TcpRetransmit (Tcb, Seg->Ack);\r
+      Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna);\r
+\r
+      //\r
+      // Deflate the CWnd by the amount of new data\r
+      // ACKed by SEG.ACK. If more than one SMSS data\r
+      // is ACKed, add back SMSS byte to CWnd after\r
+      //\r
+      if (Acked >= Tcb->SndMss) {\r
+        Acked -= Tcb->SndMss;\r
+\r
+      }\r
+\r
+      Tcb->CWnd -= Acked;\r
+\r
+      DEBUG (\r
+        (EFI_D_INFO,\r
+        "TcpFastRecover: received a partial ACK(%d) for TCB %p\n",\r
+        Seg->Ack,\r
+        Tcb)\r
+        );\r
+\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  NewReno fast loss recovery defined in RFC3792.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Seg      Segment that triggers the fast loss recovery.\r
+\r
+**/\r
+VOID\r
+TcpFastLossRecover (\r
+  IN OUT TCP_CB  *Tcb,\r
+  IN     TCP_SEG *Seg\r
+  )\r
+{\r
+  if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {\r
+\r
+    //\r
+    // New data is ACKed, check whether it is a\r
+    // full ACK or partial ACK\r
+    //\r
+    if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) {\r
+\r
+      //\r
+      // Full ACK: exit the loss recovery.\r
+      //\r
+      Tcb->LossTimes    = 0;\r
+      Tcb->CongestState = TCP_CONGEST_OPEN;\r
+\r
+      DEBUG (\r
+        (EFI_D_INFO,\r
+        "TcpFastLossRecover: received a full ACK(%d) for TCB %p\n",\r
+        Seg->Ack,\r
+        Tcb)\r
+        );\r
+\r
+    } else {\r
+\r
+      //\r
+      // Partial ACK:\r
+      // fast retransmit the first unacknowledge field.\r
+      //\r
+      TcpRetransmit (Tcb, Seg->Ack);\r
+      DEBUG (\r
+        (EFI_D_INFO,\r
+        "TcpFastLossRecover: received a partial ACK(%d) for TCB %p\n",\r
+        Seg->Ack,\r
+        Tcb)\r
+        );\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Compute the RTT as specified in RFC2988.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Measure  Currently measured RTT in heartbeats.\r
+\r
+**/\r
+VOID\r
+TcpComputeRtt (\r
+  IN OUT TCP_CB *Tcb,\r
+  IN     UINT32 Measure\r
+  )\r
+{\r
+  INT32 Var;\r
+\r
+  //\r
+  // Step 2.3: Compute the RTO for subsequent RTT measurement.\r
+  //\r
+  if (Tcb->SRtt != 0) {\r
+\r
+    Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT);\r
+\r
+    if (Var < 0) {\r
+      Var = -Var;\r
+    }\r
+\r
+    Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2;\r
+    Tcb->SRtt   = 7 * (Tcb->SRtt >> 3) + Measure;\r
+\r
+  } else {\r
+    //\r
+    // Step 2.2: compute the first RTT measure\r
+    //\r
+    Tcb->SRtt   = Measure << TCP_RTT_SHIFT;\r
+    Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1);\r
+  }\r
+\r
+  Tcb->Rto = (Tcb->SRtt + MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT;\r
+\r
+  //\r
+  // Step 2.4: Limit the RTO to at least 1 second\r
+  // Step 2.5: Limit the RTO to a maxium value that\r
+  // is at least 60 second\r
+  //\r
+  if (Tcb->Rto < TCP_RTO_MIN) {\r
+    Tcb->Rto = TCP_RTO_MIN;\r
+\r
+  } else if (Tcb->Rto > TCP_RTO_MAX) {\r
+    Tcb->Rto = TCP_RTO_MAX;\r
+\r
+  }\r
+\r
+  DEBUG (\r
+    (EFI_D_INFO,\r
+    "TcpComputeRtt: new RTT for TCB %p computed SRTT: %d RTTVAR: %d RTO: %d\n",\r
+    Tcb,\r
+    Tcb->SRtt,\r
+    Tcb->RttVar,\r
+    Tcb->Rto)\r
+    );\r
+\r
+}\r
+\r
+/**\r
+  Trim the data; SYN and FIN to fit into the window defined by Left and Right.\r
+\r
+  @param[in]  Nbuf     The buffer that contains a received TCP segment without an IP header.\r
+  @param[in]  Left     The sequence number of the window's left edge.\r
+  @param[in]  Right    The sequence number of the window's right edge.\r
+\r
+**/\r
+VOID\r
+TcpTrimSegment (\r
+  IN NET_BUF   *Nbuf,\r
+  IN TCP_SEQNO Left,\r
+  IN TCP_SEQNO Right\r
+  )\r
+{\r
+  TCP_SEG   *Seg;\r
+  TCP_SEQNO Urg;\r
+  UINT32    Drop;\r
+\r
+  Seg = TCPSEG_NETBUF (Nbuf);\r
+\r
+  //\r
+  // If the segment is completely out of window,\r
+  // truncate every thing, include SYN and FIN.\r
+  //\r
+  if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) {\r
+\r
+    TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);\r
+    TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);\r
+\r
+    Seg->Seq = Seg->End;\r
+    NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD);\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Adjust the buffer header\r
+  //\r
+  if (TCP_SEQ_LT (Seg->Seq, Left)) {\r
+\r
+    Drop      = TCP_SUB_SEQ (Left, Seg->Seq);\r
+    Urg       = Seg->Seq + Seg->Urg;\r
+    Seg->Seq  = Left;\r
+\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+      TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);\r
+      Drop--;\r
+    }\r
+\r
+    //\r
+    // Adjust the urgent point\r
+    //\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) {\r
+\r
+      if (TCP_SEQ_LT (Urg, Seg->Seq)) {\r
+\r
+        TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);\r
+      } else {\r
+        Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq);\r
+      }\r
+    }\r
+\r
+    if (Drop != 0) {\r
+      NetbufTrim (Nbuf, Drop, NET_BUF_HEAD);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Adjust the buffer tail\r
+  //\r
+  if (TCP_SEQ_GT (Seg->End, Right)) {\r
+\r
+    Drop      = TCP_SUB_SEQ (Seg->End, Right);\r
+    Seg->End  = Right;\r
+\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+      TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);\r
+      Drop--;\r
+    }\r
+\r
+    if (Drop != 0) {\r
+      NetbufTrim (Nbuf, Drop, NET_BUF_TAIL);\r
+    }\r
+  }\r
+\r
+  ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+}\r
+\r
+/**\r
+  Trim off the data outside the tcb's receive window.\r
+\r
+  @param[in]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Nbuf     Pointer to the NET_BUF containing the received tcp segment.\r
+\r
+**/\r
+VOID\r
+TcpTrimInWnd (\r
+  IN TCP_CB  *Tcb,\r
+  IN NET_BUF *Nbuf\r
+  )\r
+{\r
+  TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd);\r
+}\r
+\r
+/**\r
+  Process the data and FIN flag, and check whether to deliver\r
+  data to the socket layer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @retval 0        No error occurred to deliver data.\r
+  @retval -1       An error condition occurred. The proper response is to reset the\r
+                   connection.\r
+\r
+**/\r
+INTN\r
+TcpDeliverData (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  LIST_ENTRY      *Entry;\r
+  NET_BUF         *Nbuf;\r
+  TCP_SEQNO       Seq;\r
+  TCP_SEG         *Seg;\r
+  UINT32          Urgent;\r
+\r
+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
+\r
+  //\r
+  // make sure there is some data queued,\r
+  // and TCP is in a proper state\r
+  //\r
+  if (IsListEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) {\r
+\r
+    return 0;\r
+  }\r
+\r
+  //\r
+  // Deliver data to the socket layer\r
+  //\r
+  Entry = Tcb->RcvQue.ForwardLink;\r
+  Seq   = Tcb->RcvNxt;\r
+\r
+  while (Entry != &Tcb->RcvQue) {\r
+    Nbuf  = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+    Seg   = TCPSEG_NETBUF (Nbuf);\r
+\r
+    ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+    ASSERT (Nbuf->Tcp == NULL);\r
+\r
+    if (TCP_SEQ_GT (Seg->Seq, Seq)) {\r
+      break;\r
+    }\r
+\r
+    Entry       = Entry->ForwardLink;\r
+    Seq         = Seg->End;\r
+    Tcb->RcvNxt = Seq;\r
+\r
+    RemoveEntryList (&Nbuf->List);\r
+\r
+    //\r
+    // RFC793 Eighth step: process FIN in sequence\r
+    //\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+\r
+      //\r
+      // The peer sends to us junky data after FIN,\r
+      // reset the connection.\r
+      //\r
+      if (!IsListEmpty (&Tcb->RcvQue)) {\r
+        DEBUG (\r
+          (EFI_D_ERROR,\r
+          "TcpDeliverData: data received after FIN from peer of TCB %p, reset connection\n",\r
+          Tcb)\r
+          );\r
+\r
+        NetbufFree (Nbuf);\r
+        return -1;\r
+      }\r
+\r
+      DEBUG (\r
+        (EFI_D_INFO,\r
+        "TcpDeliverData: processing FIN from peer of TCB %p\n",\r
+        Tcb)\r
+        );\r
+\r
+      switch (Tcb->State) {\r
+      case TCP_SYN_RCVD:\r
+      case TCP_ESTABLISHED:\r
+\r
+        TcpSetState (Tcb, TCP_CLOSE_WAIT);\r
+        break;\r
+\r
+      case TCP_FIN_WAIT_1:\r
+\r
+        if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
+\r
+          TcpSetState (Tcb, TCP_CLOSING);\r
+          break;\r
+        }\r
+\r
+      //\r
+      // fall through\r
+      //\r
+      case TCP_FIN_WAIT_2:\r
+\r
+        TcpSetState (Tcb, TCP_TIME_WAIT);\r
+        TcpClearAllTimer (Tcb);\r
+\r
+        if (Tcb->TimeWaitTimeout != 0) {\r
+\r
+          TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);\r
+        } else {\r
+\r
+          DEBUG (\r
+            (EFI_D_WARN,\r
+            "Connection closed immediately because app disables TIME_WAIT timer for %p\n",\r
+            Tcb)\r
+            );\r
+\r
+          TcpSendAck (Tcb);\r
+          TcpClose (Tcb);\r
+        }\r
+        break;\r
+\r
+      case TCP_CLOSE_WAIT:\r
+      case TCP_CLOSING:\r
+      case TCP_LAST_ACK:\r
+      case TCP_TIME_WAIT:\r
+        //\r
+        // The peer sends to us junk FIN byte. Discard\r
+        // the buffer then reset the connection\r
+        //\r
+        NetbufFree (Nbuf);\r
+        return -1;\r
+        break;\r
+      default:\r
+        break;\r
+      }\r
+\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+\r
+      Seg->End--;\r
+    }\r
+\r
+    //\r
+    // Don't delay the ack if PUSH flag is on.\r
+    //\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) {\r
+\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+    }\r
+\r
+    if (Nbuf->TotalSize != 0) {\r
+      Urgent = 0;\r
+\r
+      if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&\r
+          TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)\r
+          ) {\r
+\r
+        if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) {\r
+          Urgent = Nbuf->TotalSize;\r
+        } else {\r
+          Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1;\r
+        }\r
+      }\r
+\r
+      SockDataRcvd (Tcb->Sk, Nbuf, Urgent);\r
+    }\r
+\r
+    if (TCP_FIN_RCVD (Tcb->State)) {\r
+\r
+      SockNoMoreData (Tcb->Sk);\r
+    }\r
+\r
+    NetbufFree (Nbuf);\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Store the data into the reassemble queue.\r
+\r
+  @param[in, out]  Tcb   Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Nbuf  Pointer to the buffer containing the data to be queued.\r
+\r
+**/\r
+VOID\r
+TcpQueueData (\r
+  IN OUT TCP_CB  *Tcb,\r
+  IN     NET_BUF *Nbuf\r
+  )\r
+{\r
+  TCP_SEG         *Seg;\r
+  LIST_ENTRY      *Head;\r
+  LIST_ENTRY      *Prev;\r
+  LIST_ENTRY      *Cur;\r
+  NET_BUF         *Node;\r
+\r
+  ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));\r
+\r
+  NET_GET_REF (Nbuf);\r
+\r
+  Seg   = TCPSEG_NETBUF (Nbuf);\r
+  Head  = &Tcb->RcvQue;\r
+\r
+  //\r
+  // Fast path to process normal case. That is,\r
+  // no out-of-order segments are received.\r
+  //\r
+  if (IsListEmpty (Head)) {\r
+\r
+    InsertTailList (Head, &Nbuf->List);\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Find the point to insert the buffer\r
+  //\r
+  for (Prev = Head, Cur = Head->ForwardLink;\r
+       Cur != Head;\r
+       Prev = Cur, Cur = Cur->ForwardLink\r
+       ) {\r
+\r
+    Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+\r
+    if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check whether the current segment overlaps with the\r
+  // previous segment.\r
+  //\r
+  if (Prev != Head) {\r
+    Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);\r
+\r
+    if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) {\r
+\r
+      if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) {\r
+\r
+        NetbufFree (Nbuf);\r
+        return;\r
+      }\r
+\r
+      TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End);\r
+    }\r
+  }\r
+\r
+  InsertHeadList (Prev, &Nbuf->List);\r
+\r
+  TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+\r
+  //\r
+  // Check the segments after the insert point.\r
+  //\r
+  while (Cur != Head) {\r
+    Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+\r
+    if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) {\r
+\r
+      Cur = Cur->ForwardLink;\r
+\r
+      RemoveEntryList (&Node->List);\r
+      NetbufFree (Node);\r
+      continue;\r
+    }\r
+\r
+    if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) {\r
+\r
+      if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) {\r
+\r
+        RemoveEntryList (&Nbuf->List);\r
+        NetbufFree (Nbuf);\r
+        return;\r
+      }\r
+\r
+      TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq);\r
+      break;\r
+    }\r
+\r
+    Cur = Cur->ForwardLink;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Adjust the send queue or the retransmit queue.\r
+\r
+  @param[in]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Ack      The acknowledge seuqence number of the received segment.\r
+\r
+**/\r
+VOID\r
+TcpAdjustSndQue (\r
+  IN TCP_CB    *Tcb,\r
+  IN TCP_SEQNO Ack\r
+  )\r
+{\r
+  LIST_ENTRY      *Head;\r
+  LIST_ENTRY      *Cur;\r
+  NET_BUF         *Node;\r
+  TCP_SEG         *Seg;\r
+\r
+  Head  = &Tcb->SndQue;\r
+  Cur   = Head->ForwardLink;\r
+\r
+  while (Cur != Head) {\r
+    Node  = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+    Seg   = TCPSEG_NETBUF (Node);\r
+\r
+    if (TCP_SEQ_GEQ (Seg->Seq, Ack)) {\r
+      break;\r
+    }\r
+\r
+    //\r
+    // Remove completely ACKed segments\r
+    //\r
+    if (TCP_SEQ_LEQ (Seg->End, Ack)) {\r
+      Cur = Cur->ForwardLink;\r
+\r
+      RemoveEntryList (&Node->List);\r
+      NetbufFree (Node);\r
+      continue;\r
+    }\r
+\r
+    TcpTrimSegment (Node, Ack, Seg->End);\r
+    break;\r
+  }\r
+}\r
+\r
+/**\r
+  Process the received TCP segments.\r
+\r
+  @param[in]  Nbuf     Buffer that contains received a TCP segment without an IP header.\r
+  @param[in]  Src      Source address of the segment, or the peer's IP address.\r
+  @param[in]  Dst      Destination address of the segment, or the local end's IP\r
+                       address.\r
+  @param[in]  Version  IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates\r
+                       IP6 stack.\r
+\r
+  @retval 0        Segment  processed successfully. It is either accepted or\r
+                   discarded. However, no connection is reset by the segment.\r
+  @retval -1       A connection is reset by the segment.\r
+\r
+**/\r
+INTN\r
+TcpInput (\r
+  IN NET_BUF         *Nbuf,\r
+  IN EFI_IP_ADDRESS  *Src,\r
+  IN EFI_IP_ADDRESS  *Dst,\r
+  IN UINT8           Version\r
+  )\r
+{\r
+  TCP_CB      *Tcb;\r
+  TCP_CB      *Parent;\r
+  TCP_OPTION  Option;\r
+  TCP_HEAD    *Head;\r
+  INT32       Len;\r
+  TCP_SEG     *Seg;\r
+  TCP_SEQNO   Right;\r
+  TCP_SEQNO   Urg;\r
+  UINT16      Checksum;\r
+\r
+  ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
+\r
+  NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
+\r
+  Parent  = NULL;\r
+  Tcb     = NULL;\r
+\r
+  Head    = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
+  ASSERT (Head != NULL);\r
+  Len     = Nbuf->TotalSize - (Head->HeadLen << 2);\r
+\r
+  if ((Head->HeadLen < 5) || (Len < 0)) {\r
+\r
+    DEBUG ((EFI_D_INFO, "TcpInput: received an mal-formated packet\n"));\r
+    goto DISCARD;\r
+  }\r
+\r
+  if (Version == IP_VERSION_4) {\r
+    Checksum = NetPseudoHeadChecksum (Src->Addr[0], Dst->Addr[0], 6, 0);\r
+  } else {\r
+    Checksum = NetIp6PseudoHeadChecksum (&Src->v6, &Dst->v6, 6, 0);\r
+  }\r
+\r
+  Checksum = TcpChecksum (Nbuf, Checksum);\r
+\r
+  if (Checksum != 0) {\r
+    DEBUG ((EFI_D_ERROR, "TcpInput: received a checksum error packet\n"));\r
+    goto DISCARD;\r
+  }\r
+\r
+  if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) {\r
+    Len++;\r
+  }\r
+\r
+  if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) {\r
+    Len++;\r
+  }\r
+\r
+  Tcb = TcpLocateTcb (\r
+          Head->DstPort,\r
+          Dst,\r
+          Head->SrcPort,\r
+          Src,\r
+          Version,\r
+          (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)\r
+          );\r
+\r
+  if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) {\r
+    DEBUG ((EFI_D_INFO, "TcpInput: send reset because no TCB find\n"));\r
+\r
+    Tcb = NULL;\r
+    goto SEND_RESET;\r
+  }\r
+\r
+  Seg = TcpFormatNetbuf (Tcb, Nbuf);\r
+\r
+  //\r
+  // RFC1122 recommended reaction to illegal option\r
+  // (in fact, an illegal option length) is reset.\r
+  //\r
+  if (TcpParseOption (Nbuf->Tcp, &Option) == -1) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "TcpInput: reset the peer because of mal-format option for Tcb %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    goto SEND_RESET;\r
+  }\r
+\r
+  //\r
+  // From now on, the segment is headless\r
+  //\r
+  NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD);\r
+  Nbuf->Tcp = NULL;\r
+\r
+  //\r
+  // Process the segment in LISTEN state.\r
+  //\r
+  if (Tcb->State == TCP_LISTEN) {\r
+    //\r
+    // First step: Check RST\r
+    //\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "TcpInput: discard a reset segment for TCB %p in listening\n",\r
+        Tcb)\r
+        );\r
+\r
+      goto DISCARD;\r
+    }\r
+\r
+    //\r
+    // Second step: Check ACK.\r
+    // Any ACK sent to TCP in LISTEN is reseted.\r
+    //\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "TcpInput: send reset because of segment with ACK for TCB %p in listening\n",\r
+        Tcb)\r
+        );\r
+\r
+      goto SEND_RESET;\r
+    }\r
+\r
+    //\r
+    // Third step: Check SYN\r
+    //\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+      //\r
+      // create a child TCB to handle the data\r
+      //\r
+      Parent  = Tcb;\r
+\r
+      Tcb     = TcpCloneTcb (Parent);\r
+      if (Tcb == NULL) {\r
+        DEBUG (\r
+          (EFI_D_ERROR,\r
+          "TcpInput: discard a segment because failed to clone a child for TCB%p\n",\r
+          Tcb)\r
+          );\r
+\r
+        goto DISCARD;\r
+      }\r
+\r
+      DEBUG (\r
+        (EFI_D_INFO,\r
+        "TcpInput: create a child for TCB %p in listening\n",\r
+        Tcb)\r
+        );\r
+\r
+      //\r
+      // init the TCB structure\r
+      //\r
+      IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, Dst);\r
+      IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, Src);\r
+      Tcb->LocalEnd.Port  = Head->DstPort;\r
+      Tcb->RemoteEnd.Port = Head->SrcPort;\r
+\r
+      TcpInitTcbLocal (Tcb);\r
+      TcpInitTcbPeer (Tcb, Seg, &Option);\r
+\r
+      TcpSetState (Tcb, TCP_SYN_RCVD);\r
+      TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);\r
+      TcpTrimInWnd (Tcb, Nbuf);\r
+\r
+      goto StepSix;\r
+    }\r
+\r
+    goto DISCARD;\r
+\r
+  } else if (Tcb->State == TCP_SYN_SENT) {\r
+    //\r
+    // First step: Check ACK bit\r
+    //\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) {\r
+\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "TcpInput: send reset because of wrong ACK received for TCB %p in SYN_SENT\n",\r
+        Tcb)\r
+        );\r
+\r
+      goto SEND_RESET;\r
+    }\r
+\r
+    //\r
+    // Second step: Check RST bit\r
+    //\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
+\r
+      if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+\r
+        DEBUG (\r
+          (EFI_D_WARN,\r
+          "TcpInput: connection reset by peer for TCB %p in SYN_SENT\n",\r
+          Tcb)\r
+          );\r
+\r
+        SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);\r
+        goto DROP_CONNECTION;\r
+      } else {\r
+\r
+        DEBUG (\r
+          (EFI_D_WARN,\r
+          "TcpInput: discard a reset segment because of no ACK for TCB %p in SYN_SENT\n",\r
+          Tcb)\r
+          );\r
+\r
+        goto DISCARD;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Third step: Check security and precedence. Skipped\r
+    //\r
+\r
+    //\r
+    // Fourth step: Check SYN. Pay attention to sitimulatous open\r
+    //\r
+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+\r
+      TcpInitTcbPeer (Tcb, Seg, &Option);\r
+\r
+      if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+\r
+        Tcb->SndUna = Seg->Ack;\r
+      }\r
+\r
+      TcpClearTimer (Tcb, TCP_TIMER_REXMIT);\r
+\r
+      if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) {\r
+\r
+        TcpSetState (Tcb, TCP_ESTABLISHED);\r
+\r
+        TcpClearTimer (Tcb, TCP_TIMER_CONNECT);\r
+        TcpDeliverData (Tcb);\r
+\r
+        if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&\r
+            TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)\r
+            ) {\r
+\r
+          TcpComputeRtt (Tcb, Tcb->RttMeasure);\r
+          TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+        }\r
+\r
+        TcpTrimInWnd (Tcb, Nbuf);\r
+\r
+        TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+\r
+        DEBUG (\r
+          (EFI_D_INFO,\r
+          "TcpInput: connection established for TCB %p in SYN_SENT\n",\r
+          Tcb)\r
+          );\r
+\r
+        goto StepSix;\r
+      } else {\r
+        //\r
+        // Received a SYN segment without ACK, simultanous open.\r
+        //\r
+        TcpSetState (Tcb, TCP_SYN_RCVD);\r
+\r
+        ASSERT (Tcb->SndNxt == Tcb->Iss + 1);\r
+        TcpAdjustSndQue (Tcb, Tcb->SndNxt);\r
+\r
+        TcpTrimInWnd (Tcb, Nbuf);\r
+\r
+        DEBUG (\r
+          (EFI_D_WARN,\r
+          "TcpInput: simultanous open for TCB %p in SYN_SENT\n",\r
+          Tcb)\r
+          );\r
+\r
+        goto StepSix;\r
+      }\r
+    }\r
+\r
+    goto DISCARD;\r
+  }\r
+\r
+  //\r
+  // Process segment in SYN_RCVD or TCP_CONNECTED states\r
+  //\r
+\r
+  //\r
+  // Clear probe timer since the RecvWindow is opened.\r
+  //\r
+  if (Tcb->ProbeTimerOn && (Seg->Wnd != 0)) {\r
+    TcpClearTimer (Tcb, TCP_TIMER_PROBE);\r
+    Tcb->ProbeTimerOn = FALSE;\r
+  }\r
+\r
+  //\r
+  // First step: Check whether SEG.SEQ is acceptable\r
+  //\r
+  if (TcpSeqAcceptable (Tcb, Seg) == 0) {\r
+    DEBUG (\r
+      (EFI_D_WARN,\r
+      "TcpInput: sequence acceptance test failed for segment of TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
+      TcpSendAck (Tcb);\r
+    }\r
+\r
+    goto DISCARD;\r
+  }\r
+\r
+  if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) &&\r
+      (Tcb->RcvWl2 == Seg->End) &&\r
+      !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)\r
+        ) {\r
+\r
+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+  }\r
+\r
+  //\r
+  // Second step: Check the RST\r
+  //\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
+\r
+    DEBUG ((EFI_D_WARN, "TcpInput: connection reset for TCB %p\n", Tcb));\r
+\r
+    if (Tcb->State == TCP_SYN_RCVD) {\r
+\r
+      SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED);\r
+\r
+      //\r
+      // This TCB comes from either a LISTEN TCB,\r
+      // or active open TCB with simultanous open.\r
+      // Do NOT signal user CONNECTION refused\r
+      // if it comes from a LISTEN TCB.\r
+      //\r
+    } else if ((Tcb->State == TCP_ESTABLISHED) ||\r
+             (Tcb->State == TCP_FIN_WAIT_1) ||\r
+             (Tcb->State == TCP_FIN_WAIT_2) ||\r
+             (Tcb->State == TCP_CLOSE_WAIT)\r
+            ) {\r
+\r
+      SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);\r
+\r
+    } else {\r
+    }\r
+\r
+    goto DROP_CONNECTION;\r
+  }\r
+\r
+  //\r
+  // Trim the data and flags.\r
+  //\r
+  TcpTrimInWnd (Tcb, Nbuf);\r
+\r
+  //\r
+  // Third step: Check security and precedence, Ignored\r
+  //\r
+\r
+  //\r
+  // Fourth step: Check the SYN bit.\r
+  //\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_WARN,\r
+      "TcpInput: connection reset because received extra SYN for TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);\r
+    goto RESET_THEN_DROP;\r
+  }\r
+  //\r
+  // Fifth step: Check the ACK\r
+  //\r
+  if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+    DEBUG (\r
+      (EFI_D_WARN,\r
+      "TcpInput: segment discard because of no ACK for connected TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    goto DISCARD;\r
+  } else {\r
+    if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick == 0) {\r
+      Tcp6RefreshNeighbor (Tcb, Src, TCP6_KEEP_NEIGHBOR_TIME * TICKS_PER_SECOND);\r
+      Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK;\r
+    }\r
+  }\r
+\r
+  if (Tcb->State == TCP_SYN_RCVD) {\r
+\r
+    if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) && TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) {\r
+\r
+      Tcb->SndWnd     = Seg->Wnd;\r
+      Tcb->SndWndMax  = MAX (Tcb->SndWnd, Tcb->SndWndMax);\r
+      Tcb->SndWl1     = Seg->Seq;\r
+      Tcb->SndWl2     = Seg->Ack;\r
+      TcpSetState (Tcb, TCP_ESTABLISHED);\r
+\r
+      TcpClearTimer (Tcb, TCP_TIMER_CONNECT);\r
+      TcpDeliverData (Tcb);\r
+\r
+      DEBUG (\r
+        (EFI_D_INFO,\r
+        "TcpInput: connection established  for TCB %p in SYN_RCVD\n",\r
+        Tcb)\r
+        );\r
+\r
+      //\r
+      // Continue the process as ESTABLISHED state\r
+      //\r
+    } else {\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "TcpInput: send reset because of wrong ACK for TCB %p in SYN_RCVD\n",\r
+        Tcb)\r
+        );\r
+\r
+      goto SEND_RESET;\r
+    }\r
+  }\r
+\r
+  if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_WARN,\r
+      "TcpInput: ignore the out-of-data ACK for connected TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    goto StepSix;\r
+\r
+  } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_WARN,\r
+      "TcpInput: discard segment for future ACK for connected TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    TcpSendAck (Tcb);\r
+    goto DISCARD;\r
+  }\r
+\r
+  //\r
+  // From now on: SND.UNA <= SEG.ACK <= SND.NXT.\r
+  //\r
+  if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) {\r
+    //\r
+    // update TsRecent as specified in page 16 RFC1323.\r
+    // RcvWl2 equals to the variable "LastAckSent"\r
+    // defined there.\r
+    //\r
+    if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) && TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) {\r
+\r
+      Tcb->TsRecent     = Option.TSVal;\r
+      Tcb->TsRecentAge  = mTcpTick;\r
+    }\r
+\r
+    TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr));\r
+\r
+  } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
+\r
+    ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN);\r
+\r
+    TcpComputeRtt (Tcb, Tcb->RttMeasure);\r
+    TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+  }\r
+\r
+  if (Seg->Ack == Tcb->SndNxt) {\r
+\r
+    TcpClearTimer (Tcb, TCP_TIMER_REXMIT);\r
+  } else {\r
+\r
+    TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
+  }\r
+\r
+  //\r
+  // Count duplicate acks.\r
+  //\r
+  if ((Seg->Ack == Tcb->SndUna) &&\r
+      (Tcb->SndUna != Tcb->SndNxt) &&\r
+      (Seg->Wnd == Tcb->SndWnd) &&\r
+      (0 == Len)\r
+      ) {\r
+\r
+    Tcb->DupAck++;\r
+  } else {\r
+\r
+    Tcb->DupAck = 0;\r
+  }\r
+\r
+  //\r
+  // Congestion avoidance, fast recovery and fast retransmission.\r
+  //\r
+  if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) ||\r
+      (Tcb->CongestState == TCP_CONGEST_LOSS)\r
+      ) {\r
+\r
+    if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {\r
+\r
+      if (Tcb->CWnd < Tcb->Ssthresh) {\r
+\r
+        Tcb->CWnd += Tcb->SndMss;\r
+      } else {\r
+\r
+        Tcb->CWnd += MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1);\r
+      }\r
+\r
+      Tcb->CWnd = MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale);\r
+    }\r
+\r
+    if (Tcb->CongestState == TCP_CONGEST_LOSS) {\r
+      TcpFastLossRecover (Tcb, Seg);\r
+    }\r
+  } else {\r
+\r
+    TcpFastRecover (Tcb, Seg);\r
+  }\r
+\r
+  if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {\r
+\r
+    TcpAdjustSndQue (Tcb, Seg->Ack);\r
+    Tcb->SndUna = Seg->Ack;\r
+\r
+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&\r
+        TCP_SEQ_LT (Tcb->SndUp, Seg->Ack)\r
+        ) {\r
+\r
+      TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Update window info\r
+  //\r
+  if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) ||\r
+      ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))\r
+      ) {\r
+\r
+    Right = Seg->Ack + Seg->Wnd;\r
+\r
+    if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) {\r
+\r
+      if ((Tcb->SndWl1 == Seg->Seq) &&\r
+          (Tcb->SndWl2 == Seg->Ack) &&\r
+          (Len == 0)\r
+          ) {\r
+\r
+        goto NO_UPDATE;\r
+      }\r
+\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "TcpInput: peer shrinks the window for connected TCB %p\n",\r
+        Tcb)\r
+        );\r
+\r
+      if ((Tcb->CongestState == TCP_CONGEST_RECOVER) && (TCP_SEQ_LT (Right, Tcb->Recover))) {\r
+\r
+        Tcb->Recover = Right;\r
+      }\r
+\r
+      if ((Tcb->CongestState == TCP_CONGEST_LOSS) && (TCP_SEQ_LT (Right, Tcb->LossRecover))) {\r
+\r
+        Tcb->LossRecover = Right;\r
+      }\r
+\r
+      if (TCP_SEQ_LT (Right, Tcb->SndNxt)) {\r
+\r
+        Tcb->SndNxt = Right;\r
+\r
+        if (Right == Tcb->SndUna) {\r
+\r
+          TcpClearTimer (Tcb, TCP_TIMER_REXMIT);\r
+          TcpSetProbeTimer (Tcb);\r
+        }\r
+      }\r
+    }\r
+\r
+    Tcb->SndWnd     = Seg->Wnd;\r
+    Tcb->SndWndMax  = MAX (Tcb->SndWnd, Tcb->SndWndMax);\r
+    Tcb->SndWl1     = Seg->Seq;\r
+    Tcb->SndWl2     = Seg->Ack;\r
+  }\r
+\r
+NO_UPDATE:\r
+\r
+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) && (Tcb->SndUna == Tcb->SndNxt)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_INFO,\r
+      "TcpInput: local FIN is ACKed by peer for connected TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED);\r
+  }\r
+\r
+  //\r
+  // Transit the state if proper.\r
+  //\r
+  switch (Tcb->State) {\r
+  case TCP_FIN_WAIT_1:\r
+\r
+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
+\r
+      TcpSetState (Tcb, TCP_FIN_WAIT_2);\r
+\r
+      TcpClearAllTimer (Tcb);\r
+      TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout);\r
+    }\r
+\r
+  case TCP_FIN_WAIT_2:\r
+\r
+    break;\r
+\r
+  case TCP_CLOSE_WAIT:\r
+    break;\r
+\r
+  case TCP_CLOSING:\r
+\r
+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
+\r
+      TcpSetState (Tcb, TCP_TIME_WAIT);\r
+\r
+      TcpClearAllTimer (Tcb);\r
+\r
+      if (Tcb->TimeWaitTimeout != 0) {\r
+\r
+        TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);\r
+      } else {\r
+\r
+        DEBUG (\r
+          (EFI_D_WARN,\r
+          "Connection closed immediately because app disables TIME_WAIT timer for %p\n",\r
+          Tcb)\r
+          );\r
+\r
+        TcpClose (Tcb);\r
+      }\r
+    }\r
+    break;\r
+\r
+  case TCP_LAST_ACK:\r
+\r
+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
+\r
+      TcpSetState (Tcb, TCP_CLOSED);\r
+    }\r
+\r
+    break;\r
+\r
+  case TCP_TIME_WAIT:\r
+\r
+    TcpSendAck (Tcb);\r
+\r
+    if (Tcb->TimeWaitTimeout != 0) {\r
+\r
+      TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);\r
+    } else {\r
+\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "Connection closed immediately because app disables TIME_WAIT timer for %p\n",\r
+        Tcb)\r
+        );\r
+\r
+      TcpClose (Tcb);\r
+    }\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+  //\r
+  // Sixth step: Check the URG bit.update the Urg point\r
+  // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact.\r
+  //\r
+StepSix:\r
+\r
+  Tcb->Idle = 0;\r
+  TcpSetKeepaliveTimer (Tcb);\r
+\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) && !TCP_FIN_RCVD (Tcb->State)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_INFO,\r
+      "TcpInput: received urgent data from peer for connected TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    Urg = Seg->Seq + Seg->Urg;\r
+\r
+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && TCP_SEQ_GT (Urg, Tcb->RcvUp)) {\r
+\r
+      Tcb->RcvUp = Urg;\r
+    } else {\r
+\r
+      Tcb->RcvUp = Urg;\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG);\r
+    }\r
+  }\r
+  //\r
+  // Seventh step: Process the segment data\r
+  //\r
+  if (Seg->End != Seg->Seq) {\r
+\r
+    if (TCP_FIN_RCVD (Tcb->State)) {\r
+\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "TcpInput: connection reset because data is lost for connected TCB %p\n",\r
+        Tcb)\r
+        );\r
+\r
+      goto RESET_THEN_DROP;\r
+    }\r
+\r
+    if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) {\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "TcpInput: connection reset because data is lost for connected TCB %p\n",\r
+        Tcb)\r
+        );\r
+\r
+      goto RESET_THEN_DROP;\r
+    }\r
+\r
+    TcpQueueData (Tcb, Nbuf);\r
+    if (TcpDeliverData (Tcb) == -1) {\r
+      goto RESET_THEN_DROP;\r
+    }\r
+\r
+    if (!IsListEmpty (&Tcb->RcvQue)) {\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Eighth step: check the FIN.\r
+  // This step is moved to TcpDeliverData. FIN will be\r
+  // processed in sequence there. Check the comments in\r
+  // the beginning of the file header for information.\r
+  //\r
+\r
+  //\r
+  // Tcb is a new child of the listening Parent,\r
+  // commit it.\r
+  //\r
+  if (Parent != NULL) {\r
+    Tcb->Parent = Parent;\r
+    TcpInsertTcb (Tcb);\r
+  }\r
+\r
+  if ((Tcb->State != TCP_CLOSED) &&\r
+      (TcpToSendData (Tcb, 0) == 0) &&\r
+      (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Nbuf->TotalSize != 0))\r
+      ) {\r
+\r
+    TcpToSendAck (Tcb);\r
+  }\r
+\r
+  NetbufFree (Nbuf);\r
+  return 0;\r
+\r
+RESET_THEN_DROP:\r
+  TcpSendReset (Tcb, Head, Len, Dst, Src, Version);\r
+\r
+DROP_CONNECTION:\r
+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
+\r
+  NetbufFree (Nbuf);\r
+  TcpClose (Tcb);\r
+\r
+  return -1;\r
+\r
+SEND_RESET:\r
+\r
+  TcpSendReset (Tcb, Head, Len, Dst, Src, Version);\r
+\r
+DISCARD:\r
+\r
+  //\r
+  // Tcb is a child of Parent, and it doesn't survive\r
+  //\r
+  DEBUG ((EFI_D_WARN, "TcpInput: Discard a packet\n"));\r
+  NetbufFree (Nbuf);\r
+\r
+  if ((Parent != NULL) && (Tcb != NULL)) {\r
+\r
+    ASSERT (Tcb->Sk != NULL);\r
+    TcpClose (Tcb);\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Process the received ICMP error messages for TCP.\r
+\r
+  @param[in]  Nbuf     The buffer that contains part of the TCP segment without an IP header\r
+                       truncated from the ICMP error packet.\r
+  @param[in]  IcmpErr  The ICMP error code interpreted from an ICMP error packet.\r
+  @param[in]  Src      Source address of the ICMP error message.\r
+  @param[in]  Dst      Destination address of the ICMP error message.\r
+  @param[in]  Version  IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates\r
+                       IP6 stack.\r
+\r
+**/\r
+VOID\r
+TcpIcmpInput (\r
+  IN NET_BUF         *Nbuf,\r
+  IN UINT8           IcmpErr,\r
+  IN EFI_IP_ADDRESS  *Src,\r
+  IN EFI_IP_ADDRESS  *Dst,\r
+  IN UINT8           Version\r
+  )\r
+{\r
+  TCP_HEAD         *Head;\r
+  TCP_CB           *Tcb;\r
+  TCP_SEQNO        Seq;\r
+  EFI_STATUS       IcmpErrStatus;\r
+  BOOLEAN          IcmpErrIsHard;\r
+  BOOLEAN          IcmpErrNotify;\r
+\r
+  Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
+  ASSERT (Head != NULL);\r
+\r
+  Tcb = TcpLocateTcb (\r
+          Head->DstPort,\r
+          Dst,\r
+          Head->SrcPort,\r
+          Src,\r
+          Version,\r
+          FALSE\r
+          );\r
+  if (Tcb == NULL || Tcb->State == TCP_CLOSED) {\r
+\r
+    goto CLEAN_EXIT;\r
+  }\r
+\r
+  //\r
+  // Validate the sequence number.\r
+  //\r
+  Seq = NTOHL (Head->Seq);\r
+  if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) {\r
+\r
+    goto CLEAN_EXIT;\r
+  }\r
+\r
+  IcmpErrStatus = IpIoGetIcmpErrStatus (IcmpErr, Tcb->Sk->IpVersion, &IcmpErrIsHard, &IcmpErrNotify);\r
+\r
+  if (IcmpErrNotify) {\r
+\r
+    SOCK_ERROR (Tcb->Sk, IcmpErrStatus);\r
+  }\r
+\r
+  if (IcmpErrIsHard) {\r
+\r
+    TcpClose (Tcb);\r
+  }\r
+\r
+CLEAN_EXIT:\r
+\r
+  NetbufFree (Nbuf);\r
+}\r
diff --git a/NetworkPkg/TcpDxe/TcpIo.c b/NetworkPkg/TcpDxe/TcpIo.c
new file mode 100644 (file)
index 0000000..cecb6d1
--- /dev/null
@@ -0,0 +1,190 @@
+/** @file\r
+  Implementation of I/O interfaces between TCP and IpIoLib.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+  Packet receive callback function provided to IP_IO, used to call\r
+  the proper function to handle the packet received by IP.\r
+\r
+  @param[in] Status        Result of the receive request.\r
+  @param[in] IcmpErr       Valid when Status is EFI_ICMP_ERROR.\r
+  @param[in] NetSession    The IP session for the received packet.\r
+  @param[in] Pkt           Packet received.\r
+  @param[in] Context       The data provided by the user for the received packet when\r
+                           the callback is registered in IP_IO_OPEN_DATA::RcvdContext.\r
+                           This is an optional parameter that may be NULL.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpRxCallback (\r
+  IN EFI_STATUS                       Status,\r
+  IN UINT8                            IcmpErr,\r
+  IN EFI_NET_SESSION_DATA             *NetSession,\r
+  IN NET_BUF                          *Pkt,\r
+  IN VOID                             *Context    OPTIONAL\r
+  )\r
+{\r
+  if (EFI_SUCCESS == Status) {\r
+    TcpInput (Pkt, &NetSession->Source, &NetSession->Dest, NetSession->IpVersion);\r
+  } else {\r
+    TcpIcmpInput (\r
+      Pkt,\r
+      IcmpErr,\r
+      &NetSession->Source,\r
+      &NetSession->Dest,\r
+      NetSession->IpVersion\r
+      );\r
+  }\r
+}\r
+\r
+/**\r
+  Send the segment to IP via IpIo function.\r
+\r
+  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Nbuf               Pointer to the TCP segment to be sent.\r
+  @param[in]  Src                Source address of the TCP segment.\r
+  @param[in]  Dest               Destination address of the TCP segment.\r
+  @param[in]  Version            IP_VERSION_4 or IP_VERSION_6\r
+\r
+  @retval 0                      The segment was sent out successfully.\r
+  @retval -1                     The segment failed to send.\r
+\r
+**/\r
+INTN\r
+TcpSendIpPacket (\r
+  IN TCP_CB          *Tcb,\r
+  IN NET_BUF         *Nbuf,\r
+  IN EFI_IP_ADDRESS  *Src,\r
+  IN EFI_IP_ADDRESS  *Dest,\r
+  IN UINT8           Version\r
+  )\r
+{\r
+  EFI_STATUS       Status;\r
+  IP_IO            *IpIo;\r
+  IP_IO_OVERRIDE   Override;\r
+  SOCKET           *Sock;\r
+  VOID             *IpSender;\r
+  TCP_PROTO_DATA  *TcpProto;\r
+\r
+  if (NULL == Tcb) {\r
+\r
+    IpIo     = NULL;\r
+    IpSender = IpIoFindSender (&IpIo, Version, Src);\r
+\r
+    if (IpSender == NULL) {\r
+      DEBUG ((EFI_D_WARN, "TcpSendIpPacket: No appropriate IpSender.\n"));\r
+      return -1;\r
+    }\r
+\r
+    if (Version == IP_VERSION_6) {\r
+      //\r
+      // It's tricky here. EFI IPv6 Spec don't allow an instance overriding the\r
+      // destination address if the dest is already specified through the\r
+      // configuration data. Here we get the IpIo we need and use the default IP\r
+      // instance in this IpIo to send the packet. The dest address is configured\r
+      // to be the unspecified address for the default IP instance.\r
+      //\r
+      IpSender = NULL;\r
+    }\r
+  } else {\r
+\r
+    Sock     = Tcb->Sk;\r
+    TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+    IpIo     = TcpProto->TcpService->IpIo;\r
+    IpSender = Tcb->IpInfo;\r
+\r
+    if (Version == IP_VERSION_6) {\r
+      //\r
+      // It's IPv6 and this TCP segment belongs to a solid TCB, in such case\r
+      // the destination address can't be overridden, so reset the Dest to NULL.\r
+      //\r
+      Dest = NULL;\r
+    }\r
+  }\r
+\r
+  ASSERT (Version == IpIo->IpVersion);\r
+\r
+  if (Version == IP_VERSION_4) {\r
+    Override.Ip4OverrideData.TypeOfService = 0;\r
+    Override.Ip4OverrideData.TimeToLive    = 255;\r
+    Override.Ip4OverrideData.DoNotFragment = FALSE;\r
+    Override.Ip4OverrideData.Protocol      = EFI_IP_PROTO_TCP;\r
+    ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Override.Ip4OverrideData.SourceAddress, Src, sizeof (EFI_IPv4_ADDRESS));\r
+  } else {\r
+    Override.Ip6OverrideData.Protocol  = EFI_IP_PROTO_TCP;\r
+    Override.Ip6OverrideData.HopLimit  = 255;\r
+    Override.Ip6OverrideData.FlowLabel = 0;\r
+  }\r
+\r
+  Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "TcpSendIpPacket: return %r error\n", Status));\r
+    return -1;\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Refresh the remote peer's Neighbor Cache State if already exists.\r
+\r
+  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Neighbor           Source address of the TCP segment.\r
+  @param[in]  Timeout            Time in 100-ns units that this entry will remain\r
+                                 in the neighbor cache. A value of zero means that\r
+                                 the entry  is permanent. A value of non-zero means\r
+                                 that the entry is dynamic and will be deleted\r
+                                 after Timeout.\r
+\r
+  @retval EFI_SUCCESS            Successfully updated the neighbor relationship.\r
+  @retval EFI_NOT_STARTED        The IpIo is not configured.\r
+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.\r
+  @retval EFI_NOT_FOUND          This entry is not in the neighbor table.\r
+\r
+**/\r
+EFI_STATUS\r
+Tcp6RefreshNeighbor (\r
+  IN TCP_CB          *Tcb,\r
+  IN EFI_IP_ADDRESS  *Neighbor,\r
+  IN UINT32          Timeout\r
+  )\r
+{\r
+  IP_IO            *IpIo;\r
+  SOCKET           *Sock;\r
+  TCP_PROTO_DATA  *TcpProto;\r
+\r
+  if (NULL == Tcb) {\r
+    IpIo = NULL;\r
+    IpIoFindSender (&IpIo, IP_VERSION_6, Neighbor);\r
+\r
+    if (IpIo == NULL) {\r
+      DEBUG ((EFI_D_WARN, "Tcp6AddNeighbor: No appropriate IpIo.\n"));\r
+      return EFI_NOT_STARTED;\r
+    }\r
+\r
+  } else {\r
+    Sock     = Tcb->Sk;\r
+    TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+    IpIo     = TcpProto->TcpService->IpIo;\r
+  }\r
+\r
+  return IpIoRefreshNeighbor (IpIo, Neighbor, Timeout);\r
+}\r
+\r
diff --git a/NetworkPkg/TcpDxe/TcpMain.c b/NetworkPkg/TcpDxe/TcpMain.c
new file mode 100644 (file)
index 0000000..55a6d5b
--- /dev/null
@@ -0,0 +1,1074 @@
+/** @file\r
+  Implementation of EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+  Check the integrity of the data buffer.\r
+\r
+  @param[in]  DataLen              The total length of the data buffer.\r
+  @param[in]  FragmentCount        The fragment count of the fragment table.\r
+  @param[in]  FragmentTable        Pointer to the fragment table of the data\r
+                                   buffer.\r
+\r
+  @retval EFI_SUCCESS              The integrity check passed.\r
+  @retval EFI_INVALID_PARAMETER    The integrity check failed.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpChkDataBuf (\r
+  IN UINT32                 DataLen,\r
+  IN UINT32                 FragmentCount,\r
+  IN EFI_TCP4_FRAGMENT_DATA *FragmentTable\r
+  )\r
+{\r
+  UINT32 Index;\r
+\r
+  UINT32 Len;\r
+\r
+  for (Index = 0, Len = 0; Index < FragmentCount; Index++) {\r
+    Len = Len + FragmentTable[Index].FragmentLength;\r
+  }\r
+\r
+  if (DataLen != Len) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get the current operational status.\r
+\r
+  @param[in]   This                Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[out]  Tcp4State           Pointer to the buffer to receive the current TCP\r
+                                   state. Optional parameter that may be NULL.\r
+  @param[out]  Tcp4ConfigData      Pointer to the buffer to receive the current TCP\r
+                                   configuration. Optional parameter that may be NULL.\r
+  @param[out]  Ip4ModeData         Pointer to the buffer to receive the current\r
+                                   IPv4 configuration. Optional parameter that may be NULL.\r
+  @param[out]  MnpConfigData       Pointer to the buffer to receive the current MNP\r
+                                   configuration data indirectly used by the TCPv4\r
+                                   Instance. Optional parameter that may be NULL.\r
+  @param[out]  SnpModeData         Pointer to the buffer to receive the current SNP\r
+                                   configuration data indirectly used by the TCPv4\r
+                                   Instance. Optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS              The mode data was read.\r
+  @retval EFI_NOT_STARTED          No configuration data is available because this\r
+                                   instance hasn't been started.\r
+  @retval EFI_INVALID_PARAMETER    This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4GetModeData (\r
+  IN CONST  EFI_TCP4_PROTOCOL                  *This,\r
+  OUT       EFI_TCP4_CONNECTION_STATE          *Tcp4State      OPTIONAL,\r
+  OUT       EFI_TCP4_CONFIG_DATA               *Tcp4ConfigData OPTIONAL,\r
+  OUT       EFI_IP4_MODE_DATA                  *Ip4ModeData    OPTIONAL,\r
+  OUT       EFI_MANAGED_NETWORK_CONFIG_DATA    *MnpConfigData  OPTIONAL,\r
+  OUT       EFI_SIMPLE_NETWORK_MODE            *SnpModeData    OPTIONAL\r
+  )\r
+{\r
+  TCP4_MODE_DATA  TcpMode;\r
+  SOCKET          *Sock;\r
+\r
+  if (NULL == This) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock                    = SOCK_FROM_THIS (This);\r
+\r
+  TcpMode.Tcp4State       = Tcp4State;\r
+  TcpMode.Tcp4ConfigData  = Tcp4ConfigData;\r
+  TcpMode.Ip4ModeData     = Ip4ModeData;\r
+  TcpMode.MnpConfigData   = MnpConfigData;\r
+  TcpMode.SnpModeData     = SnpModeData;\r
+\r
+  return SockGetMode (Sock, &TcpMode);\r
+}\r
+\r
+/**\r
+  Initialize or brutally reset the operational parameters for\r
+  this EFI TCPv4 instance.\r
+\r
+  @param[in]   This                Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]   TcpConfigData       Pointer to the configure data to configure the\r
+                                   instance. Optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS              The operational settings were set, changed, or\r
+                                   reset successfully.\r
+  @retval EFI_NO_MAPPING           When using a default address, configuration\r
+                                   (through DHCP, BOOTP, RARP, etc.) is not\r
+                                   finished.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_ACCESS_DENIED        Configuring TCP instance when it is already\r
+                                   configured.\r
+  @retval EFI_DEVICE_ERROR         An unexpected network or system error occurred.\r
+  @retval EFI_UNSUPPORTED          One or more of the control options are not\r
+                                   supported in the implementation.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough system resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Configure (\r
+  IN EFI_TCP4_PROTOCOL        * This,\r
+  IN EFI_TCP4_CONFIG_DATA     * TcpConfigData OPTIONAL\r
+  )\r
+{\r
+  EFI_TCP4_OPTION  *Option;\r
+  SOCKET           *Sock;\r
+  EFI_STATUS       Status;\r
+  IP4_ADDR         Ip;\r
+  IP4_ADDR         SubnetMask;\r
+\r
+  if (NULL == This) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Tcp protocol related parameter check will be conducted here\r
+  //\r
+  if (NULL != TcpConfigData) {\r
+\r
+    CopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));\r
+    if ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (TcpConfigData->AccessPoint.ActiveFlag && (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (!TcpConfigData->AccessPoint.UseDefaultAddress) {\r
+\r
+      CopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR));\r
+      CopyMem (&SubnetMask, &TcpConfigData->AccessPoint.SubnetMask, sizeof (IP4_ADDR));\r
+      if (!NetIp4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (SubnetMask))) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+\r
+    Option = TcpConfigData->ControlOption;\r
+    if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  if (NULL == TcpConfigData) {\r
+    return SockFlush (Sock);\r
+  }\r
+\r
+  Status = SockConfigure (Sock, TcpConfigData);\r
+\r
+  if (EFI_NO_MAPPING == Status) {\r
+    Sock->ConfigureState = SO_NO_MAPPING;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Add or delete routing entries.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  DeleteRoute          If TRUE, delete the specified route from routing\r
+                                   table; if FALSE, add the specified route to\r
+                                   routing table.\r
+  @param[in]  SubnetAddress        The destination network.\r
+  @param[in]  SubnetMask           The subnet mask for the destination network.\r
+  @param[in]  GatewayAddress       The gateway address for this route.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance has not been\r
+                                   configured.\r
+  @retval EFI_NO_MAPPING           When using a default address, configuration\r
+                                   (through DHCP, BOOTP, RARP, etc.) is not\r
+                                   finished.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to add the\r
+                                   entry to the routing table.\r
+  @retval EFI_NOT_FOUND            This route is not in the routing table.\r
+  @retval EFI_ACCESS_DENIED        This route is already in the routing table.\r
+  @retval EFI_UNSUPPORTED          The TCP driver does not support this operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Routes (\r
+  IN EFI_TCP4_PROTOCOL           *This,\r
+  IN BOOLEAN                     DeleteRoute,\r
+  IN EFI_IPv4_ADDRESS            *SubnetAddress,\r
+  IN EFI_IPv4_ADDRESS            *SubnetMask,\r
+  IN EFI_IPv4_ADDRESS            *GatewayAddress\r
+  )\r
+{\r
+  SOCKET          *Sock;\r
+  TCP4_ROUTE_INFO RouteInfo;\r
+\r
+  if (NULL == This) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock                      = SOCK_FROM_THIS (This);\r
+\r
+  RouteInfo.DeleteRoute     = DeleteRoute;\r
+  RouteInfo.SubnetAddress   = SubnetAddress;\r
+  RouteInfo.SubnetMask      = SubnetMask;\r
+  RouteInfo.GatewayAddress  = GatewayAddress;\r
+\r
+  return SockRoute (Sock, &RouteInfo);\r
+}\r
+\r
+/**\r
+  Initiate a non-blocking TCP connection request for an active TCP instance.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  ConnectionToken      Pointer to the connection token to return when\r
+                                   the TCP three way handshake finishes.\r
+\r
+  @retval EFI_SUCCESS              The connection request successfully\r
+                                   initiated.\r
+  @retval EFI_NOT_STARTED          This EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_ACCESS_DENIED        The instance is not configured as an active one,\r
+                                   or it is not in Tcp4StateClosed state.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     The driver can't allocate enough resources to\r
+                                   initiate the active open.\r
+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Connect (\r
+  IN EFI_TCP4_PROTOCOL           *This,\r
+  IN EFI_TCP4_CONNECTION_TOKEN   *ConnectionToken\r
+  )\r
+{\r
+  SOCKET  *Sock;\r
+\r
+  if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockConnect (Sock, ConnectionToken);\r
+}\r
+\r
+/**\r
+  Listen on the passive instance to accept an incoming connection request.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  ListenToken          Pointer to the listen token to return when\r
+                                   operation finishes.\r
+\r
+  @retval EFI_SUCCESS              The listen token was queued successfully.\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_ACCESS_DENIED        The instatnce is not a passive one or it is not\r
+                                   in Tcp4StateListen state or a same listen token\r
+                                   has already existed in the listen token queue of\r
+                                   this TCP instance.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to finish\r
+                                   the operation.\r
+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Accept (\r
+  IN EFI_TCP4_PROTOCOL             *This,\r
+  IN EFI_TCP4_LISTEN_TOKEN         *ListenToken\r
+  )\r
+{\r
+  SOCKET  *Sock;\r
+\r
+  if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockAccept (Sock, ListenToken);\r
+}\r
+\r
+/**\r
+  Queues outgoing data into the transmit queue\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  Token                Pointer to the completion token to queue to the\r
+                                   transmit queue.\r
+\r
+  @retval EFI_SUCCESS              The data has been queued for transmission.\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_NO_MAPPING           When using a default address, configuration\r
+                                   (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid\r
+  @retval EFI_ACCESS_DENIED        One or more of the following conditions is TRUE:\r
+                                   * A transmit completion token with the same\r
+                                   Token-> CompletionToken.Event was already in the\r
+                                   transmission queue. * The current instance is in\r
+                                   Tcp4StateClosed state. * The current instance is\r
+                                   a passive one and it is in Tcp4StateListen\r
+                                   state. * User has called Close() to disconnect\r
+                                   this connection.\r
+  @retval EFI_NOT_READY            The completion token could not be queued because\r
+                                   the transmit queue is full.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not queue the transmit data because of a\r
+                                   resource shortage.\r
+  @retval EFI_NETWORK_UNREACHABLE  There is no route to the destination network or\r
+                                   address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Transmit (\r
+  IN EFI_TCP4_PROTOCOL            *This,\r
+  IN EFI_TCP4_IO_TOKEN            *Token\r
+  )\r
+{\r
+  SOCKET      *Sock;\r
+  EFI_STATUS  Status;\r
+\r
+  if (NULL == This ||\r
+      NULL == Token ||\r
+      NULL == Token->CompletionToken.Event ||\r
+      NULL == Token->Packet.TxData ||\r
+      0 == Token->Packet.TxData->FragmentCount ||\r
+      0 == Token->Packet.TxData->DataLength\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = TcpChkDataBuf (\r
+            Token->Packet.TxData->DataLength,\r
+            Token->Packet.TxData->FragmentCount,\r
+            Token->Packet.TxData->FragmentTable\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockSend (Sock, Token);\r
+}\r
+\r
+/**\r
+  Place an asynchronous receive request into the receiving queue.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  Token                Pointer to a token that is associated with the\r
+                                   receive data descriptor.\r
+\r
+  @retval EFI_SUCCESS              The receive completion token was cached\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_NO_MAPPING           When using a default address, configuration\r
+                                   (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     The receive completion token could not be queued\r
+                                   due to a lack of system resources.\r
+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.\r
+  @retval EFI_ACCESS_DENIED        One or more of the following conditions is TRUE:\r
+                                   * A receive completion token with the same\r
+                                   Token->CompletionToken.Event was already in the\r
+                                   receive queue. * The current instance is in\r
+                                   Tcp4StateClosed state. * The current instance is\r
+                                   a passive one and it is in Tcp4StateListen\r
+                                   state. * User has called Close() to disconnect\r
+                                   this connection.\r
+  @retval EFI_CONNECTION_FIN       The communication peer has closed the connection,\r
+                                   and there is no any buffered data in the receive\r
+                                   buffer of this instance.\r
+  @retval EFI_NOT_READY            The receive request could not be queued because\r
+                                   the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Receive (\r
+  IN EFI_TCP4_PROTOCOL           *This,\r
+  IN EFI_TCP4_IO_TOKEN           *Token\r
+  )\r
+{\r
+  SOCKET      *Sock;\r
+  EFI_STATUS  Status;\r
+\r
+  if (NULL == This ||\r
+      NULL == Token ||\r
+      NULL == Token->CompletionToken.Event ||\r
+      NULL == Token->Packet.RxData ||\r
+      0 == Token->Packet.RxData->FragmentCount ||\r
+      0 == Token->Packet.RxData->DataLength\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = TcpChkDataBuf (\r
+            Token->Packet.RxData->DataLength,\r
+            Token->Packet.RxData->FragmentCount,\r
+            Token->Packet.RxData->FragmentTable\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockRcv (Sock, Token);\r
+\r
+}\r
+\r
+/**\r
+  Disconnecting a TCP connection gracefully or reset a TCP connection.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  CloseToken           Pointer to the close token to return when\r
+                                   operation finishes.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_ACCESS_DENIED        One or more of the following are TRUE: *\r
+                                   Configure() has been called with TcpConfigData\r
+                                   set to NULL, and this function has not returned.\r
+                                   * Previous Close() call on this instance has not\r
+                                   finished.\r
+  @retval EFI_INVALID_PARAMETER    One ore more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to finish the\r
+                                   operation.\r
+  @retval EFI_DEVICE_ERROR         Any unexpected category error not belonging to those\r
+                                   listed above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Close (\r
+  IN EFI_TCP4_PROTOCOL           *This,\r
+  IN EFI_TCP4_CLOSE_TOKEN        *CloseToken\r
+  )\r
+{\r
+  SOCKET  *Sock;\r
+\r
+  if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);\r
+}\r
+\r
+/**\r
+  Abort an asynchronous connection, listen, transmission or receive request.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  Token                Pointer to a token that has been issued by\r
+                                   Connect(), Accept(), Transmit() or Receive(). If\r
+                                   NULL, all pending tokens issued by the four\r
+                                   functions listed above will be aborted.\r
+\r
+  @retval EFI_UNSUPPORTED          The operation is not supported in the current\r
+                                   implementation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Cancel (\r
+  IN EFI_TCP4_PROTOCOL             *This,\r
+  IN EFI_TCP4_COMPLETION_TOKEN     *Token OPTIONAL\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Poll to receive incoming data and transmit outgoing segments.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+\r
+  @retval EFI_SUCCESS              Incoming or outgoing data was processed.\r
+  @retval EFI_INVALID_PARAMETER    This is NULL.\r
+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.\r
+  @retval EFI_NOT_READY            No incoming or outgoing data was processed.\r
+  @retval EFI_TIMEOUT              Data was dropped out of the transmission or\r
+                                   receive queue. Consider increasing the polling\r
+                                   rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Poll (\r
+  IN EFI_TCP4_PROTOCOL        *This\r
+  )\r
+{\r
+  SOCKET      *Sock;\r
+  EFI_STATUS  Status;\r
+\r
+  if (NULL == This) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock   = SOCK_FROM_THIS (This);\r
+\r
+  Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get the current operational status.\r
+\r
+  The GetModeData() function copies the current operational settings of this EFI TCPv6\r
+  Protocol instance into user-supplied buffers. This function can also be used to retrieve\r
+  the operational setting of underlying drivers such as IPv6, MNP, or SNP.\r
+\r
+  @param[in]  This              Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[out] Tcp6State         The buffer in which the current TCP state is\r
+                                returned. Optional parameter that may be NULL.\r
+  @param[out] Tcp6ConfigData    The buffer in which the current TCP configuration\r
+                                is returned. Optional parameter that may be NULL.\r
+  @param[out] Ip6ModeData       The buffer in which the current IPv6 configuration\r
+                                data used by the TCP instance is returned.\r
+                                Optional parameter that may be NULL.\r
+  @param[out] MnpConfigData     The buffer in which the current MNP configuration\r
+                                data indirectly used by the TCP instance is returned.\r
+                                Optional parameter that may be NULL.\r
+  @param[out] SnpModeData       The buffer in which the current SNP mode data\r
+                                indirectly used by the TCP instance is returned.\r
+                                Optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS           The mode data was read.\r
+  @retval EFI_NOT_STARTED       No configuration data is available because this instance hasn't\r
+                                been started.\r
+  @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6GetModeData (\r
+  IN  EFI_TCP6_PROTOCOL                  *This,\r
+  OUT EFI_TCP6_CONNECTION_STATE          *Tcp6State      OPTIONAL,\r
+  OUT EFI_TCP6_CONFIG_DATA               *Tcp6ConfigData OPTIONAL,\r
+  OUT EFI_IP6_MODE_DATA                  *Ip6ModeData    OPTIONAL,\r
+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA    *MnpConfigData  OPTIONAL,\r
+  OUT EFI_SIMPLE_NETWORK_MODE            *SnpModeData    OPTIONAL\r
+  )\r
+{\r
+  TCP6_MODE_DATA  TcpMode;\r
+  SOCKET          *Sock;\r
+\r
+  if (NULL == This) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock                   = SOCK_FROM_THIS (This);\r
+\r
+  TcpMode.Tcp6State      = Tcp6State;\r
+  TcpMode.Tcp6ConfigData = Tcp6ConfigData;\r
+  TcpMode.Ip6ModeData    = Ip6ModeData;\r
+  TcpMode.MnpConfigData  = MnpConfigData;\r
+  TcpMode.SnpModeData    = SnpModeData;\r
+\r
+  return SockGetMode (Sock, &TcpMode);\r
+}\r
+\r
+/**\r
+  Initialize or brutally reset the operational parameters for this EFI TCPv6 instance.\r
+\r
+  The Configure() function does the following:\r
+  - Initialize this TCP instance, i.e., initialize the communication end settings and\r
+    specify active open or passive open for an instance.\r
+  - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush\r
+    transmission and receiving buffer directly without informing the communication peer.\r
+\r
+  No other TCPv6 Protocol operation except Poll() can be executed by this instance until\r
+  it is configured properly. For an active TCP instance, after a proper configuration it\r
+  may call Connect() to initiate a three-way handshake. For a passive TCP instance,\r
+  its state transits to Tcp6StateListen after configuration, and Accept() may be\r
+  called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL,\r
+  the instance is reset. The resetting process will be done brutally, the state machine will\r
+  be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed,\r
+  and no traffic is allowed through this instance.\r
+\r
+  @param[in] This               Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] Tcp6ConfigData     Pointer to the configure data to configure the instance.\r
+                                If Tcp6ConfigData is set to NULL, the instance is reset.\r
+\r
+  @retval EFI_SUCCESS           The operational settings were set, changed, or reset\r
+                                successfully.\r
+  @retval EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source\r
+                                address for this instance, but no source address was available for\r
+                                use.\r
+  @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE:\r
+                                - This is NULL.\r
+                                - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor\r
+                                  one of the configured IP addresses in the underlying IPv6 driver.\r
+                                - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast\r
+                                  IPv6 address.\r
+                                - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or\r
+                                  Tcp6ConfigData->AccessPoint.RemotePort is zero when\r
+                                  Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE.\r
+                                - A same access point has been configured in other TCP\r
+                                  instance properly.\r
+  @retval EFI_ACCESS_DENIED     Configuring a TCP instance when it is configured without\r
+                                calling Configure() with NULL to reset it.\r
+  @retval EFI_UNSUPPORTED       One or more of the control options are not supported in\r
+                                the implementation.\r
+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough system resources when\r
+                                executing Configure().\r
+  @retval EFI_DEVICE_ERROR      An unexpected network or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Configure (\r
+  IN EFI_TCP6_PROTOCOL        *This,\r
+  IN EFI_TCP6_CONFIG_DATA     *Tcp6ConfigData OPTIONAL\r
+  )\r
+{\r
+  EFI_TCP6_OPTION   *Option;\r
+  SOCKET            *Sock;\r
+  EFI_STATUS        Status;\r
+  EFI_IPv6_ADDRESS  *Ip;\r
+\r
+  if (NULL == This) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Tcp protocol related parameter check will be conducted here\r
+  //\r
+  if (NULL != Tcp6ConfigData) {\r
+\r
+    Ip = &Tcp6ConfigData->AccessPoint.RemoteAddress;\r
+    if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (Tcp6ConfigData->AccessPoint.ActiveFlag &&\r
+        (0 == Tcp6ConfigData->AccessPoint.RemotePort || NetIp6IsUnspecifiedAddr (Ip))\r
+        ) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Ip = &Tcp6ConfigData->AccessPoint.StationAddress;\r
+    if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Option = Tcp6ConfigData->ControlOption;\r
+    if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  if (NULL == Tcp6ConfigData) {\r
+    return SockFlush (Sock);\r
+  }\r
+\r
+  Status = SockConfigure (Sock, Tcp6ConfigData);\r
+\r
+  if (EFI_NO_MAPPING == Status) {\r
+    Sock->ConfigureState = SO_NO_MAPPING;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Initiate a nonblocking TCP connection request for an active TCP instance.\r
+\r
+  The Connect() function will initiate an active open to the remote peer configured\r
+  in a current TCP instance if it is configured active. If the connection succeeds or\r
+  fails due to any error, the ConnectionToken->CompletionToken.Event will be signaled\r
+  and ConnectionToken->CompletionToken.Status will be updated accordingly. This\r
+  function can only be called for the TCP instance in the Tcp6StateClosed state. The\r
+  instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS.\r
+  If a TCP three-way handshake succeeds, its state will become Tcp6StateEstablished.\r
+  Otherwise, the state will return to Tcp6StateClosed.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] ConnectionToken     Pointer to the connection token to return when the TCP three\r
+                                 way handshake finishes.\r
+\r
+  @retval EFI_SUCCESS            The connection request successfully initiated and the state of\r
+                                 this TCP instance has been changed to Tcp6StateSynSent.\r
+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_ACCESS_DENIED      One or more of the following conditions are TRUE:\r
+                                 - This instance is not configured as an active one.\r
+                                 - This instance is not in Tcp6StateClosed state.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 - This is NULL.\r
+                                 - ConnectionToken is NULL.\r
+                                 - ConnectionToken->CompletionToken.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   The driver can't allocate enough resources to initiate the active open.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Connect (\r
+  IN EFI_TCP6_PROTOCOL           *This,\r
+  IN EFI_TCP6_CONNECTION_TOKEN   *ConnectionToken\r
+  )\r
+{\r
+  SOCKET  *Sock;\r
+\r
+  if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockConnect (Sock, ConnectionToken);\r
+}\r
+\r
+/**\r
+  Listen on the passive instance to accept an incoming connection request. This is a\r
+  nonblocking operation.\r
+\r
+  The Accept() function initiates an asynchronous accept request to wait for an incoming\r
+  connection on the passive TCP instance. If a remote peer successfully establishes a\r
+  connection with this instance, a new TCP instance will be created and its handle will\r
+  be returned in ListenToken->NewChildHandle. The newly created instance is configured\r
+  by inheriting the passive instance's configuration and is ready for use upon return.\r
+  The new instance is in the Tcp6StateEstablished state.\r
+\r
+  The ListenToken->CompletionToken.Event will be signaled when a new connection is\r
+  accepted, when a user aborts the listen or when a connection is reset.\r
+\r
+  This function only can be called when a current TCP instance is in Tcp6StateListen state.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] ListenToken         Pointer to the listen token to return when operation finishes.\r
+\r
+\r
+  @retval EFI_SUCCESS            The listen token queued successfully.\r
+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_ACCESS_DENIED      One or more of the following are TRUE:\r
+                                 - This instance is not a passive instance.\r
+                                 - This instance is not in Tcp6StateListen state.\r
+                                 - The same listen token has already existed in the listen\r
+                                   token queue of this TCP instance.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 - This is NULL.\r
+                                 - ListenToken is NULL.\r
+                                 - ListentToken->CompletionToken.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate enough resource to finish the operation.\r
+  @retval EFI_DEVICE_ERROR       Any unexpected error not belonging to a category listed above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Accept (\r
+  IN EFI_TCP6_PROTOCOL             *This,\r
+  IN EFI_TCP6_LISTEN_TOKEN         *ListenToken\r
+  )\r
+{\r
+  SOCKET  *Sock;\r
+\r
+  if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockAccept (Sock, ListenToken);\r
+}\r
+\r
+/**\r
+  Queues outgoing data into the transmit queue.\r
+\r
+  The Transmit() function queues a sending request to this TCP instance along with the\r
+  user data. The status of the token is updated and the event in the token will be\r
+  signaled once the data is sent out or an error occurs.\r
+\r
+  @param[in] This                 Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] Token                Pointer to the completion token to queue to the transmit queue.\r
+\r
+  @retval EFI_SUCCESS             The data has been queued for transmission.\r
+  @retval EFI_NOT_STARTED         This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_NO_MAPPING          The underlying IPv6 driver was responsible for choosing a\r
+                                  source address for this instance, but no source address was\r
+                                  available for use.\r
+  @retval EFI_INVALID_PARAMETER   One or more of the following are TRUE:\r
+                                  - This is NULL.\r
+                                  - Token is NULL.\r
+                                  - Token->CompletionToken.Event is NULL.\r
+                                  - Token->Packet.TxData is NULL.\r
+                                  - Token->Packet.FragmentCount is zero.\r
+                                  - Token->Packet.DataLength is not equal to the sum of fragment lengths.\r
+  @retval EFI_ACCESS_DENIED       One or more of the following conditions are TRUE:\r
+                                  - A transmit completion token with the same Token->\r
+                                    CompletionToken.Event was already in the\r
+                                    transmission queue.\r
+                                  - The current instance is in Tcp6StateClosed state.\r
+                                  - The current instance is a passive one and it is in\r
+                                    Tcp6StateListen state.\r
+                                  - User has called Close() to disconnect this connection.\r
+  @retval EFI_NOT_READY           The completion token could not be queued because the\r
+                                  transmit queue is full.\r
+  @retval EFI_OUT_OF_RESOURCES    Could not queue the transmit data because of resource\r
+                                  shortage.\r
+  @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Transmit (\r
+  IN EFI_TCP6_PROTOCOL            *This,\r
+  IN EFI_TCP6_IO_TOKEN            *Token\r
+  )\r
+{\r
+  SOCKET      *Sock;\r
+  EFI_STATUS  Status;\r
+\r
+  if (NULL == This ||\r
+      NULL == Token ||\r
+      NULL == Token->CompletionToken.Event ||\r
+      NULL == Token->Packet.TxData ||\r
+      0 == Token->Packet.TxData->FragmentCount ||\r
+      0 == Token->Packet.TxData->DataLength\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = TcpChkDataBuf (\r
+            Token->Packet.TxData->DataLength,\r
+            Token->Packet.TxData->FragmentCount,\r
+            (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.TxData->FragmentTable\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockSend (Sock, Token);\r
+}\r
+\r
+/**\r
+  Places an asynchronous receive request into the receiving queue.\r
+\r
+  The Receive() function places a completion token into the receive packet queue. This\r
+  function is always asynchronous. The caller must allocate the Token->CompletionToken.Event\r
+  and the FragmentBuffer used to receive data. The caller also must fill the DataLength that\r
+  represents the whole length of all FragmentBuffer. When the receive operation completes, the\r
+  EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData\r
+  fields, and the Token->CompletionToken.Event is signaled. If data obtained, the data and its length\r
+  will be copied into the FragmentTable; at the same time the full length of received data will\r
+  be recorded in the DataLength fields. Providing a proper notification function and context\r
+  for the event enables the user to receive the notification and receiving status. That\r
+  notification function is guaranteed to not be re-entered.\r
+\r
+  @param[in] This               Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] Token              Pointer to a token that is associated with the receive data\r
+                                descriptor.\r
+\r
+  @retval EFI_SUCCESS            The receive completion token was cached.\r
+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_NO_MAPPING         The underlying IPv6 driver was responsible for choosing a source\r
+                                 address for this instance, but no source address was available for use.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token->CompletionToken.Event is NULL.\r
+                                 - Token->Packet.RxData is NULL.\r
+                                 - Token->Packet.RxData->DataLength is 0.\r
+                                 - The Token->Packet.RxData->DataLength is not the\r
+                                   sum of all FragmentBuffer length in FragmentTable.\r
+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued due to a lack of\r
+                                 system resources (usually memory).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+                                 The EFI TCPv6 Protocol instance has been reset to startup defaults.\r
+  @retval EFI_ACCESS_DENIED      One or more of the following conditions is TRUE:\r
+                                 - A receive completion token with the same Token->CompletionToken.Event\r
+                                   was already in the receive queue.\r
+                                 - The current instance is in Tcp6StateClosed state.\r
+                                 - The current instance is a passive one and it is in\r
+                                   Tcp6StateListen state.\r
+                                 - User has called Close() to disconnect this connection.\r
+  @retval EFI_CONNECTION_FIN     The communication peer has closed the connection and there is no\r
+                                 buffered data in the receive buffer of this instance.\r
+  @retval EFI_NOT_READY          The receive request could not be queued because the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Receive (\r
+  IN EFI_TCP6_PROTOCOL           *This,\r
+  IN EFI_TCP6_IO_TOKEN           *Token\r
+  )\r
+{\r
+  SOCKET      *Sock;\r
+  EFI_STATUS  Status;\r
+\r
+  if (NULL == This ||\r
+      NULL == Token ||\r
+      NULL == Token->CompletionToken.Event ||\r
+      NULL == Token->Packet.RxData ||\r
+      0 == Token->Packet.RxData->FragmentCount ||\r
+      0 == Token->Packet.RxData->DataLength\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = TcpChkDataBuf (\r
+            Token->Packet.RxData->DataLength,\r
+            Token->Packet.RxData->FragmentCount,\r
+            (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.RxData->FragmentTable\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockRcv (Sock, Token);\r
+}\r
+\r
+/**\r
+  Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a\r
+  nonblocking operation.\r
+\r
+  Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered\r
+  transmission data will be sent by the TCP driver, and the current instance will have a graceful close\r
+  working flow described as RFC 793 if AbortOnClose is set to FALSE. Otherwise, a rest packet\r
+  will be sent by TCP driver to fast disconnect this connection. When the close operation completes\r
+  successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous\r
+  operations are signaled, and any buffers used for TCP network traffic are flushed.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] CloseToken          Pointer to the close token to return when operation finishes.\r
+\r
+  @retval EFI_SUCCESS            The Close() was called successfully.\r
+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_ACCESS_DENIED      One or more of the following are TRUE:\r
+                                 - CloseToken or CloseToken->CompletionToken.Event is already in use.\r
+                                 - Previous Close() call on this instance has not finished.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 - This is NULL.\r
+                                 - CloseToken is NULL.\r
+                                 - CloseToken->CompletionToken.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate enough resource to finish the operation.\r
+  @retval EFI_DEVICE_ERROR       Any unexpected error not belonging to error categories given above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Close (\r
+  IN EFI_TCP6_PROTOCOL           *This,\r
+  IN EFI_TCP6_CLOSE_TOKEN        *CloseToken\r
+  )\r
+{\r
+  SOCKET  *Sock;\r
+\r
+  if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock = SOCK_FROM_THIS (This);\r
+\r
+  return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);\r
+}\r
+\r
+/**\r
+  Abort an asynchronous connection, listen, transmission, or receive request.\r
+\r
+  The Cancel() function aborts a pending connection, listen, transmit, or\r
+  receive request.\r
+\r
+  If Token is not NULL and the token is in the connection, listen, transmission,\r
+  or receive queue when it is being cancelled, its Token->Status will be set\r
+  to EFI_ABORTED, and then Token->Event will be signaled.\r
+\r
+  If the token is not in one of the queues, which usually means that the\r
+  asynchronous operation has completed, EFI_NOT_FOUND is returned.\r
+\r
+  If Token is NULL all asynchronous token issued by Connect(), Accept(),\r
+  Transmit(), and Receive() will be aborted.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] Token               Pointer to a token that has been issued by\r
+                                 EFI_TCP6_PROTOCOL.Connect(),\r
+                                 EFI_TCP6_PROTOCOL.Accept(),\r
+                                 EFI_TCP6_PROTOCOL.Transmit() or\r
+                                 EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending\r
+                                 tokens issued by above four functions will be aborted. Type\r
+                                 EFI_TCP6_COMPLETION_TOKEN is defined in\r
+                                 EFI_TCP_PROTOCOL.Connect().\r
+\r
+  @retval EFI_UNSUPPORTED        The implementation does not support this function.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Cancel (\r
+  IN EFI_TCP6_PROTOCOL           *This,\r
+  IN EFI_TCP6_COMPLETION_TOKEN   *Token OPTIONAL\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Poll to receive incoming data and transmit outgoing segments.\r
+\r
+  The Poll() function increases the rate that data is moved between the network\r
+  and application, and can be called when the TCP instance is created successfully.\r
+  Its use is optional.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+\r
+  @retval EFI_SUCCESS            Incoming or outgoing data was processed.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+  @retval EFI_NOT_READY          No incoming or outgoing data is processed.\r
+  @retval EFI_TIMEOUT            Data was dropped out of the transmission or receive queue.\r
+                                 Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Poll (\r
+  IN EFI_TCP6_PROTOCOL        *This\r
+  )\r
+{\r
+  SOCKET      *Sock;\r
+  EFI_STATUS  Status;\r
+\r
+  if (NULL == This) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Sock   = SOCK_FROM_THIS (This);\r
+\r
+  Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/TcpDxe/TcpMain.h b/NetworkPkg/TcpDxe/TcpMain.h
new file mode 100644 (file)
index 0000000..fabffc2
--- /dev/null
@@ -0,0 +1,758 @@
+/** @file\r
+  Declaration of protocol interfaces in EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.\r
+  It is the common head file for all Tcp*.c in TCP driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _TCP_MAIN_H_\r
+#define _TCP_MAIN_H_\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DriverBinding.h>\r
+#include <Library/IpIoLib.h>\r
+#include <Library/DevicePathLib.h>\r
+\r
+#include "Socket.h"\r
+#include "TcpProto.h"\r
+#include "TcpDriver.h"\r
+#include "TcpFunc.h"\r
+\r
+extern UINT16                        mTcp4RandomPort;\r
+extern UINT16                        mTcp6RandomPort;\r
+extern CHAR16                        *mTcpStateName[];\r
+extern EFI_COMPONENT_NAME_PROTOCOL   gTcpComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL  gTcpComponentName2;\r
+\r
+extern LIST_ENTRY                    mTcpRunQue;\r
+extern LIST_ENTRY                    mTcpListenQue;\r
+extern TCP_SEQNO                     mTcpGlobalIss;\r
+extern UINT32                        mTcpTick;\r
+\r
+///\r
+/// 30 seconds.\r
+///\r
+#define TCP6_KEEP_NEIGHBOR_TIME    30\r
+///\r
+/// 5 seconds, since 1 tick equals 200ms.\r
+///\r
+#define TCP6_REFRESH_NEIGHBOR_TICK 25\r
+\r
+#define TCP_EXPIRE_TIME            65535\r
+\r
+///\r
+/// The implementation selects the initial send sequence number and the unit to\r
+/// be added when it is increased.\r
+///\r
+#define TCP_BASE_ISS               0x4d7e980b\r
+#define TCP_ISS_INCREMENT_1        2048\r
+#define TCP_ISS_INCREMENT_2        100\r
+\r
+typedef union {\r
+  EFI_TCP4_CONFIG_DATA  Tcp4CfgData;\r
+  EFI_TCP6_CONFIG_DATA  Tcp6CfgData;\r
+} TCP_CONFIG_DATA;\r
+\r
+typedef union {\r
+  EFI_TCP4_ACCESS_POINT  Tcp4Ap;\r
+  EFI_TCP6_ACCESS_POINT  Tcp6Ap;\r
+} TCP_ACCESS_POINT;\r
+\r
+typedef struct _TCP4_MODE_DATA {\r
+  EFI_TCP4_CONNECTION_STATE       *Tcp4State;\r
+  EFI_TCP4_CONFIG_DATA            *Tcp4ConfigData;\r
+  EFI_IP4_MODE_DATA               *Ip4ModeData;\r
+  EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;\r
+  EFI_SIMPLE_NETWORK_MODE         *SnpModeData;\r
+} TCP4_MODE_DATA;\r
+\r
+typedef struct _TCP6_MODE_DATA {\r
+  EFI_TCP6_CONNECTION_STATE       *Tcp6State;\r
+  EFI_TCP6_CONFIG_DATA            *Tcp6ConfigData;\r
+  EFI_IP6_MODE_DATA               *Ip6ModeData;\r
+  EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;\r
+  EFI_SIMPLE_NETWORK_MODE         *SnpModeData;\r
+} TCP6_MODE_DATA;\r
+\r
+typedef struct _TCP4_ROUTE_INFO {\r
+  BOOLEAN           DeleteRoute;\r
+  EFI_IPv4_ADDRESS  *SubnetAddress;\r
+  EFI_IPv4_ADDRESS  *SubnetMask;\r
+  EFI_IPv4_ADDRESS  *GatewayAddress;\r
+} TCP4_ROUTE_INFO;\r
+\r
+//\r
+// EFI_TCP4_PROTOCOL definitions.\r
+//\r
+\r
+/**\r
+  Get the current operational status.\r
+\r
+  @param[in]   This                Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[out]  Tcp4State           Pointer to the buffer to receive the current TCP\r
+                                   state. Optional parameter that may be NULL.\r
+  @param[out]  Tcp4ConfigData      Pointer to the buffer to receive the current TCP\r
+                                   configuration. Optional parameter that may be NULL.\r
+  @param[out]  Ip4ModeData         Pointer to the buffer to receive the current\r
+                                   IPv4 configuration. Optional parameter that may be NULL.\r
+  @param[out]  MnpConfigData       Pointer to the buffer to receive the current MNP\r
+                                   configuration data indirectly used by the TCPv4\r
+                                   Instance. Optional parameter that may be NULL.\r
+  @param[out]  SnpModeData         Pointer to the buffer to receive the current SNP\r
+                                   configuration data indirectly used by the TCPv4\r
+                                   Instance. Optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS              The mode data was read.\r
+  @retval EFI_NOT_STARTED          No configuration data is available because this\r
+                                   instance hasn't been started.\r
+  @retval EFI_INVALID_PARAMETER    This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4GetModeData (\r
+  IN CONST  EFI_TCP4_PROTOCOL                  *This,\r
+  OUT       EFI_TCP4_CONNECTION_STATE          *Tcp4State      OPTIONAL,\r
+  OUT       EFI_TCP4_CONFIG_DATA               *Tcp4ConfigData OPTIONAL,\r
+  OUT       EFI_IP4_MODE_DATA                  *Ip4ModeData    OPTIONAL,\r
+  OUT       EFI_MANAGED_NETWORK_CONFIG_DATA    *MnpConfigData  OPTIONAL,\r
+  OUT       EFI_SIMPLE_NETWORK_MODE            *SnpModeData    OPTIONAL\r
+  );\r
+\r
+/**\r
+  Initialize or brutally reset the operational parameters for\r
+  this EFI TCPv4 instance.\r
+\r
+  @param[in]   This                Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]   TcpConfigData       Pointer to the configure data to configure the\r
+                                   instance. Optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS              The operational settings are set, changed, or\r
+                                   reset successfully.\r
+  @retval EFI_NO_MAPPING           When using a default address, configuration\r
+                                   (through DHCP, BOOTP, RARP, etc.) is not\r
+                                   finished.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_ACCESS_DENIED        Configuring the TCP instance when it is already\r
+                                   configured.\r
+  @retval EFI_DEVICE_ERROR         An unexpected network or system error occurred.\r
+  @retval EFI_UNSUPPORTED          One or more of the control options are not\r
+                                   supported in the implementation.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough system resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Configure (\r
+  IN EFI_TCP4_PROTOCOL        * This,\r
+  IN EFI_TCP4_CONFIG_DATA     * TcpConfigData OPTIONAL\r
+  );\r
+\r
+/**\r
+  Add or delete routing entries.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  DeleteRoute          If TRUE, delete the specified route from routing\r
+                                   table; if FALSE, add the specified route to\r
+                                   routing table.\r
+  @param[in]  SubnetAddress        The destination network.\r
+  @param[in]  SubnetMask           The subnet mask for the destination network.\r
+  @param[in]  GatewayAddress       The gateway address for this route.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance has not been\r
+                                   configured.\r
+  @retval EFI_NO_MAPPING           When using a default address, configuration\r
+                                   (through DHCP, BOOTP, RARP, etc.) is not\r
+                                   finished.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to add the\r
+                                   entry to the routing table.\r
+  @retval EFI_NOT_FOUND            This route is not in the routing table.\r
+  @retval EFI_ACCESS_DENIED        This route is already in the routing table.\r
+  @retval EFI_UNSUPPORTED          The TCP driver does not support this operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Routes (\r
+  IN EFI_TCP4_PROTOCOL           *This,\r
+  IN BOOLEAN                     DeleteRoute,\r
+  IN EFI_IPv4_ADDRESS            *SubnetAddress,\r
+  IN EFI_IPv4_ADDRESS            *SubnetMask,\r
+  IN EFI_IPv4_ADDRESS            *GatewayAddress\r
+  );\r
+\r
+/**\r
+  Initiate a nonblocking TCP connection request for an active TCP instance.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  ConnectionToken      Pointer to the connection token to return when\r
+                                   the TCP three way handshake finishes.\r
+\r
+  @retval EFI_SUCCESS              The connection request is successfully\r
+                                   initiated.\r
+  @retval EFI_NOT_STARTED          This EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_ACCESS_DENIED        The instance is not configured as an active one\r
+                                   or it is not in Tcp4StateClosed state.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     The driver can't allocate enough resources to\r
+                                   initiate the active open.\r
+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Connect (\r
+  IN EFI_TCP4_PROTOCOL           *This,\r
+  IN EFI_TCP4_CONNECTION_TOKEN   *ConnectionToken\r
+  );\r
+\r
+/**\r
+  Listen on the passive instance to accept an incoming connection request.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  ListenToken          Pointer to the listen token to return when\r
+                                   operation finishes.\r
+\r
+  @retval EFI_SUCCESS              The listen token has been queued successfully.\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_ACCESS_DENIED        The instatnce is not a passive one or it is not\r
+                                   in Tcp4StateListen state, or a same listen token\r
+                                   has already existed in the listen token queue of\r
+                                   this TCP instance.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to finish\r
+                                   the operation.\r
+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Accept (\r
+  IN EFI_TCP4_PROTOCOL             *This,\r
+  IN EFI_TCP4_LISTEN_TOKEN         *ListenToken\r
+  );\r
+\r
+/**\r
+  Queues outgoing data into the transmit queue\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance\r
+  @param[in]  Token                Pointer to the completion token to queue to the\r
+                                   transmit queue\r
+\r
+  @retval EFI_SUCCESS              The data has been queued for transmission\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_NO_MAPPING           When using a default address, configuration\r
+                                   (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid\r
+  @retval EFI_ACCESS_DENIED        One or more of the following conditions is TRUE:\r
+                                   * A transmit completion token with the same\r
+                                   Token-> CompletionToken.Event was already in the\r
+                                   transmission queue. * The current instance is in\r
+                                   Tcp4StateClosed state * The current instance is\r
+                                   a passive one and it is in Tcp4StateListen\r
+                                   state. * User has called Close() to disconnect\r
+                                   this connection.\r
+  @retval EFI_NOT_READY            The completion token could not be queued because\r
+                                   the transmit queue is full.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not queue the transmit data because of a\r
+                                   resource shortage.\r
+  @retval EFI_NETWORK_UNREACHABLE  There is no route to the destination network or\r
+                                   address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Transmit (\r
+  IN EFI_TCP4_PROTOCOL            *This,\r
+  IN EFI_TCP4_IO_TOKEN            *Token\r
+  );\r
+\r
+/**\r
+  Place an asynchronous receive request into the receiving queue.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  Token                Pointer to a token that is associated with the\r
+                                   receive data descriptor.\r
+\r
+  @retval EFI_SUCCESS              The receive completion token was cached.\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_NO_MAPPING           When using a default address, configuration\r
+                                   (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     The receive completion token could not be queued\r
+                                   due to a lack of system resources.\r
+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.\r
+  @retval EFI_ACCESS_DENIED        One or more of the following conditions is TRUE:\r
+                                   * A receive completion token with the same\r
+                                   Token->CompletionToken.Event was already in the\r
+                                   receive queue. * The current instance is in\r
+                                   Tcp4StateClosed state. * The current instance is\r
+                                   a passive one and it is in Tcp4StateListen\r
+                                   state. * User has called Close() to disconnect\r
+                                   this connection.\r
+  @retval EFI_CONNECTION_FIN       The communication peer has closed the connection\r
+                                   and there is no buffered data in the receive\r
+                                   buffer of this instance.\r
+  @retval EFI_NOT_READY            The receive request could not be queued because\r
+                                   the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Receive (\r
+  IN EFI_TCP4_PROTOCOL           *This,\r
+  IN EFI_TCP4_IO_TOKEN           *Token\r
+  );\r
+\r
+/**\r
+  Disconnecting a TCP connection gracefully or reset a TCP connection.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  CloseToken           Pointer to the close token to return when\r
+                                   operation finishes.\r
+\r
+  @retval EFI_SUCCESS              The operation completed successfully.\r
+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been\r
+                                   configured.\r
+  @retval EFI_ACCESS_DENIED        One or more of the following are TRUE: *\r
+                                   Configure() has been called with TcpConfigData\r
+                                   set to NULL and this function has not returned.\r
+                                   * Previous Close() call on this instance has not\r
+                                   finished.\r
+  @retval EFI_INVALID_PARAMETER    One ore more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to finish the\r
+                                   operation.\r
+  @retval EFI_DEVICE_ERROR         Any unexpected error not belonging to the error\r
+                                   categories given above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Close (\r
+  IN EFI_TCP4_PROTOCOL           *This,\r
+  IN EFI_TCP4_CLOSE_TOKEN        *CloseToken\r
+  );\r
+\r
+/**\r
+  Abort an asynchronous connection, listen, transmission or receive request.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+  @param[in]  Token                Pointer to a token that has been issued by\r
+                                   Connect(), Accept(), Transmit() or Receive(). If\r
+                                   NULL, all pending tokens issued by the above four\r
+                                   functions will be aborted.\r
+\r
+  @retval EFI_UNSUPPORTED          The operation is not supported in the current\r
+                                   implementation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Cancel (\r
+  IN EFI_TCP4_PROTOCOL             *This,\r
+  IN EFI_TCP4_COMPLETION_TOKEN     *Token OPTIONAL\r
+  );\r
+\r
+/**\r
+  Poll to receive incoming data and transmit outgoing segments.\r
+\r
+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.\r
+\r
+  @retval EFI_SUCCESS              Incoming or outgoing data was processed.\r
+  @retval EFI_INVALID_PARAMETER    This is NULL.\r
+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.\r
+  @retval EFI_NOT_READY            No incoming or outgoing data was processed.\r
+  @retval EFI_TIMEOUT              Data was dropped out of the transmission or\r
+                                   receive queue. Consider increasing the polling\r
+                                   rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Poll (\r
+  IN EFI_TCP4_PROTOCOL        *This\r
+  );\r
+\r
+//\r
+// EFI_TCP6_PROTOCOL definitions.\r
+//\r
+\r
+/**\r
+  Get the current operational status.\r
+\r
+  The GetModeData() function copies the current operational settings of this EFI TCPv6\r
+  Protocol instance into user-supplied buffers. This function can also be used to retrieve\r
+  the operational setting of underlying drivers such as IPv6, MNP, or SNP.\r
+\r
+  @param[in]  This              Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[out] Tcp6State         The buffer in which the current TCP state is\r
+                                returned. Optional parameter that may be NULL.\r
+  @param[out] Tcp6ConfigData    The buffer in which the current TCP configuration\r
+                                is returned. Optional parameter that may be NULL.\r
+  @param[out] Ip6ModeData       The buffer in which the current IPv6 configuration\r
+                                data used by the TCP instance is returned.\r
+                                Optional parameter that may be NULL.\r
+  @param[out] MnpConfigData     The buffer in which the current MNP configuration\r
+                                data used indirectly by the TCP instance is returned.\r
+                                Optional parameter that may be NULL.\r
+  @param[out] SnpModeData       The buffer in which the current SNP mode data\r
+                                 used indirectly by the TCP instance is returned.\r
+                                Optional parameter that may be NULL.\r
+\r
+  @retval EFI_SUCCESS           The mode data was read.\r
+  @retval EFI_NOT_STARTED       No configuration data is available because this instance hasn't\r
+                                been started.\r
+  @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6GetModeData (\r
+  IN  EFI_TCP6_PROTOCOL                  *This,\r
+  OUT EFI_TCP6_CONNECTION_STATE          *Tcp6State      OPTIONAL,\r
+  OUT EFI_TCP6_CONFIG_DATA               *Tcp6ConfigData OPTIONAL,\r
+  OUT EFI_IP6_MODE_DATA                  *Ip6ModeData    OPTIONAL,\r
+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA    *MnpConfigData  OPTIONAL,\r
+  OUT EFI_SIMPLE_NETWORK_MODE            *SnpModeData    OPTIONAL\r
+  );\r
+\r
+/**\r
+  Initialize or brutally reset the operational parameters for this EFI TCPv6 instance.\r
+\r
+  The Configure() function does the following:\r
+  - Initialize this TCP instance, i.e., initialize the communication end settings and\r
+    specify active open or passive open for an instance.\r
+  - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush\r
+    transmission and receiving buffer directly without informing the communication peer.\r
+\r
+  No other TCPv6 Protocol operation except Poll() can be executed by this instance until\r
+  it is configured properly. For an active TCP instance, after a proper configuration it\r
+  may call Connect() to initiates the three-way handshake. For a passive TCP instance,\r
+  its state will transit to Tcp6StateListen after configuration, and Accept() may be\r
+  called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL,\r
+  the instance is reset. Resetting process will be done brutally, the state machine will\r
+  be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed,\r
+  and no traffic is allowed through this instance.\r
+\r
+  @param[in] This               Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] Tcp6ConfigData     Pointer to the configure data to configure the instance.\r
+                                If Tcp6ConfigData is set to NULL, the instance is reset.\r
+\r
+  @retval EFI_SUCCESS           The operational settings were set, changed, or reset\r
+                                successfully.\r
+  @retval EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source\r
+                                address for this instance, but no source address was available for\r
+                                use.\r
+  @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE:\r
+                                - This is NULL.\r
+                                - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor\r
+                                  one of the configured IP addresses in the underlying IPv6 driver.\r
+                                - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast\r
+                                  IPv6 address.\r
+                                - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or\r
+                                  Tcp6ConfigData->AccessPoint.RemotePort is zero when\r
+                                  Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE.\r
+                                - A same access point has been configured in other TCP\r
+                                  instance properly.\r
+  @retval EFI_ACCESS_DENIED     Configuring TCP instance when it is configured without\r
+                                calling Configure() with NULL to reset it.\r
+  @retval EFI_UNSUPPORTED       One or more of the control options are not supported in\r
+                                the implementation.\r
+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough system resources when\r
+                                executing Configure().\r
+  @retval EFI_DEVICE_ERROR      An unexpected network or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Configure (\r
+  IN EFI_TCP6_PROTOCOL        *This,\r
+  IN EFI_TCP6_CONFIG_DATA     *Tcp6ConfigData OPTIONAL\r
+  );\r
+\r
+/**\r
+  Initiate a nonblocking TCP connection request for an active TCP instance.\r
+\r
+  The Connect() function will initiate an active open to the remote peer configured\r
+  in current TCP instance if it is configured active. If the connection succeeds or\r
+  fails due to an error, the ConnectionToken->CompletionToken.Event will be signaled,\r
+  and ConnectionToken->CompletionToken.Status will be updated accordingly. This\r
+  function can only be called for the TCP instance in Tcp6StateClosed state. The\r
+  instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS.\r
+  If TCP three-way handshake succeeds, its state will become Tcp6StateEstablished;\r
+  otherwise, the state will return to Tcp6StateClosed.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] ConnectionToken     Pointer to the connection token to return when the TCP\r
+                                 three-way handshake finishes.\r
+\r
+  @retval EFI_SUCCESS            The connection request successfully initiated and the state of\r
+                                 this TCP instance has been changed to Tcp6StateSynSent.\r
+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_ACCESS_DENIED      One or more of the following conditions are TRUE:\r
+                                 - This instance is not configured as an active instance.\r
+                                 - This instance is not in Tcp6StateClosed state.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 - This is NULL.\r
+                                 - ConnectionToken is NULL.\r
+                                 - ConnectionToken->CompletionToken.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   The driver can't allocate enough resources to initiate the active open.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Connect (\r
+  IN EFI_TCP6_PROTOCOL           *This,\r
+  IN EFI_TCP6_CONNECTION_TOKEN   *ConnectionToken\r
+  );\r
+\r
+/**\r
+  Listen on the passive instance to accept an incoming connection request. This is a\r
+  nonblocking operation.\r
+\r
+  The Accept() function initiates an asynchronous accept request to wait for an incoming\r
+  connection on the passive TCP instance. If a remote peer successfully establishes a\r
+  connection with this instance, a new TCP instance will be created and its handle will\r
+  be returned in ListenToken->NewChildHandle. The newly created instance is configured\r
+  by inheriting the passive instance's configuration, and is ready for use upon return.\r
+  The new instance is in the Tcp6StateEstablished state.\r
+\r
+  The ListenToken->CompletionToken.Event will be signaled when a new connection is\r
+  accepted, user aborts the listen or connection is reset.\r
+\r
+  This function only can be called when the current TCP instance is in Tcp6StateListen state.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] ListenToken         Pointer to the listen token to return when the operation finishes.\r
+\r
+\r
+  @retval EFI_SUCCESS            The listen token was been queued successfully.\r
+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_ACCESS_DENIED      One or more of the following are TRUE:\r
+                                 - This instance is not a passive instance.\r
+                                 - This instance is not in Tcp6StateListen state.\r
+                                 - The same listen token has already existed in the listen\r
+                                   token queue of this TCP instance.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 - This is NULL.\r
+                                 - ListenToken is NULL.\r
+                                 - ListentToken->CompletionToken.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate enough resources to finish the operation.\r
+  @retval EFI_DEVICE_ERROR       Any unexpected error not belonging to the error\r
+                                 categories given above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Accept (\r
+  IN EFI_TCP6_PROTOCOL             *This,\r
+  IN EFI_TCP6_LISTEN_TOKEN         *ListenToken\r
+  );\r
+\r
+/**\r
+  Queues outgoing data into the transmit queue.\r
+\r
+  The Transmit() function queues a sending request to this TCP instance along with the\r
+  user data. The status of the token is updated and the event in the token will be\r
+  signaled once the data is sent out or some error occurs.\r
+\r
+  @param[in] This                 Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] Token                Pointer to the completion token to queue to the transmit queue.\r
+\r
+  @retval EFI_SUCCESS             The data has been queued for transmission.\r
+  @retval EFI_NOT_STARTED         This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_NO_MAPPING          The underlying IPv6 driver was responsible for choosing a\r
+                                  source address for this instance, but no source address was\r
+                                  available for use.\r
+  @retval EFI_INVALID_PARAMETER   One or more of the following are TRUE:\r
+                                  - This is NULL.\r
+                                  - Token is NULL.\r
+                                  - Token->CompletionToken.Event is NULL.\r
+                                  - Token->Packet.TxData is NULL.\r
+                                  - Token->Packet.FragmentCount is zero.\r
+                                  - Token->Packet.DataLength is not equal to the sum of fragment lengths.\r
+  @retval EFI_ACCESS_DENIED       One or more of the following conditions are TRUE:\r
+                                  - A transmit completion token with the same Token->\r
+                                    CompletionToken.Event was already in the\r
+                                    transmission queue.\r
+                                  - The current instance is in Tcp6StateClosed state.\r
+                                  - The current instance is a passive one and it is in\r
+                                    Tcp6StateListen state.\r
+                                  - User has called Close() to disconnect this connection.\r
+  @retval EFI_NOT_READY           The completion token could not be queued because the\r
+                                  transmit queue is full.\r
+  @retval EFI_OUT_OF_RESOURCES    Could not queue the transmit data because of a resource\r
+                                  shortage.\r
+  @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Transmit (\r
+  IN EFI_TCP6_PROTOCOL            *This,\r
+  IN EFI_TCP6_IO_TOKEN            *Token\r
+  );\r
+\r
+/**\r
+  Places an asynchronous receive request into the receiving queue.\r
+\r
+  The Receive() function places a completion token into the receive packet queue. This\r
+  function is always asynchronous. The caller must allocate the Token->CompletionToken.Event\r
+  and the FragmentBuffer used to receive data. The caller also must fill the DataLength, which\r
+  represents the whole length of all FragmentBuffer. When the receive operation completes, the\r
+  EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData\r
+  fields, and the Token->CompletionToken.Event is signaled. If data is obtained, the data and its length\r
+  will be copied into the FragmentTable. At the same time the full length of received data will\r
+  be recorded in the DataLength fields. Providing a proper notification function and context\r
+  for the event enables the user to receive the notification and receiving status. That\r
+  notification function is guaranteed to not be re-entered.\r
+\r
+  @param[in] This               Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] Token              Pointer to a token that is associated with the receive data\r
+                                descriptor.\r
+\r
+  @retval EFI_SUCCESS            The receive completion token was cached.\r
+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_NO_MAPPING         The underlying IPv6 driver was responsible for choosing a source\r
+                                 address for this instance, but no source address was available for use.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 - This is NULL.\r
+                                 - Token is NULL.\r
+                                 - Token->CompletionToken.Event is NULL.\r
+                                 - Token->Packet.RxData is NULL.\r
+                                 - Token->Packet.RxData->DataLength is 0.\r
+                                 - The Token->Packet.RxData->DataLength is not the\r
+                                   sum of all FragmentBuffer length in FragmentTable.\r
+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued due to a lack of\r
+                                 system resources (usually memory).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+                                 The EFI TCPv6 Protocol instance has been reset to startup defaults.\r
+  @retval EFI_ACCESS_DENIED      One or more of the following conditions is TRUE:\r
+                                 - A receive completion token with the same Token->CompletionToken.Event\r
+                                   was already in the receive queue.\r
+                                 - The current instance is in Tcp6StateClosed state.\r
+                                 - The current instance is a passive one and it is in\r
+                                   Tcp6StateListen state.\r
+                                 - The user has called Close() to disconnect this connection.\r
+  @retval EFI_CONNECTION_FIN     The communication peer has closed the connection, and there is no\r
+                                 buffered data in the receive buffer of this instance.\r
+  @retval EFI_NOT_READY          The receive request could not be queued because the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Receive (\r
+  IN EFI_TCP6_PROTOCOL           *This,\r
+  IN EFI_TCP6_IO_TOKEN           *Token\r
+  );\r
+\r
+/**\r
+  Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a\r
+  nonblocking operation.\r
+\r
+  Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered\r
+  transmission data will be sent by the TCP driver, and the current instance will have a graceful close\r
+  working flow described as RFC 793 if AbortOnClose is set to FALSE, otherwise, a rest packet\r
+  will be sent by TCP driver to fast disconnect this connection. When the close operation completes\r
+  successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous\r
+  operations are signaled, and any buffers used for TCP network traffic are flushed.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] CloseToken          Pointer to the close token to return when operation finishes.\r
+\r
+  @retval EFI_SUCCESS            The Close() was called successfully.\r
+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.\r
+  @retval EFI_ACCESS_DENIED      One or more of the following are TRUE:\r
+                                 - CloseToken or CloseToken->CompletionToken.Event is already in use.\r
+                                 - Previous Close() call on this instance has not finished.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 - This is NULL.\r
+                                 - CloseToken is NULL.\r
+                                 - CloseToken->CompletionToken.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate enough resources to finish the operation.\r
+  @retval EFI_DEVICE_ERROR       Any unexpected error not belonging to the error categories given above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Close (\r
+  IN EFI_TCP6_PROTOCOL           *This,\r
+  IN EFI_TCP6_CLOSE_TOKEN        *CloseToken\r
+  );\r
+\r
+/**\r
+  Abort an asynchronous connection, listen, transmission or receive request.\r
+\r
+  The Cancel() function aborts a pending connection, listen, transmit or\r
+  receive request.\r
+\r
+  If Token is not NULL and the token is in the connection, listen, transmission\r
+  or receive queue when it is being cancelled, its Token->Status will be set\r
+  to EFI_ABORTED and then Token->Event will be signaled.\r
+\r
+  If the token is not in one of the queues, which usually means that the\r
+  asynchronous operation has completed, EFI_NOT_FOUND is returned.\r
+\r
+  If Token is NULL all asynchronous token issued by Connect(), Accept(),\r
+  Transmit() and Receive() will be aborted.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+  @param[in] Token               Pointer to a token that has been issued by\r
+                                 EFI_TCP6_PROTOCOL.Connect(),\r
+                                 EFI_TCP6_PROTOCOL.Accept(),\r
+                                 EFI_TCP6_PROTOCOL.Transmit() or\r
+                                 EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending\r
+                                 tokens issued by above four functions will be aborted. Type\r
+                                 EFI_TCP6_COMPLETION_TOKEN is defined in\r
+                                 EFI_TCP_PROTOCOL.Connect().\r
+\r
+  @retval EFI_UNSUPPORTED        The implementation does not support this function.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Cancel (\r
+  IN EFI_TCP6_PROTOCOL           *This,\r
+  IN EFI_TCP6_COMPLETION_TOKEN   *Token OPTIONAL\r
+  );\r
+\r
+/**\r
+  Poll to receive incoming data and transmit outgoing segments.\r
+\r
+  The Poll() function increases the rate that data is moved between the network\r
+  and application and can be called when the TCP instance is created successfully.\r
+  Its use is optional.\r
+\r
+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.\r
+\r
+  @retval EFI_SUCCESS            Incoming or outgoing data was processed.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+  @retval EFI_NOT_READY          No incoming or outgoing data is processed.\r
+  @retval EFI_TIMEOUT            Data was dropped out of the transmission or receive queue.\r
+                                 Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Poll (\r
+  IN EFI_TCP6_PROTOCOL        *This\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/TcpDxe/TcpMisc.c b/NetworkPkg/TcpDxe/TcpMisc.c
new file mode 100644 (file)
index 0000000..492ec35
--- /dev/null
@@ -0,0 +1,1281 @@
+/** @file\r
+  Misc support routines for TCP driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+LIST_ENTRY      mTcpRunQue = {\r
+  &mTcpRunQue,\r
+  &mTcpRunQue\r
+};\r
+\r
+LIST_ENTRY      mTcpListenQue = {\r
+  &mTcpListenQue,\r
+  &mTcpListenQue\r
+};\r
+\r
+TCP_SEQNO       mTcpGlobalIss = TCP_BASE_ISS;\r
+\r
+CHAR16          *mTcpStateName[] = {\r
+  L"TCP_CLOSED",\r
+  L"TCP_LISTEN",\r
+  L"TCP_SYN_SENT",\r
+  L"TCP_SYN_RCVD",\r
+  L"TCP_ESTABLISHED",\r
+  L"TCP_FIN_WAIT_1",\r
+  L"TCP_FIN_WAIT_2",\r
+  L"TCP_CLOSING",\r
+  L"TCP_TIME_WAIT",\r
+  L"TCP_CLOSE_WAIT",\r
+  L"TCP_LAST_ACK"\r
+};\r
+\r
+\r
+/**\r
+  Initialize the Tcb local related members.\r
+\r
+  @param[in, out]  Tcb               Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbLocal (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  //\r
+  // Compute the checksum of the fixed parts of pseudo header\r
+  //\r
+  if (Tcb->Sk->IpVersion == IP_VERSION_4) {\r
+    Tcb->HeadSum = NetPseudoHeadChecksum (\r
+                    Tcb->LocalEnd.Ip.Addr[0],\r
+                    Tcb->RemoteEnd.Ip.Addr[0],\r
+                    0x06,\r
+                    0\r
+                    );\r
+  } else {\r
+    Tcb->HeadSum = NetIp6PseudoHeadChecksum (\r
+                    &Tcb->LocalEnd.Ip.v6,\r
+                    &Tcb->RemoteEnd.Ip.v6,\r
+                    0x06,\r
+                    0\r
+                    );\r
+  }\r
+\r
+  Tcb->Iss    = TcpGetIss ();\r
+  Tcb->SndUna = Tcb->Iss;\r
+  Tcb->SndNxt = Tcb->Iss;\r
+\r
+  Tcb->SndWl2 = Tcb->Iss;\r
+  Tcb->SndWnd = 536;\r
+\r
+  Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+\r
+  //\r
+  // First window size is never scaled\r
+  //\r
+  Tcb->RcvWndScale  = 0;\r
+\r
+  Tcb->ProbeTimerOn = FALSE;\r
+}\r
+\r
+/**\r
+  Initialize the peer related members.\r
+\r
+  @param[in, out]  Tcb    Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Seg    Pointer to the segment that contains the peer's intial info.\r
+  @param[in]       Opt    Pointer to the options announced by the peer.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbPeer (\r
+  IN OUT TCP_CB     *Tcb,\r
+  IN     TCP_SEG    *Seg,\r
+  IN     TCP_OPTION *Opt\r
+  )\r
+{\r
+  UINT16  RcvMss;\r
+\r
+  ASSERT ((Tcb != NULL) && (Seg != NULL) && (Opt != NULL));\r
+  ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN));\r
+\r
+  Tcb->SndWnd     = Seg->Wnd;\r
+  Tcb->SndWndMax  = Tcb->SndWnd;\r
+  Tcb->SndWl1     = Seg->Seq;\r
+\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+    Tcb->SndWl2 = Seg->Ack;\r
+  } else {\r
+    Tcb->SndWl2 = Tcb->Iss + 1;\r
+  }\r
+\r
+  if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) {\r
+    Tcb->SndMss = (UINT16) MAX (64, Opt->Mss);\r
+\r
+    RcvMss      = TcpGetRcvMss (Tcb->Sk);\r
+    if (Tcb->SndMss > RcvMss) {\r
+      Tcb->SndMss = RcvMss;\r
+    }\r
+\r
+  } else {\r
+    //\r
+    // One end doesn't support MSS option, use default.\r
+    //\r
+    Tcb->RcvMss = 536;\r
+  }\r
+\r
+  Tcb->CWnd   = Tcb->SndMss;\r
+\r
+  Tcb->Irs    = Seg->Seq;\r
+  Tcb->RcvNxt = Tcb->Irs + 1;\r
+\r
+  Tcb->RcvWl2 = Tcb->RcvNxt;\r
+\r
+  if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) {\r
+\r
+    Tcb->SndWndScale  = Opt->WndScale;\r
+\r
+    Tcb->RcvWndScale  = TcpComputeScale (Tcb);\r
+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS);\r
+\r
+  } else {\r
+    //\r
+    // One end doesn't support window scale option. use zero.\r
+    //\r
+    Tcb->RcvWndScale = 0;\r
+  }\r
+\r
+  if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) {\r
+\r
+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS);\r
+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS);\r
+\r
+    //\r
+    // Compute the effective SndMss per RFC1122\r
+    // section 4.2.2.6. If timestamp option is\r
+    // enabled, it will always occupy 12 bytes.\r
+    //\r
+    Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN;\r
+  }\r
+}\r
+\r
+/**\r
+  Check whether one IP address equals the other.\r
+\r
+  @param[in]   Ip1     Pointer to IP address to be checked.\r
+  @param[in]   Ip2     Pointer to IP address to be checked.\r
+  @param[in]   Version IP_VERSION_4 indicates the IP address is an IPv4 address,\r
+                       IP_VERSION_6 indicates the IP address is an IPv6 address.\r
+\r
+  @retval      TRUE    Ip1 equals Ip2.\r
+  @retval      FALSE   Ip1 does not equal Ip2.\r
+\r
+**/\r
+BOOLEAN\r
+TcpIsIpEqual (\r
+  IN EFI_IP_ADDRESS  *Ip1,\r
+  IN EFI_IP_ADDRESS  *Ip2,\r
+  IN UINT8           Version\r
+  )\r
+{\r
+  ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
+\r
+  if (Version == IP_VERSION_4) {\r
+    return (BOOLEAN) (Ip1->Addr[0] == Ip2->Addr[0]);\r
+  } else {\r
+    return (BOOLEAN) EFI_IP6_EQUAL (&Ip1->v6, &Ip2->v6);\r
+  }\r
+}\r
+\r
+/**\r
+  Check whether one IP address is filled with ZERO.\r
+\r
+  @param[in]   Ip      Pointer to the IP address to be checked.\r
+  @param[in]   Version IP_VERSION_4 indicates the IP address is an IPv4 address,\r
+                       IP_VERSION_6 indicates the IP address is an IPv6 address.\r
+\r
+  @retval      TRUE    Ip is all zero address.\r
+  @retval      FALSE   Ip is not all zero address.\r
+\r
+**/\r
+BOOLEAN\r
+TcpIsIpZero (\r
+  IN EFI_IP_ADDRESS *Ip,\r
+  IN UINT8          Version\r
+  )\r
+{\r
+  ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
+\r
+  if (Version == IP_VERSION_4) {\r
+    return (BOOLEAN) (Ip->Addr[0] == 0);\r
+  } else {\r
+    return (BOOLEAN) ((Ip->Addr[0] == 0) && (Ip->Addr[1] == 0) &&\r
+      (Ip->Addr[2] == 0) && (Ip->Addr[3] == 0));\r
+  }\r
+}\r
+\r
+/**\r
+  Locate a listen TCB that matchs the Local and Remote.\r
+\r
+  @param[in]  Local    Pointer to the local (IP, Port).\r
+  @param[in]  Remote   Pointer to the remote (IP, Port).\r
+  @param[in]  Version  IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+                       IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+  @return  Pointer to the TCP_CB with the least number of wildcards,\r
+           if NULL no match is found.\r
+\r
+**/\r
+TCP_CB *\r
+TcpLocateListenTcb (\r
+  IN TCP_PEER    *Local,\r
+  IN TCP_PEER    *Remote,\r
+  IN UINT8       Version\r
+  )\r
+{\r
+  LIST_ENTRY      *Entry;\r
+  TCP_CB          *Node;\r
+  TCP_CB          *Match;\r
+  INTN            Last;\r
+  INTN            Cur;\r
+\r
+  Last  = 4;\r
+  Match = NULL;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+    Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    if ((Version != Node->Sk->IpVersion) ||\r
+        (Local->Port != Node->LocalEnd.Port) ||\r
+        !TCP_PEER_MATCH (Remote, &Node->RemoteEnd, Version) ||\r
+        !TCP_PEER_MATCH (Local, &Node->LocalEnd, Version)\r
+          ) {\r
+\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Compute the number of wildcard\r
+    //\r
+    Cur = 0;\r
+    if (TcpIsIpZero (&Node->RemoteEnd.Ip, Version)) {\r
+      Cur++;\r
+    }\r
+\r
+    if (Node->RemoteEnd.Port == 0) {\r
+      Cur++;\r
+    }\r
+\r
+    if (TcpIsIpZero (&Node->LocalEnd.Ip, Version)) {\r
+      Cur++;\r
+    }\r
+\r
+    if (Cur < Last) {\r
+      if (Cur == 0) {\r
+        return Node;\r
+      }\r
+\r
+      Last  = Cur;\r
+      Match = Node;\r
+    }\r
+  }\r
+\r
+  return Match;\r
+}\r
+\r
+/**\r
+  Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.\r
+\r
+  @param[in]  Addr     Pointer to the IP address needs to match.\r
+  @param[in]  Port     The port number needs to match.\r
+  @param[in]  Version  IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+                       IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+\r
+  @retval     TRUE     The Tcb which matches the <Addr Port> pair exists.\r
+  @retval     FALSE    Otherwise\r
+\r
+**/\r
+BOOLEAN\r
+TcpFindTcbByPeer (\r
+  IN EFI_IP_ADDRESS  *Addr,\r
+  IN TCP_PORTNO      Port,\r
+  IN UINT8           Version\r
+  )\r
+{\r
+  TCP_PORTNO      LocalPort;\r
+  LIST_ENTRY      *Entry;\r
+  TCP_CB          *Tcb;\r
+\r
+  ASSERT ((Addr != NULL) && (Port != 0));\r
+\r
+  LocalPort = HTONS (Port);\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+    Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    if ((Version == Tcb->Sk->IpVersion) &&\r
+      TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&\r
+        (LocalPort == Tcb->LocalEnd.Port)\r
+        ) {\r
+\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+    Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    if ((Version == Tcb->Sk->IpVersion) &&\r
+      TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&\r
+        (LocalPort == Tcb->LocalEnd.Port)\r
+        ) {\r
+\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Locate the TCP_CB related to the socket pair.\r
+\r
+  @param[in]  LocalPort      The local port number.\r
+  @param[in]  LocalIp        The local IP address.\r
+  @param[in]  RemotePort     The remote port number.\r
+  @param[in]  RemoteIp       The remote IP address.\r
+  @param[in]  Version        IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+                             IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+  @param[in]  Syn            If TRUE, the listen sockets are searched.\r
+\r
+  @return Pointer to the related TCP_CB. If NULL, no match is found.\r
+\r
+**/\r
+TCP_CB *\r
+TcpLocateTcb (\r
+  IN TCP_PORTNO      LocalPort,\r
+  IN EFI_IP_ADDRESS  *LocalIp,\r
+  IN TCP_PORTNO      RemotePort,\r
+  IN EFI_IP_ADDRESS  *RemoteIp,\r
+  IN UINT8           Version,\r
+  IN BOOLEAN         Syn\r
+  )\r
+{\r
+  TCP_PEER        Local;\r
+  TCP_PEER        Remote;\r
+  LIST_ENTRY      *Entry;\r
+  TCP_CB          *Tcb;\r
+\r
+  Local.Port  = LocalPort;\r
+  Remote.Port = RemotePort;\r
+\r
+  CopyMem (&Local.Ip, LocalIp, sizeof (EFI_IP_ADDRESS));\r
+  CopyMem (&Remote.Ip, RemoteIp, sizeof (EFI_IP_ADDRESS));\r
+\r
+  //\r
+  // First check for exact match.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+    Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    if ((Version == Tcb->Sk->IpVersion) &&\r
+        TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd, Version) &&\r
+        TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd, Version)\r
+          ) {\r
+\r
+      RemoveEntryList (&Tcb->List);\r
+      InsertHeadList (&mTcpRunQue, &Tcb->List);\r
+\r
+      return Tcb;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Only check the listen queue when the SYN flag is on.\r
+  //\r
+  if (Syn) {\r
+    return TcpLocateListenTcb (&Local, &Remote, Version);\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Insert a Tcb into the proper queue.\r
+\r
+  @param[in]  Tcb               Pointer to the TCP_CB to be inserted.\r
+\r
+  @retval 0                     The Tcb was inserted successfully.\r
+  @retval -1                    Error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpInsertTcb (\r
+  IN TCP_CB *Tcb\r
+  )\r
+{\r
+  LIST_ENTRY       *Entry;\r
+  LIST_ENTRY       *Head;\r
+  TCP_CB           *Node;\r
+  TCP_PROTO_DATA  *TcpProto;\r
+\r
+  ASSERT (\r
+    (Tcb != NULL) &&\r
+    (\r
+    (Tcb->State == TCP_LISTEN) ||\r
+    (Tcb->State == TCP_SYN_SENT) ||\r
+    (Tcb->State == TCP_SYN_RCVD) ||\r
+    (Tcb->State == TCP_CLOSED)\r
+    )\r
+    );\r
+\r
+  if (Tcb->LocalEnd.Port == 0) {\r
+    return -1;\r
+  }\r
+\r
+  Head = &mTcpRunQue;\r
+\r
+  if (Tcb->State == TCP_LISTEN) {\r
+    Head = &mTcpListenQue;\r
+  }\r
+\r
+  //\r
+  // Check that the Tcb isn't already on the list.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, Head) {\r
+    Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd, Tcb->Sk->IpVersion) &&\r
+        TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd, Tcb->Sk->IpVersion)\r
+          ) {\r
+\r
+      return -1;\r
+    }\r
+  }\r
+\r
+  InsertHeadList (Head, &Tcb->List);\r
+\r
+  TcpProto = (TCP_PROTO_DATA *) Tcb->Sk->ProtoReserved;\r
+  TcpSetVariableData (TcpProto->TcpService);\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Clone a TCP_CB from Tcb.\r
+\r
+  @param[in]  Tcb                   Pointer to the TCP_CB to be cloned.\r
+\r
+  @return Pointer to the new cloned TCP_CB; if NULL, error condition occurred.\r
+\r
+**/\r
+TCP_CB *\r
+TcpCloneTcb (\r
+  IN TCP_CB *Tcb\r
+  )\r
+{\r
+  TCP_CB             *Clone;\r
+\r
+  Clone = AllocateZeroPool (sizeof (TCP_CB));\r
+\r
+  if (Clone == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  CopyMem (Clone, Tcb, sizeof (TCP_CB));\r
+\r
+  //\r
+  // Increase the reference count of the shared IpInfo.\r
+  //\r
+  NET_GET_REF (Tcb->IpInfo);\r
+\r
+  InitializeListHead (&Clone->List);\r
+  InitializeListHead (&Clone->SndQue);\r
+  InitializeListHead (&Clone->RcvQue);\r
+\r
+  Clone->Sk = SockClone (Tcb->Sk);\r
+  if (Clone->Sk == NULL) {\r
+    DEBUG ((EFI_D_ERROR, "TcpCloneTcb: failed to clone a sock\n"));\r
+    FreePool (Clone);\r
+    return NULL;\r
+  }\r
+\r
+  ((TCP_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;\r
+\r
+  return Clone;\r
+}\r
+\r
+/**\r
+  Compute an ISS to be used by a new connection.\r
+\r
+  @return The resulting ISS.\r
+\r
+**/\r
+TCP_SEQNO\r
+TcpGetIss (\r
+  VOID\r
+  )\r
+{\r
+  mTcpGlobalIss += TCP_ISS_INCREMENT_1;\r
+  return mTcpGlobalIss;\r
+}\r
+\r
+/**\r
+  Get the local mss.\r
+\r
+  @param[in]  Sock        Pointer to the socket to get mss.\r
+\r
+  @return The mss size.\r
+\r
+**/\r
+UINT16\r
+TcpGetRcvMss (\r
+  IN SOCKET  *Sock\r
+  )\r
+{\r
+  EFI_IP4_MODE_DATA      Ip4Mode;\r
+  EFI_IP6_MODE_DATA      Ip6Mode;\r
+  EFI_IP4_PROTOCOL       *Ip4;\r
+  EFI_IP6_PROTOCOL       *Ip6;\r
+  TCP_PROTO_DATA         *TcpProto;\r
+\r
+  ASSERT (Sock != NULL);\r
+\r
+  ZeroMem (&Ip4Mode, sizeof (EFI_IP4_MODE_DATA));\r
+  ZeroMem (&Ip6Mode, sizeof (EFI_IP6_MODE_DATA));\r
+\r
+  TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+\r
+  if (Sock->IpVersion == IP_VERSION_4) {\r
+    Ip4 = TcpProto->TcpService->IpIo->Ip.Ip4;\r
+    ASSERT (Ip4 != NULL);\r
+    Ip4->GetModeData (Ip4, &Ip4Mode, NULL, NULL);\r
+\r
+    return (UINT16) (Ip4Mode.MaxPacketSize - sizeof (TCP_HEAD));\r
+  } else {\r
+    Ip6 = TcpProto->TcpService->IpIo->Ip.Ip6;\r
+    ASSERT (Ip6 != NULL);\r
+    Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL);\r
+\r
+    return (UINT16) (Ip6Mode.MaxPacketSize - sizeof (TCP_HEAD));\r
+  }\r
+}\r
+\r
+/**\r
+  Set the Tcb's state.\r
+\r
+  @param[in]  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  State                 The state to be set.\r
+\r
+**/\r
+VOID\r
+TcpSetState (\r
+  IN TCP_CB *Tcb,\r
+  IN UINT8  State\r
+  )\r
+{\r
+  ASSERT (Tcb->State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));\r
+  ASSERT (State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));\r
+\r
+  DEBUG (\r
+    (EFI_D_INFO,\r
+    "Tcb (%p) state %s --> %s\n",\r
+    Tcb,\r
+    mTcpStateName[Tcb->State],\r
+    mTcpStateName[State])\r
+    );\r
+\r
+  Tcb->State = State;\r
+\r
+  switch (State) {\r
+  case TCP_ESTABLISHED:\r
+\r
+    SockConnEstablished (Tcb->Sk);\r
+\r
+    if (Tcb->Parent != NULL) {\r
+      //\r
+      // A new connection is accepted by a listening socket. Install\r
+      // the device path.\r
+      //\r
+      TcpInstallDevicePath (Tcb->Sk);\r
+    }\r
+\r
+    break;\r
+\r
+  case TCP_CLOSED:\r
+\r
+    SockConnClosed (Tcb->Sk);\r
+\r
+    break;\r
+  default:\r
+    break;\r
+  }\r
+}\r
+\r
+/**\r
+  Compute the TCP segment's checksum.\r
+\r
+  @param[in]  Nbuf       Pointer to the buffer that contains the TCP segment.\r
+  @param[in]  HeadSum    The checksum value of the fixed part of pseudo header.\r
+\r
+  @return The checksum value.\r
+\r
+**/\r
+UINT16\r
+TcpChecksum (\r
+  IN NET_BUF *Nbuf,\r
+  IN UINT16  HeadSum\r
+  )\r
+{\r
+  UINT16  Checksum;\r
+\r
+  Checksum  = NetbufChecksum (Nbuf);\r
+  Checksum  = NetAddChecksum (Checksum, HeadSum);\r
+\r
+  Checksum = NetAddChecksum (\r
+              Checksum,\r
+              HTONS ((UINT16) Nbuf->TotalSize)\r
+              );\r
+\r
+  return (UINT16) (~Checksum);\r
+}\r
+\r
+/**\r
+  Translate the information from the head of the received TCP\r
+  segment Nbuf contents and fill it into a TCP_SEG structure.\r
+\r
+  @param[in]       Tcb           Pointer to the TCP_CB of this TCP instance.\r
+  @param[in, out]  Nbuf          Pointer to the buffer contains the TCP segment.\r
+\r
+  @return Pointer to the TCP_SEG that contains the translated TCP head information.\r
+\r
+**/\r
+TCP_SEG *\r
+TcpFormatNetbuf (\r
+  IN     TCP_CB  *Tcb,\r
+  IN OUT NET_BUF *Nbuf\r
+  )\r
+{\r
+  TCP_SEG   *Seg;\r
+  TCP_HEAD  *Head;\r
+\r
+  Seg       = TCPSEG_NETBUF (Nbuf);\r
+  Head      = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
+  ASSERT (Head != NULL);\r
+\r
+  Nbuf->Tcp = Head;\r
+\r
+  Seg->Seq  = NTOHL (Head->Seq);\r
+  Seg->Ack  = NTOHL (Head->Ack);\r
+  Seg->End  = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));\r
+\r
+  Seg->Urg  = NTOHS (Head->Urg);\r
+  Seg->Wnd  = (NTOHS (Head->Wnd) << Tcb->SndWndScale);\r
+  Seg->Flag = Head->Flag;\r
+\r
+  //\r
+  // SYN and FIN flag occupy one sequence space each.\r
+  //\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+    //\r
+    // RFC requires that the initial window not be scaled.\r
+    //\r
+    Seg->Wnd = NTOHS (Head->Wnd);\r
+    Seg->End++;\r
+  }\r
+\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+    Seg->End++;\r
+  }\r
+\r
+  return Seg;\r
+}\r
+\r
+/**\r
+  Initialize an active connection.\r
+\r
+  @param[in, out]  Tcb          Pointer to the TCP_CB that wants to initiate a\r
+                                connection.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConnect (\r
+  IN OUT TCP_CB  *Tcb\r
+  )\r
+{\r
+  TcpInitTcbLocal (Tcb);\r
+  TcpSetState (Tcb, TCP_SYN_SENT);\r
+\r
+  TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);\r
+  TcpToSendData (Tcb, 1);\r
+}\r
+\r
+/**\r
+  Initiate the connection close procedure, called when\r
+  applications want to close the connection.\r
+\r
+  @param[in, out]  Tcb          Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppClose (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  ASSERT (Tcb != NULL);\r
+\r
+  if (!IsListEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk) != 0) {\r
+\r
+    DEBUG (\r
+      (EFI_D_WARN,\r
+      "TcpOnAppClose: connection reset because data is lost for TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    TcpResetConnection (Tcb);\r
+    TcpClose (Tcb);\r
+    return;\r
+  }\r
+\r
+  switch (Tcb->State) {\r
+  case TCP_CLOSED:\r
+  case TCP_LISTEN:\r
+  case TCP_SYN_SENT:\r
+    TcpSetState (Tcb, TCP_CLOSED);\r
+    break;\r
+\r
+  case TCP_SYN_RCVD:\r
+  case TCP_ESTABLISHED:\r
+    TcpSetState (Tcb, TCP_FIN_WAIT_1);\r
+    break;\r
+\r
+  case TCP_CLOSE_WAIT:\r
+    TcpSetState (Tcb, TCP_LAST_ACK);\r
+    break;\r
+  default:\r
+    break;\r
+  }\r
+\r
+  TcpToSendData (Tcb, 1);\r
+}\r
+\r
+/**\r
+  Check whether the application's newly delivered data can be sent out.\r
+\r
+  @param[in, out]  Tcb          Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @retval 0                     The data has been sent out successfully.\r
+  @retval -1                    The Tcb is not in a state that data is permitted to\r
+                                be sent out.\r
+\r
+**/\r
+INTN\r
+TcpOnAppSend (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+\r
+  switch (Tcb->State) {\r
+  case TCP_CLOSED:\r
+    return -1;\r
+\r
+  case TCP_LISTEN:\r
+    return -1;\r
+\r
+  case TCP_SYN_SENT:\r
+  case TCP_SYN_RCVD:\r
+    return 0;\r
+\r
+  case TCP_ESTABLISHED:\r
+  case TCP_CLOSE_WAIT:\r
+    TcpToSendData (Tcb, 0);\r
+    return 0;\r
+\r
+  case TCP_FIN_WAIT_1:\r
+  case TCP_FIN_WAIT_2:\r
+  case TCP_CLOSING:\r
+  case TCP_LAST_ACK:\r
+  case TCP_TIME_WAIT:\r
+    return -1;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Application has consumed some data. Check whether\r
+  to send a window update ack or a delayed ack.\r
+\r
+  @param[in]  Tcb        Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConsume (\r
+  IN TCP_CB *Tcb\r
+  )\r
+{\r
+  UINT32 TcpOld;\r
+\r
+  switch (Tcb->State) {\r
+  case TCP_ESTABLISHED:\r
+    TcpOld = TcpRcvWinOld (Tcb);\r
+    if (TcpRcvWinNow (Tcb) > TcpOld) {\r
+\r
+      if (TcpOld < Tcb->RcvMss) {\r
+\r
+        DEBUG (\r
+          (EFI_D_INFO,\r
+          "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",\r
+          Tcb)\r
+          );\r
+\r
+        TcpSendAck (Tcb);\r
+      } else if (Tcb->DelayedAck == 0) {\r
+\r
+        DEBUG (\r
+          (EFI_D_INFO,\r
+          "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n",\r
+          Tcb)\r
+          );\r
+\r
+        Tcb->DelayedAck = 1;\r
+      }\r
+    }\r
+\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+}\r
+\r
+/**\r
+  Abort the connection by sending a reset segment. Called\r
+  when the application wants to abort the connection.\r
+\r
+  @param[in]  Tcb                   Pointer to the TCP_CB of the TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppAbort (\r
+  IN TCP_CB *Tcb\r
+  )\r
+{\r
+  DEBUG (\r
+    (EFI_D_WARN,\r
+    "TcpOnAppAbort: connection reset issued by application for TCB %p\n",\r
+    Tcb)\r
+    );\r
+\r
+  switch (Tcb->State) {\r
+  case TCP_SYN_RCVD:\r
+  case TCP_ESTABLISHED:\r
+  case TCP_FIN_WAIT_1:\r
+  case TCP_FIN_WAIT_2:\r
+  case TCP_CLOSE_WAIT:\r
+    TcpResetConnection (Tcb);\r
+    break;\r
+  default:\r
+    break;\r
+  }\r
+\r
+  TcpSetState (Tcb, TCP_CLOSED);\r
+}\r
+\r
+/**\r
+  Reset the connection related with Tcb.\r
+\r
+  @param[in]  Tcb         Pointer to the TCP_CB of the connection to be reset.\r
+\r
+**/\r
+VOID\r
+TcpResetConnection (\r
+  IN TCP_CB *Tcb\r
+  )\r
+{\r
+  NET_BUF   *Nbuf;\r
+  TCP_HEAD  *Nhead;\r
+\r
+  Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
+\r
+  if (Nbuf == NULL) {\r
+    return ;\r
+  }\r
+\r
+  Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
+                        Nbuf,\r
+                        sizeof (TCP_HEAD),\r
+                        NET_BUF_TAIL\r
+                        );\r
+\r
+  ASSERT (Nhead != NULL);\r
+\r
+  Nbuf->Tcp       = Nhead;\r
+\r
+  Nhead->Flag     = TCP_FLG_RST;\r
+  Nhead->Seq      = HTONL (Tcb->SndNxt);\r
+  Nhead->Ack      = HTONL (Tcb->RcvNxt);\r
+  Nhead->SrcPort  = Tcb->LocalEnd.Port;\r
+  Nhead->DstPort  = Tcb->RemoteEnd.Port;\r
+  Nhead->HeadLen  = (UINT8) (sizeof (TCP_HEAD) >> 2);\r
+  Nhead->Res      = 0;\r
+  Nhead->Wnd      = HTONS (0xFFFF);\r
+  Nhead->Checksum = 0;\r
+  Nhead->Urg      = 0;\r
+  Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);\r
+\r
+  TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);\r
+\r
+  NetbufFree (Nbuf);\r
+}\r
+\r
+/**\r
+  Set the Tcp variable data.\r
+\r
+  @param[in]  TcpService        Tcp service data.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.\r
+  @retval other                 Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpSetVariableData (\r
+  IN TCP_SERVICE_DATA  *TcpService\r
+  )\r
+{\r
+  EFI_GUID                *ServiceBindingGuid;\r
+  UINT32                  NumConfiguredInstance;\r
+  LIST_ENTRY              *Entry;\r
+  TCP_CB                  *TcpPcb;\r
+  TCP_PROTO_DATA          *TcpProto;\r
+  UINTN                   VariableDataSize;\r
+  EFI_TCP4_VARIABLE_DATA  *Tcp4VariableData;\r
+  EFI_TCP4_SERVICE_POINT  *Tcp4ServicePoint;\r
+  EFI_TCP6_VARIABLE_DATA  *Tcp6VariableData;\r
+  EFI_TCP6_SERVICE_POINT  *Tcp6ServicePoint;\r
+  VOID                    *VariableData;\r
+  CHAR16                  *NewMacString;\r
+  EFI_STATUS              Status;\r
+\r
+  if (TcpService->IpVersion == IP_VERSION_4) {\r
+    ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+  } else {\r
+    ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+  }\r
+\r
+  NumConfiguredInstance = 0;\r
+  Tcp4VariableData      = NULL;\r
+  Tcp6VariableData      = NULL;\r
+\r
+  //\r
+  // Go through the running queue to count the instances.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+    TcpPcb    = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    TcpProto  = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+    if (TcpProto->TcpService == TcpService) {\r
+      //\r
+      // This tcp instance belongs to the TcpService.\r
+      //\r
+      NumConfiguredInstance++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Go through the listening queue to count the instances.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+    TcpPcb    = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    TcpProto  = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+    if (TcpProto->TcpService == TcpService) {\r
+      //\r
+      // This tcp instance belongs to the TcpService.\r
+      //\r
+      NumConfiguredInstance++;\r
+    }\r
+  }\r
+\r
+  Tcp4ServicePoint = NULL;\r
+  Tcp6ServicePoint = NULL;\r
+\r
+  //\r
+  // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child,\r
+  // we should add extra buffers for the service points only if the number of configured\r
+  // children is more than one.\r
+  //\r
+  if (TcpService->IpVersion == IP_VERSION_4) {\r
+    VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA);\r
+\r
+    if (NumConfiguredInstance > 1) {\r
+      VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
+    }\r
+\r
+    Tcp4VariableData = AllocateZeroPool (VariableDataSize);\r
+    if (Tcp4VariableData == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    Tcp4VariableData->DriverHandle  = TcpService->DriverBindingHandle;\r
+    Tcp4VariableData->ServiceCount  = NumConfiguredInstance;\r
+\r
+    Tcp4ServicePoint                = &Tcp4VariableData->Services[0];\r
+    VariableData                    = Tcp4VariableData;\r
+  } else {\r
+    VariableDataSize = sizeof (EFI_TCP6_VARIABLE_DATA);\r
+\r
+    if (NumConfiguredInstance > 1) {\r
+      VariableDataSize += sizeof (EFI_TCP6_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
+    }\r
+\r
+    Tcp6VariableData = AllocateZeroPool (VariableDataSize);\r
+    if (Tcp6VariableData == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    Tcp6VariableData->DriverHandle  = TcpService->DriverBindingHandle;\r
+    Tcp6VariableData->ServiceCount  = NumConfiguredInstance;\r
+\r
+    Tcp6ServicePoint                = &Tcp6VariableData->Services[0];\r
+    VariableData                    = Tcp6VariableData;\r
+  }\r
+\r
+  //\r
+  // Go through the running queue to fill the service points.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+    TcpPcb    = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    TcpProto  = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+    if (TcpProto->TcpService == TcpService) {\r
+      //\r
+      // This tcp instance belongs to the TcpService.\r
+      //\r
+      if (TcpService->IpVersion == IP_VERSION_4) {\r
+        Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
+        CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+        Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
+        CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+        Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+        Tcp4ServicePoint++;\r
+      } else {\r
+        Tcp6ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
+        IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);\r
+        Tcp6ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
+        IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);\r
+        Tcp6ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+        Tcp6ServicePoint++;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Go through the listening queue to fill the service points.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+    TcpPcb    = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    TcpProto  = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+    if (TcpProto->TcpService == TcpService) {\r
+      //\r
+      // This tcp instance belongs to the TcpService.\r
+      //\r
+      if (TcpService->IpVersion == IP_VERSION_4) {\r
+        Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
+        CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+        Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
+        CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+        Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+        Tcp4ServicePoint++;\r
+      } else {\r
+        Tcp6ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
+        IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);\r
+        Tcp6ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
+        IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);\r
+        Tcp6ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+        Tcp6ServicePoint++;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Get the mac string.\r
+  //\r
+  Status = NetLibGetMacString (\r
+             TcpService->ControllerHandle,\r
+             TcpService->DriverBindingHandle,\r
+             &NewMacString\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (TcpService->MacString != NULL) {\r
+    //\r
+    // The variable is set already. We're going to update it.\r
+    //\r
+    if (StrCmp (TcpService->MacString, NewMacString) != 0) {\r
+      //\r
+      // The mac address is changed. Delete the previous variable first.\r
+      //\r
+      gRT->SetVariable (\r
+             TcpService->MacString,\r
+             ServiceBindingGuid,\r
+             EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+             0,\r
+             NULL\r
+             );\r
+    }\r
+\r
+    FreePool (TcpService->MacString);\r
+  }\r
+\r
+  TcpService->MacString = NewMacString;\r
+\r
+  Status = gRT->SetVariable (\r
+                  TcpService->MacString,\r
+                  ServiceBindingGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                  VariableDataSize,\r
+                  VariableData\r
+                  );\r
+\r
+ON_ERROR:\r
+\r
+  FreePool (VariableData);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Clear the variable and free the resource.\r
+\r
+  @param[in]  TcpService            Tcp service data.\r
+\r
+**/\r
+VOID\r
+TcpClearVariableData (\r
+  IN TCP_SERVICE_DATA  *TcpService\r
+  )\r
+{\r
+  EFI_GUID  *ServiceBindingGuid;\r
+\r
+  ASSERT (TcpService->MacString != NULL);\r
+\r
+  if (TcpService->IpVersion == IP_VERSION_4) {\r
+    ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+  } else {\r
+    ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+  }\r
+\r
+  gRT->SetVariable (\r
+         TcpService->MacString,\r
+         ServiceBindingGuid,\r
+         EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+         0,\r
+         NULL\r
+         );\r
+\r
+  FreePool (TcpService->MacString);\r
+  TcpService->MacString = NULL;\r
+}\r
+\r
+/**\r
+  Install the device path protocol on the TCP instance.\r
+\r
+  @param[in]  Sock          Pointer to the socket representing the TCP instance.\r
+\r
+  @retval EFI_SUCCESS           The device path protocol was installed.\r
+  @retval other                 Failed to install the device path protocol.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpInstallDevicePath (\r
+  IN SOCKET *Sock\r
+  )\r
+{\r
+  TCP_PROTO_DATA           *TcpProto;\r
+  TCP_SERVICE_DATA         *TcpService;\r
+  TCP_CB                   *Tcb;\r
+  IPv4_DEVICE_PATH         Ip4DPathNode;\r
+  IPv6_DEVICE_PATH         Ip6DPathNode;\r
+  EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+  EFI_STATUS               Status;\r
+  TCP_PORTNO               LocalPort;\r
+  TCP_PORTNO               RemotePort;\r
+\r
+  TcpProto   = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+  TcpService = TcpProto->TcpService;\r
+  Tcb        = TcpProto->TcpPcb;\r
+\r
+  LocalPort = NTOHS (Tcb->LocalEnd.Port);\r
+  RemotePort = NTOHS (Tcb->RemoteEnd.Port);\r
+  if (Sock->IpVersion == IP_VERSION_4) {\r
+    NetLibCreateIPv4DPathNode (\r
+      &Ip4DPathNode,\r
+      TcpService->ControllerHandle,\r
+      Tcb->LocalEnd.Ip.Addr[0],\r
+      LocalPort,\r
+      Tcb->RemoteEnd.Ip.Addr[0],\r
+      RemotePort,\r
+      EFI_IP_PROTO_TCP,\r
+      Tcb->UseDefaultAddr\r
+      );\r
+\r
+    DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode;\r
+  } else {\r
+    NetLibCreateIPv6DPathNode (\r
+      &Ip6DPathNode,\r
+      TcpService->ControllerHandle,\r
+      &Tcb->LocalEnd.Ip.v6,\r
+      LocalPort,\r
+      &Tcb->RemoteEnd.Ip.v6,\r
+      RemotePort,\r
+      EFI_IP_PROTO_TCP\r
+      );\r
+\r
+    DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip6DPathNode;\r
+  }\r
+\r
+  Sock->DevicePath = AppendDevicePathNode (Sock->ParentDevicePath, DevicePath);\r
+  if (Sock->DevicePath == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &Sock->SockHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  Sock->DevicePath\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Sock->DevicePath);\r
+    Sock->DevicePath = NULL;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/TcpDxe/TcpOption.c b/NetworkPkg/TcpDxe/TcpOption.c
new file mode 100644 (file)
index 0000000..bacce10
--- /dev/null
@@ -0,0 +1,374 @@
+/** @file\r
+  Routines to process TCP option.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+  Get a UINT16 value from buffer.\r
+\r
+  @param[in] Buf              Pointer to input buffer.\r
+\r
+  @return                     The UINT16 value obtained from the buffer.\r
+\r
+**/\r
+UINT16\r
+TcpGetUint16 (\r
+  IN UINT8 *Buf\r
+  )\r
+{\r
+  UINT16  Value;\r
+  CopyMem (&Value, Buf, sizeof (UINT16));\r
+  return NTOHS (Value);\r
+}\r
+\r
+/**\r
+  Get a UINT32 value from buffer.\r
+\r
+  @param[in] Buf              Pointer to input buffer.\r
+\r
+  @return                     The UINT32 value obtained from the buffer.\r
+\r
+**/\r
+UINT32\r
+TcpGetUint32 (\r
+  IN UINT8 *Buf\r
+  )\r
+{\r
+  UINT32  Value;\r
+  CopyMem (&Value, Buf, sizeof (UINT32));\r
+  return NTOHL (Value);\r
+}\r
+\r
+/**\r
+  Put a UINT32 value in buffer.\r
+\r
+  @param[out] Buf             Pointer to the buffer.\r
+  @param[in] Data             The UINT32 Date to put in the buffer.\r
+\r
+**/\r
+VOID\r
+TcpPutUint32 (\r
+     OUT UINT8  *Buf,\r
+  IN     UINT32 Data\r
+  )\r
+{\r
+  Data = HTONL (Data);\r
+  CopyMem (Buf, &Data, sizeof (UINT32));\r
+}\r
+\r
+/**\r
+  Compute the window scale value according to the given buffer size.\r
+\r
+  @param[in]  Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @return         The scale value.\r
+\r
+**/\r
+UINT8\r
+TcpComputeScale (\r
+  IN TCP_CB *Tcb\r
+  )\r
+{\r
+  UINT8   Scale;\r
+  UINT32  BufSize;\r
+\r
+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
+\r
+  BufSize = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+\r
+  Scale   = 0;\r
+  while ((Scale < TCP_OPTION_MAX_WS) && ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) {\r
+\r
+    Scale++;\r
+  }\r
+\r
+  return Scale;\r
+}\r
+\r
+/**\r
+  Build the TCP option in three-way handshake.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Nbuf    Pointer to the buffer to store the options.\r
+\r
+  @return             The total length of the TCP option field.\r
+\r
+**/\r
+UINT16\r
+TcpSynBuildOption (\r
+  IN TCP_CB  *Tcb,\r
+  IN NET_BUF *Nbuf\r
+  )\r
+{\r
+  UINT8   *Data;\r
+  UINT16  Len;\r
+\r
+  ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));\r
+\r
+  Len = 0;\r
+\r
+  //\r
+  // Add a timestamp option if not disabled by the application\r
+  // and it is the first SYN segment, or the peer has sent\r
+  // us its timestamp.\r
+  //\r
+  if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) &&\r
+      (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||\r
+        TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS))\r
+      ) {\r
+\r
+    Data = NetbufAllocSpace (\r
+             Nbuf,\r
+             TCP_OPTION_TS_ALIGNED_LEN,\r
+             NET_BUF_HEAD\r
+             );\r
+\r
+    ASSERT (Data != NULL);\r
+    Len += TCP_OPTION_TS_ALIGNED_LEN;\r
+\r
+    TcpPutUint32 (Data, TCP_OPTION_TS_FAST);\r
+    TcpPutUint32 (Data + 4, mTcpTick);\r
+    TcpPutUint32 (Data + 8, 0);\r
+  }\r
+\r
+  //\r
+  // Build window scale option, only when configured\r
+  // to send WS option, and either we are doing active\r
+  // open or we have received WS option from peer.\r
+  //\r
+  if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) &&\r
+      (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||\r
+        TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS))\r
+      ) {\r
+\r
+    Data = NetbufAllocSpace (\r
+             Nbuf,\r
+             TCP_OPTION_WS_ALIGNED_LEN,\r
+             NET_BUF_HEAD\r
+             );\r
+\r
+    ASSERT (Data != NULL);\r
+\r
+    Len += TCP_OPTION_WS_ALIGNED_LEN;\r
+    TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb));\r
+  }\r
+\r
+  //\r
+  // Build the MSS option.\r
+  //\r
+  Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1);\r
+  ASSERT (Data != NULL);\r
+\r
+  Len += TCP_OPTION_MSS_LEN;\r
+  TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss);\r
+\r
+  return Len;\r
+}\r
+\r
+/**\r
+  Build the TCP option in synchronized states.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Nbuf    Pointer to the buffer to store the options.\r
+\r
+  @return             The total length of the TCP option field.\r
+\r
+**/\r
+UINT16\r
+TcpBuildOption (\r
+  IN TCP_CB  *Tcb,\r
+  IN NET_BUF *Nbuf\r
+  )\r
+{\r
+  UINT8   *Data;\r
+  UINT16  Len;\r
+\r
+  ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));\r
+  Len = 0;\r
+\r
+  //\r
+  // Build the Timestamp option.\r
+  //\r
+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) &&\r
+      !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST)\r
+      ) {\r
+\r
+    Data = NetbufAllocSpace (\r
+            Nbuf,\r
+            TCP_OPTION_TS_ALIGNED_LEN,\r
+            NET_BUF_HEAD\r
+            );\r
+\r
+    ASSERT (Data != NULL);\r
+    Len += TCP_OPTION_TS_ALIGNED_LEN;\r
+\r
+    TcpPutUint32 (Data, TCP_OPTION_TS_FAST);\r
+    TcpPutUint32 (Data + 4, mTcpTick);\r
+    TcpPutUint32 (Data + 8, Tcb->TsRecent);\r
+  }\r
+\r
+  return Len;\r
+}\r
+\r
+/**\r
+  Parse the supported options.\r
+\r
+  @param[in]       Tcp     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in, out]  Option  Pointer to the TCP_OPTION used to store the\r
+                           successfully pasrsed options.\r
+\r
+  @retval          0       The options are successfully pasrsed.\r
+  @retval          -1      Ilegal option was found.\r
+\r
+**/\r
+INTN\r
+TcpParseOption (\r
+  IN     TCP_HEAD   *Tcp,\r
+  IN OUT TCP_OPTION *Option\r
+  )\r
+{\r
+  UINT8 *Head;\r
+  UINT8 TotalLen;\r
+  UINT8 Cur;\r
+  UINT8 Type;\r
+  UINT8 Len;\r
+\r
+  ASSERT ((Tcp != NULL) && (Option != NULL));\r
+\r
+  Option->Flag  = 0;\r
+\r
+  TotalLen      = (UINT8) ((Tcp->HeadLen << 2) - sizeof (TCP_HEAD));\r
+  if (TotalLen <= 0) {\r
+    return 0;\r
+  }\r
+\r
+  Head = (UINT8 *) (Tcp + 1);\r
+\r
+  //\r
+  // Fast process of the timestamp option.\r
+  //\r
+  if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) {\r
+\r
+    Option->TSVal = TcpGetUint32 (Head + 4);\r
+    Option->TSEcr = TcpGetUint32 (Head + 8);\r
+    Option->Flag  = TCP_OPTION_RCVD_TS;\r
+\r
+    return 0;\r
+  }\r
+  //\r
+  // Slow path to process the options.\r
+  //\r
+  Cur = 0;\r
+\r
+  while (Cur < TotalLen) {\r
+    Type = Head[Cur];\r
+\r
+    switch (Type) {\r
+    case TCP_OPTION_MSS:\r
+      Len = Head[Cur + 1];\r
+\r
+      if ((Len != TCP_OPTION_MSS_LEN) || (TotalLen - Cur < TCP_OPTION_MSS_LEN)) {\r
+\r
+        return -1;\r
+      }\r
+\r
+      Option->Mss = TcpGetUint16 (&Head[Cur + 2]);\r
+      TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS);\r
+\r
+      Cur += TCP_OPTION_MSS_LEN;\r
+      break;\r
+\r
+    case TCP_OPTION_WS:\r
+      Len = Head[Cur + 1];\r
+\r
+      if ((Len != TCP_OPTION_WS_LEN) || (TotalLen - Cur < TCP_OPTION_WS_LEN)) {\r
+\r
+        return -1;\r
+      }\r
+\r
+      Option->WndScale = (UINT8) MIN (14, Head[Cur + 2]);\r
+      TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS);\r
+\r
+      Cur += TCP_OPTION_WS_LEN;\r
+      break;\r
+\r
+    case TCP_OPTION_TS:\r
+      Len = Head[Cur + 1];\r
+\r
+      if ((Len != TCP_OPTION_TS_LEN) || (TotalLen - Cur < TCP_OPTION_TS_LEN)) {\r
+\r
+        return -1;\r
+      }\r
+\r
+      Option->TSVal = TcpGetUint32 (&Head[Cur + 2]);\r
+      Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]);\r
+      TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS);\r
+\r
+      Cur += TCP_OPTION_TS_LEN;\r
+      break;\r
+\r
+    case TCP_OPTION_NOP:\r
+      Cur++;\r
+      break;\r
+\r
+    case TCP_OPTION_EOP:\r
+      Cur = TotalLen;\r
+      break;\r
+\r
+    default:\r
+      Len = Head[Cur + 1];\r
+\r
+      if ((TotalLen - Cur) < Len || Len < 2) {\r
+        return -1;\r
+      }\r
+\r
+      Cur = (UINT8) (Cur + Len);\r
+      break;\r
+    }\r
+\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Check the segment against PAWS.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  TSVal   The timestamp value.\r
+\r
+  @retval     1       The segment passed the PAWS check.\r
+  @retval     0       The segment failed to pass the PAWS check.\r
+\r
+**/\r
+UINT32\r
+TcpPawsOK (\r
+  IN TCP_CB *Tcb,\r
+  IN UINT32 TSVal\r
+  )\r
+{\r
+  //\r
+  // PAWS as defined in RFC1323, buggy...\r
+  //\r
+  if (TCP_TIME_LT (TSVal, Tcb->TsRecent) &&\r
+      TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick)\r
+      ) {\r
+\r
+    return 0;\r
+\r
+  }\r
+\r
+  return 1;\r
+}\r
diff --git a/NetworkPkg/TcpDxe/TcpOption.h b/NetworkPkg/TcpDxe/TcpOption.h
new file mode 100644 (file)
index 0000000..0ccadb9
--- /dev/null
@@ -0,0 +1,145 @@
+/** @file\r
+  Tcp option's routine header file.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _TCP_OPTION_H_\r
+#define _TCP_OPTION_H_\r
+\r
+//\r
+// Supported TCP option types and their length.\r
+//\r
+#define TCP_OPTION_EOP             0  ///< End Of oPtion\r
+#define TCP_OPTION_NOP             1  ///< No-Option.\r
+#define TCP_OPTION_MSS             2  ///< Maximum Segment Size\r
+#define TCP_OPTION_WS              3  ///< Window scale\r
+#define TCP_OPTION_TS              8  ///< Timestamp\r
+#define TCP_OPTION_MSS_LEN         4  ///< Length of MSS option\r
+#define TCP_OPTION_WS_LEN          3  ///< Length of window scale option\r
+#define TCP_OPTION_TS_LEN          10 ///< Length of timestamp option\r
+#define TCP_OPTION_WS_ALIGNED_LEN  4  ///< Length of window scale option, aligned\r
+#define TCP_OPTION_TS_ALIGNED_LEN  12 ///< Length of timestamp option, aligned\r
+\r
+//\r
+// recommend format of timestamp window scale\r
+// option for fast process.\r
+//\r
+#define TCP_OPTION_TS_FAST ((TCP_OPTION_NOP << 24) | \\r
+                            (TCP_OPTION_NOP << 16) | \\r
+                            (TCP_OPTION_TS << 8)   | \\r
+                            (TCP_OPTION_TS_LEN))\r
+\r
+#define TCP_OPTION_WS_FAST   ((TCP_OPTION_NOP << 24) | \\r
+                              (TCP_OPTION_WS << 16)  | \\r
+                              (TCP_OPTION_WS_LEN << 8))\r
+\r
+#define TCP_OPTION_MSS_FAST  ((TCP_OPTION_MSS << 24) | (TCP_OPTION_MSS_LEN << 16))\r
+\r
+//\r
+// Other misc definations\r
+//\r
+#define TCP_OPTION_RCVD_MSS        0x01\r
+#define TCP_OPTION_RCVD_WS         0x02\r
+#define TCP_OPTION_RCVD_TS         0x04\r
+#define TCP_OPTION_MAX_WS          14      ///< Maxium window scale value\r
+#define TCP_OPTION_MAX_WIN         0xffff  ///< Max window size in TCP header\r
+\r
+///\r
+/// The structure to store the parse option value.\r
+/// ParseOption only parses the options, doesn't process them.\r
+///\r
+typedef struct _TCP_OPTION {\r
+  UINT8   Flag;     ///< Flag such as TCP_OPTION_RCVD_MSS\r
+  UINT8   WndScale; ///< The WndScale received\r
+  UINT16  Mss;      ///< The Mss received\r
+  UINT32  TSVal;    ///< The TSVal field in a timestamp option\r
+  UINT32  TSEcr;    ///< The TSEcr field in a timestamp option\r
+} TCP_OPTION;\r
+\r
+/**\r
+  Compute the window scale value according to the given buffer size.\r
+\r
+  @param[in]  Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @return         The scale value.\r
+\r
+**/\r
+UINT8\r
+TcpComputeScale (\r
+  IN TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Build the TCP option in three-way handshake.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Nbuf    Pointer to the buffer to store the options.\r
+\r
+  @return             The total length of the TCP option field.\r
+\r
+**/\r
+UINT16\r
+TcpSynBuildOption (\r
+  IN TCP_CB  *Tcb,\r
+  IN NET_BUF *Nbuf\r
+  );\r
+\r
+/**\r
+  Build the TCP option in synchronized states.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Nbuf    Pointer to the buffer to store the options.\r
+\r
+  @return             The total length of the TCP option field.\r
+\r
+**/\r
+UINT16\r
+TcpBuildOption (\r
+  IN TCP_CB  *Tcb,\r
+  IN NET_BUF *Nbuf\r
+  );\r
+\r
+/**\r
+  Parse the supported options.\r
+\r
+  @param[in]       Tcp     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in, out]  Option  Pointer to the TCP_OPTION used to store the\r
+                           successfully pasrsed options.\r
+\r
+  @retval          0       The options successfully pasrsed.\r
+  @retval          -1      Ilegal option was found.\r
+\r
+**/\r
+INTN\r
+TcpParseOption (\r
+  IN     TCP_HEAD   *Tcp,\r
+  IN OUT TCP_OPTION *Option\r
+  );\r
+\r
+/**\r
+  Check the segment against PAWS.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  TSVal   The timestamp value.\r
+\r
+  @retval     1       The segment passed the PAWS check.\r
+  @retval     0       The segment failed to pass the PAWS check.\r
+\r
+**/\r
+UINT32\r
+TcpPawsOK (\r
+  IN TCP_CB *Tcb,\r
+  IN UINT32 TSVal\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/TcpDxe/TcpOutput.c b/NetworkPkg/TcpDxe/TcpOutput.c
new file mode 100644 (file)
index 0000000..c038213
--- /dev/null
@@ -0,0 +1,1219 @@
+/** @file\r
+  TCP output process routines.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+UINT8  mTcpOutFlag[] = {\r
+  0,                          // TCP_CLOSED\r
+  0,                          // TCP_LISTEN\r
+  TCP_FLG_SYN,                // TCP_SYN_SENT\r
+  TCP_FLG_SYN | TCP_FLG_ACK,  // TCP_SYN_RCVD\r
+  TCP_FLG_ACK,                // TCP_ESTABLISHED\r
+  TCP_FLG_FIN | TCP_FLG_ACK,  // TCP_FIN_WAIT_1\r
+  TCP_FLG_ACK,                // TCP_FIN_WAIT_2\r
+  TCP_FLG_ACK | TCP_FLG_FIN,  // TCP_CLOSING\r
+  TCP_FLG_ACK,                // TCP_TIME_WAIT\r
+  TCP_FLG_ACK,                // TCP_CLOSE_WAIT\r
+  TCP_FLG_FIN | TCP_FLG_ACK   // TCP_LAST_ACK\r
+};\r
+\r
+/**\r
+  Compute the sequence space left in the old receive window.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @return The sequence space left in the old receive window.\r
+\r
+**/\r
+UINT32\r
+TcpRcvWinOld (\r
+  IN TCP_CB *Tcb\r
+  )\r
+{\r
+  UINT32  OldWin;\r
+\r
+  OldWin = 0;\r
+\r
+  if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) {\r
+\r
+    OldWin = TCP_SUB_SEQ (\r
+              Tcb->RcvWl2 + Tcb->RcvWnd,\r
+              Tcb->RcvNxt\r
+              );\r
+  }\r
+\r
+  return OldWin;\r
+}\r
+\r
+/**\r
+  Compute the current receive window.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @return The size of the current receive window, in bytes.\r
+\r
+**/\r
+UINT32\r
+TcpRcvWinNow (\r
+  IN TCP_CB *Tcb\r
+  )\r
+{\r
+  SOCKET  *Sk;\r
+  UINT32  Win;\r
+  UINT32  Increase;\r
+  UINT32  OldWin;\r
+\r
+  Sk = Tcb->Sk;\r
+  ASSERT (Sk != NULL);\r
+\r
+  OldWin    = TcpRcvWinOld (Tcb);\r
+\r
+  Win       = SockGetFreeSpace (Sk, SOCK_RCV_BUF);\r
+\r
+  Increase  = 0;\r
+  if (Win > OldWin) {\r
+    Increase = Win - OldWin;\r
+  }\r
+\r
+  //\r
+  // Receiver's SWS: don't advertise a bigger window\r
+  // unless it can be increased by at least one Mss or\r
+  // half of the receive buffer.\r
+  //\r
+  if ((Increase > Tcb->SndMss) || (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) {\r
+\r
+    return Win;\r
+  }\r
+\r
+  return OldWin;\r
+}\r
+\r
+/**\r
+  Compute the value to fill in the window size field of the outgoing segment.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Syn     The flag to indicate whether the outgoing segment\r
+                           is a SYN segment.\r
+\r
+  @return The value of the local receive window size used to fill the outgoing segment.\r
+\r
+**/\r
+UINT16\r
+TcpComputeWnd (\r
+  IN OUT TCP_CB  *Tcb,\r
+  IN     BOOLEAN Syn\r
+  )\r
+{\r
+  UINT32  Wnd;\r
+\r
+  //\r
+  // RFC requires that initial window not be scaled\r
+  //\r
+  if (Syn) {\r
+\r
+    Wnd = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+  } else {\r
+\r
+    Wnd         = TcpRcvWinNow (Tcb);\r
+\r
+    Tcb->RcvWnd = Wnd;\r
+  }\r
+\r
+  Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff);\r
+  return NTOHS ((UINT16) Wnd);\r
+}\r
+\r
+/**\r
+  Get the maximum SndNxt.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @return The sequence number of the maximum SndNxt.\r
+\r
+**/\r
+TCP_SEQNO\r
+TcpGetMaxSndNxt (\r
+  IN TCP_CB *Tcb\r
+  )\r
+{\r
+  LIST_ENTRY      *Entry;\r
+  NET_BUF         *Nbuf;\r
+\r
+  if (IsListEmpty (&Tcb->SndQue)) {\r
+    return Tcb->SndNxt;\r
+  }\r
+\r
+  Entry = Tcb->SndQue.BackLink;\r
+  Nbuf  = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+\r
+  ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt));\r
+  return TCPSEG_NETBUF (Nbuf)->End;\r
+}\r
+\r
+/**\r
+  Compute how much data to send.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Force   If TRUE, to ignore the sender's SWS avoidance algorithm and send\r
+                      out data by force.\r
+\r
+  @return The length of the data can be sent. If 0, no data can be sent.\r
+\r
+**/\r
+UINT32\r
+TcpDataToSend (\r
+  IN TCP_CB *Tcb,\r
+  IN INTN   Force\r
+  )\r
+{\r
+  SOCKET  *Sk;\r
+  UINT32  Win;\r
+  UINT32  Len;\r
+  UINT32  Left;\r
+  UINT32  Limit;\r
+\r
+  Sk = Tcb->Sk;\r
+  ASSERT (Sk != NULL);\r
+\r
+  //\r
+  // TCP should NOT send data beyond the send window\r
+  // and congestion window. The right edge of send\r
+  // window is defined as SND.WL2 + SND.WND. The right\r
+  // edge of congestion window is defined as SND.UNA +\r
+  // CWND.\r
+  //\r
+  Win   = 0;\r
+  Limit = Tcb->SndWl2 + Tcb->SndWnd;\r
+\r
+  if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) {\r
+\r
+    Limit = Tcb->SndUna + Tcb->CWnd;\r
+  }\r
+\r
+  if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) {\r
+    Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt);\r
+  }\r
+\r
+  //\r
+  // The data to send contains two parts: the data on the\r
+  // socket send queue, and the data on the TCB's send\r
+  // buffer. The later can be non-zero if the peer shrinks\r
+  // its advertised window.\r
+  //\r
+  Left  = GET_SND_DATASIZE (Sk) + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt);\r
+\r
+  Len   = MIN (Win, Left);\r
+\r
+  if (Len > Tcb->SndMss) {\r
+    Len = Tcb->SndMss;\r
+  }\r
+\r
+  if ((Force != 0)|| (Len == 0 && Left == 0)) {\r
+    return Len;\r
+  }\r
+\r
+  if (Len == 0 && Left != 0) {\r
+    goto SetPersistTimer;\r
+  }\r
+\r
+  //\r
+  // Sender's SWS avoidance: Don't send a small segment unless\r
+  // a)A full-sized segment can be sent,\r
+  // b)At least one-half of the maximum sized windows that\r
+  // the other end has ever advertised.\r
+  // c)It can send everything it has, and either it isn't\r
+  // expecting an ACK, or the Nagle algorithm is disabled.\r
+  //\r
+  if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) {\r
+\r
+    return Len;\r
+  }\r
+\r
+  if ((Len == Left) &&\r
+      ((Tcb->SndNxt == Tcb->SndUna) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))\r
+      ) {\r
+\r
+    return Len;\r
+  }\r
+\r
+  //\r
+  // RFC1122 suggests to set a timer when SWSA forbids TCP\r
+  // sending more data, and combines it with a probe timer.\r
+  //\r
+SetPersistTimer:\r
+  if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_WARN,\r
+      "TcpDataToSend: enter persistent state for TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    if (!Tcb->ProbeTimerOn) {\r
+      TcpSetProbeTimer (Tcb);\r
+    }\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Build the TCP header of the TCP segment and transmit the segment by IP.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Nbuf    Pointer to the buffer containing the segment to be\r
+                           sent out.\r
+\r
+  @retval 0       The segment was sent out successfully.\r
+  @retval -1      An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpTransmitSegment (\r
+  IN OUT TCP_CB  *Tcb,\r
+  IN     NET_BUF *Nbuf\r
+  )\r
+{\r
+  UINT16    Len;\r
+  TCP_HEAD  *Head;\r
+  TCP_SEG   *Seg;\r
+  BOOLEAN   Syn;\r
+  UINT32    DataLen;\r
+\r
+  ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0));\r
+\r
+  DataLen = Nbuf->TotalSize;\r
+\r
+  Seg     = TCPSEG_NETBUF (Nbuf);\r
+  Syn     = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN);\r
+\r
+  if (Syn) {\r
+\r
+    Len = TcpSynBuildOption (Tcb, Nbuf);\r
+  } else {\r
+\r
+    Len = TcpBuildOption (Tcb, Nbuf);\r
+  }\r
+\r
+  ASSERT ((Len % 4 == 0) && (Len <= 40));\r
+\r
+  Len += sizeof (TCP_HEAD);\r
+\r
+  Head = (TCP_HEAD *) NetbufAllocSpace (\r
+                        Nbuf,\r
+                        sizeof (TCP_HEAD),\r
+                        NET_BUF_HEAD\r
+                        );\r
+\r
+  ASSERT (Head != NULL);\r
+\r
+  Nbuf->Tcp       = Head;\r
+\r
+  Head->SrcPort   = Tcb->LocalEnd.Port;\r
+  Head->DstPort   = Tcb->RemoteEnd.Port;\r
+  Head->Seq       = NTOHL (Seg->Seq);\r
+  Head->Ack       = NTOHL (Tcb->RcvNxt);\r
+  Head->HeadLen   = (UINT8) (Len >> 2);\r
+  Head->Res       = 0;\r
+  Head->Wnd       = TcpComputeWnd (Tcb, Syn);\r
+  Head->Checksum  = 0;\r
+\r
+  //\r
+  // Check whether to set the PSH flag.\r
+  //\r
+  TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH);\r
+\r
+  if (DataLen != 0) {\r
+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) &&\r
+        TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)\r
+        ) {\r
+\r
+      TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);\r
+      TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);\r
+\r
+    } else if ((Seg->End == Tcb->SndNxt) && (GET_SND_DATASIZE (Tcb->Sk) == 0)) {\r
+\r
+      TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check whether to set the URG flag and the urgent pointer.\r
+  //\r
+  TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);\r
+\r
+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) {\r
+\r
+    TCP_SET_FLG (Seg->Flag, TCP_FLG_URG);\r
+\r
+    if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) {\r
+\r
+      Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq);\r
+    } else {\r
+\r
+      Seg->Urg = (UINT16) MIN (\r
+                            TCP_SUB_SEQ (Tcb->SndUp,\r
+                            Seg->Seq),\r
+                            0xffff\r
+                            );\r
+    }\r
+  }\r
+\r
+  Head->Flag      = Seg->Flag;\r
+  Head->Urg       = NTOHS (Seg->Urg);\r
+  Head->Checksum  = TcpChecksum (Nbuf, Tcb->HeadSum);\r
+\r
+  //\r
+  // Update the TCP session's control information.\r
+  //\r
+  Tcb->RcvWl2 = Tcb->RcvNxt;\r
+  if (Syn) {\r
+    Tcb->RcvWnd = NTOHS (Head->Wnd);\r
+  }\r
+\r
+  //\r
+  // Clear the delayedack flag.\r
+  //\r
+  Tcb->DelayedAck = 0;\r
+\r
+  return TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);\r
+}\r
+\r
+/**\r
+  Get a segment from the Tcb's SndQue.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Seq     The sequence number of the segment.\r
+  @param[in]  Len     The maximum length of the segment.\r
+\r
+  @return Pointer to the segment. If NULL, some error occurred.\r
+\r
+**/\r
+NET_BUF *\r
+TcpGetSegmentSndQue (\r
+  IN TCP_CB    *Tcb,\r
+  IN TCP_SEQNO Seq,\r
+  IN UINT32    Len\r
+  )\r
+{\r
+  LIST_ENTRY      *Head;\r
+  LIST_ENTRY      *Cur;\r
+  NET_BUF         *Node;\r
+  TCP_SEG         *Seg;\r
+  NET_BUF         *Nbuf;\r
+  TCP_SEQNO       End;\r
+  UINT8           *Data;\r
+  UINT8           Flag;\r
+  INT32           Offset;\r
+  INT32           CopyLen;\r
+\r
+  ASSERT ((Tcb != NULL) && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0));\r
+\r
+  //\r
+  // Find the segment that contains the Seq.\r
+  //\r
+  Head  = &Tcb->SndQue;\r
+\r
+  Node  = NULL;\r
+  Seg   = NULL;\r
+\r
+  NET_LIST_FOR_EACH (Cur, Head) {\r
+    Node  = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+    Seg   = TCPSEG_NETBUF (Node);\r
+\r
+    if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) {\r
+\r
+      break;\r
+    }\r
+  }\r
+\r
+  if ((Cur == Head) || (Seg == NULL) || (Node == NULL)) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Return the buffer if it can be returned without\r
+  // adjustment:\r
+  //\r
+  if ((Seg->Seq == Seq) &&\r
+      TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&\r
+      !NET_BUF_SHARED (Node)\r
+      ) {\r
+\r
+    NET_GET_REF (Node);\r
+    return Node;\r
+  }\r
+\r
+  //\r
+  // Create a new buffer and copy data there.\r
+  //\r
+  Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
+\r
+  if (Nbuf == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
+\r
+  Flag  = Seg->Flag;\r
+  End   = Seg->End;\r
+\r
+  if (TCP_SEQ_LT (Seq + Len, Seg->End)) {\r
+    End = Seq + Len;\r
+  }\r
+\r
+  CopyLen = TCP_SUB_SEQ (End, Seq);\r
+  Offset  = TCP_SUB_SEQ (Seq, Seg->Seq);\r
+\r
+  //\r
+  // If SYN is set and out of the range, clear the flag.\r
+  // Becuase the sequence of the first byte is SEG.SEQ+1,\r
+  // adjust Offset by -1. If SYN is in the range, copy\r
+  // one byte less.\r
+  //\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+\r
+    if (TCP_SEQ_LT (Seg->Seq, Seq)) {\r
+\r
+      TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);\r
+      Offset--;\r
+    } else {\r
+\r
+      CopyLen--;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If FIN is set and in the range, copy one byte less,\r
+  // and if it is out of the range, clear the flag.\r
+  //\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+\r
+    if (Seg->End == End) {\r
+\r
+      CopyLen--;\r
+    } else {\r
+\r
+      TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
+    }\r
+  }\r
+\r
+  ASSERT (CopyLen >= 0);\r
+\r
+  //\r
+  // Copy data to the segment\r
+  //\r
+  if (CopyLen != 0) {\r
+    Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);\r
+    ASSERT (Data != NULL);\r
+\r
+    if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {\r
+      goto OnError;\r
+    }\r
+  }\r
+\r
+  CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));\r
+\r
+  TCPSEG_NETBUF (Nbuf)->Seq   = Seq;\r
+  TCPSEG_NETBUF (Nbuf)->End   = End;\r
+  TCPSEG_NETBUF (Nbuf)->Flag  = Flag;\r
+\r
+  return Nbuf;\r
+\r
+OnError:\r
+  NetbufFree (Nbuf);\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Get a segment from the Tcb's socket buffer.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Seq     The sequence number of the segment.\r
+  @param[in]  Len     The maximum length of the segment.\r
+\r
+  @return Pointer to the segment. If NULL, some error occurred.\r
+\r
+**/\r
+NET_BUF *\r
+TcpGetSegmentSock (\r
+  IN TCP_CB    *Tcb,\r
+  IN TCP_SEQNO Seq,\r
+  IN UINT32    Len\r
+  )\r
+{\r
+  NET_BUF *Nbuf;\r
+  UINT8   *Data;\r
+  UINT32  DataGet;\r
+\r
+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
+\r
+  Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
+\r
+  if (Nbuf == NULL) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "TcpGetSegmentSock: failed to allocate a netbuf for TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    return NULL;\r
+  }\r
+\r
+  NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
+\r
+  DataGet = 0;\r
+\r
+  if (Len != 0) {\r
+    //\r
+    // copy data to the segment.\r
+    //\r
+    Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);\r
+    ASSERT (Data != NULL);\r
+\r
+    DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);\r
+  }\r
+\r
+  NET_GET_REF (Nbuf);\r
+\r
+  TCPSEG_NETBUF (Nbuf)->Seq = Seq;\r
+  TCPSEG_NETBUF (Nbuf)->End = Seq + Len;\r
+\r
+  InsertTailList (&(Tcb->SndQue), &(Nbuf->List));\r
+\r
+  if (DataGet != 0) {\r
+\r
+    SockDataSent (Tcb->Sk, DataGet);\r
+  }\r
+\r
+  return Nbuf;\r
+}\r
+\r
+/**\r
+  Get a segment starting from sequence Seq of a maximum\r
+  length of Len.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Seq     The sequence number of the segment.\r
+  @param[in]  Len     The maximum length of the segment.\r
+\r
+  @return Pointer to the segment. If NULL, some error occurred.\r
+\r
+**/\r
+NET_BUF *\r
+TcpGetSegment (\r
+  IN TCP_CB    *Tcb,\r
+  IN TCP_SEQNO Seq,\r
+  IN UINT32    Len\r
+  )\r
+{\r
+  NET_BUF *Nbuf;\r
+\r
+  ASSERT (Tcb != NULL);\r
+\r
+  //\r
+  // Compare the SndNxt with the max sequence number sent.\r
+  //\r
+  if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {\r
+\r
+    Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
+  } else {\r
+\r
+    Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);\r
+  }\r
+\r
+  ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+  return Nbuf;\r
+}\r
+\r
+/**\r
+  Retransmit the segment from sequence Seq.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]  Seq     The sequence number of the segment to be retransmitted.\r
+\r
+  @retval 0       Retransmission succeeded.\r
+  @retval -1      Error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpRetransmit (\r
+  IN TCP_CB    *Tcb,\r
+  IN TCP_SEQNO Seq\r
+  )\r
+{\r
+  NET_BUF *Nbuf;\r
+  UINT32  Len;\r
+\r
+  //\r
+  // Compute the maxium length of retransmission. It is\r
+  // limited by three factors:\r
+  // 1. Less than SndMss\r
+  // 2. Must in the current send window\r
+  // 3. Will not change the boundaries of queued segments.\r
+  //\r
+  if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {\r
+    DEBUG (\r
+      (EFI_D_WARN,\r
+      "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    return 0;\r
+  }\r
+\r
+  Len   = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);\r
+  Len   = MIN (Len, Tcb->SndMss);\r
+\r
+  Nbuf  = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
+  if (Nbuf == NULL) {\r
+    return -1;\r
+  }\r
+\r
+  ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+\r
+  if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
+    goto OnError;\r
+  }\r
+\r
+  //\r
+  // The retransmitted buffer may be on the SndQue,\r
+  // trim TCP head because all the buffers on SndQue\r
+  // are headless.\r
+  //\r
+  ASSERT (Nbuf->Tcp != NULL);\r
+  NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
+  Nbuf->Tcp = NULL;\r
+\r
+  NetbufFree (Nbuf);\r
+  return 0;\r
+\r
+OnError:\r
+  if (Nbuf != NULL) {\r
+    NetbufFree (Nbuf);\r
+  }\r
+\r
+  return -1;\r
+}\r
+\r
+/**\r
+  Verify that all the segments in SndQue are in good shape.\r
+\r
+  @param[in]  Head    Pointer to the head node of the SndQue.\r
+\r
+  @retval     0       At least one segment is broken.\r
+  @retval     1       All segments in the specific queue are in good shape.\r
+\r
+**/\r
+INTN\r
+TcpCheckSndQue (\r
+  IN LIST_ENTRY     *Head\r
+  )\r
+{\r
+  LIST_ENTRY      *Entry;\r
+  NET_BUF         *Nbuf;\r
+  TCP_SEQNO       Seq;\r
+\r
+  if (IsListEmpty (Head)) {\r
+    return 1;\r
+  }\r
+  //\r
+  // Initialize the Seq.\r
+  //\r
+  Entry = Head->ForwardLink;\r
+  Nbuf  = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+  Seq   = TCPSEG_NETBUF (Nbuf)->Seq;\r
+\r
+  NET_LIST_FOR_EACH (Entry, Head) {\r
+    Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+\r
+    if (TcpVerifySegment (Nbuf) == 0) {\r
+      return 0;\r
+    }\r
+\r
+    //\r
+    // All the node in the SndQue should has:\r
+    // SEG.SEQ = LAST_SEG.END\r
+    //\r
+    if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {\r
+      return 0;\r
+    }\r
+\r
+    Seq = TCPSEG_NETBUF (Nbuf)->End;\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
+/**\r
+  Check whether to send data/SYN/FIN and piggyback an ACK.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Force   If TRUE, ignore the sender's SWS avoidance algorithm\r
+                           and send out data by force.\r
+\r
+  @return The number of bytes sent.\r
+\r
+**/\r
+INTN\r
+TcpToSendData (\r
+  IN OUT TCP_CB *Tcb,\r
+  IN     INTN   Force\r
+  )\r
+{\r
+  UINT32    Len;\r
+  INTN      Sent;\r
+  UINT8     Flag;\r
+  NET_BUF   *Nbuf;\r
+  TCP_SEG   *Seg;\r
+  TCP_SEQNO Seq;\r
+  TCP_SEQNO End;\r
+\r
+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN));\r
+\r
+  Sent = 0;\r
+\r
+  if ((Tcb->State == TCP_CLOSED) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {\r
+\r
+    return 0;\r
+  }\r
+\r
+  do {\r
+    //\r
+    // Compute how much data can be sent\r
+    //\r
+    Len   = TcpDataToSend (Tcb, Force);\r
+    Seq   = Tcb->SndNxt;\r
+\r
+    ASSERT ((Tcb->State) < (sizeof (mTcpOutFlag) / sizeof (mTcpOutFlag[0])));\r
+    Flag  = mTcpOutFlag[Tcb->State];\r
+\r
+    if ((Flag & TCP_FLG_SYN) != 0) {\r
+\r
+      Seq = Tcb->Iss;\r
+      Len = 0;\r
+    }\r
+\r
+    //\r
+    // Only send a segment without data if SYN or\r
+    // FIN is set.\r
+    //\r
+    if ((Len == 0) && ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) {\r
+      return Sent;\r
+    }\r
+\r
+    Nbuf = TcpGetSegment (Tcb, Seq, Len);\r
+\r
+    if (Nbuf == NULL) {\r
+      DEBUG (\r
+        (EFI_D_ERROR,\r
+        "TcpToSendData: failed to get a segment for TCB %p\n",\r
+        Tcb)\r
+        );\r
+\r
+      goto OnError;\r
+    }\r
+\r
+    Seg = TCPSEG_NETBUF (Nbuf);\r
+\r
+    //\r
+    // Set the TcpSeg in Nbuf.\r
+    //\r
+    Len = Nbuf->TotalSize;\r
+    End = Seq + Len;\r
+    if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {\r
+      End++;\r
+    }\r
+\r
+    if ((Flag & TCP_FLG_FIN) != 0) {\r
+      //\r
+      // Send FIN if all data is sent, and FIN is\r
+      // in the window\r
+      //\r
+      if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&\r
+          (GET_SND_DATASIZE (Tcb->Sk) == 0) &&\r
+          TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)\r
+            ) {\r
+        DEBUG (\r
+          (EFI_D_INFO,\r
+          "TcpToSendData: send FIN to peer for TCB %p in state %s\n",\r
+          Tcb,\r
+          mTcpStateName[Tcb->State])\r
+          );\r
+\r
+        End++;\r
+      } else {\r
+        TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
+      }\r
+    }\r
+\r
+    Seg->Seq  = Seq;\r
+    Seg->End  = End;\r
+    Seg->Flag = Flag;\r
+\r
+    ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+    ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0);\r
+\r
+    //\r
+    // Don't send an empty segment here.\r
+    //\r
+    if (Seg->End == Seg->Seq) {\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "TcpToSendData: created a empty segment for TCB %p, free it now\n",\r
+        Tcb)\r
+        );\r
+\r
+      NetbufFree (Nbuf);\r
+      return Sent;\r
+    }\r
+\r
+    if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
+      NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
+      Nbuf->Tcp = NULL;\r
+\r
+      if ((Flag & TCP_FLG_FIN) != 0)  {\r
+        TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
+      }\r
+\r
+      goto OnError;\r
+    }\r
+\r
+    Sent += TCP_SUB_SEQ (End, Seq);\r
+\r
+    //\r
+    // All the buffers in the SndQue are headless.\r
+    //\r
+    ASSERT (Nbuf->Tcp != NULL);\r
+\r
+    NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
+    Nbuf->Tcp = NULL;\r
+\r
+    NetbufFree (Nbuf);\r
+\r
+    //\r
+    // Update the status in TCB.\r
+    //\r
+    Tcb->DelayedAck = 0;\r
+\r
+    if ((Flag & TCP_FLG_FIN) != 0) {\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
+    }\r
+\r
+    if (TCP_SEQ_GT (End, Tcb->SndNxt)) {\r
+      Tcb->SndNxt = End;\r
+    }\r
+\r
+    if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
+      TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
+    }\r
+\r
+    //\r
+    // Enable RTT measurement only if not in retransmit.\r
+    // Karn's algorithm requires not to update RTT when in loss.\r
+    //\r
+    if ((Tcb->CongestState == TCP_CONGEST_OPEN) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
+\r
+      DEBUG (\r
+        (EFI_D_INFO,\r
+        "TcpToSendData: set RTT measure sequence %d for TCB %p\n",\r
+        Seq,\r
+        Tcb)\r
+        );\r
+\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+      Tcb->RttSeq     = Seq;\r
+      Tcb->RttMeasure = 0;\r
+    }\r
+\r
+  } while (Len == Tcb->SndMss);\r
+\r
+  return Sent;\r
+\r
+OnError:\r
+  if (Nbuf != NULL) {\r
+    NetbufFree (Nbuf);\r
+  }\r
+\r
+  return Sent;\r
+}\r
+\r
+/**\r
+  Send an ACK immediately.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSendAck (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  NET_BUF *Nbuf;\r
+  TCP_SEG *Seg;\r
+\r
+  Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
+\r
+  if (Nbuf == NULL) {\r
+    return;\r
+  }\r
+\r
+  NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
+\r
+  Seg       = TCPSEG_NETBUF (Nbuf);\r
+  Seg->Seq  = Tcb->SndNxt;\r
+  Seg->End  = Tcb->SndNxt;\r
+  Seg->Flag = TCP_FLG_ACK;\r
+\r
+  if (TcpTransmitSegment (Tcb, Nbuf) == 0) {\r
+    TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+    Tcb->DelayedAck = 0;\r
+  }\r
+\r
+  NetbufFree (Nbuf);\r
+}\r
+\r
+/**\r
+  Send a zero probe segment. It can be used by keepalive and zero window probe.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+  @retval 0       The zero probe segment was sent out successfully.\r
+  @retval other   An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpSendZeroProbe (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  NET_BUF *Nbuf;\r
+  TCP_SEG *Seg;\r
+  INTN     Result;\r
+\r
+  Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
+\r
+  if (Nbuf == NULL) {\r
+    return -1;\r
+  }\r
+\r
+  NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
+\r
+  //\r
+  // SndNxt-1 is out of window. The peer should respond\r
+  // with an ACK.\r
+  //\r
+  Seg       = TCPSEG_NETBUF (Nbuf);\r
+  Seg->Seq  = Tcb->SndNxt - 1;\r
+  Seg->End  = Tcb->SndNxt - 1;\r
+  Seg->Flag = TCP_FLG_ACK;\r
+\r
+  Result    = TcpTransmitSegment (Tcb, Nbuf);\r
+  NetbufFree (Nbuf);\r
+\r
+  return Result;\r
+}\r
+\r
+/**\r
+  Check whether to send an ACK or delayed ACK.\r
+\r
+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpToSendAck (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  UINT32 TcpNow;\r
+\r
+  //\r
+  // Generally, TCP should send a delayed ACK unless:\r
+  //   1. ACK at least every other FULL sized segment received.\r
+  //   2. Packets received out of order.\r
+  //   3. Receiving window is open.\r
+  //\r
+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Tcb->DelayedAck >= 1)) {\r
+    TcpSendAck (Tcb);\r
+    return;\r
+  }\r
+\r
+  TcpNow = TcpRcvWinNow (Tcb);\r
+\r
+  if (TcpNow > TcpRcvWinOld (Tcb)) {\r
+    TcpSendAck (Tcb);\r
+    return;\r
+  }\r
+\r
+  DEBUG (\r
+    (EFI_D_INFO,\r
+    "TcpToSendAck: scheduled a delayed ACK for TCB %p\n",\r
+    Tcb)\r
+    );\r
+\r
+  //\r
+  // Schedule a delayed ACK.\r
+  //\r
+  Tcb->DelayedAck++;\r
+}\r
+\r
+/**\r
+  Send a RESET segment in response to the segment received.\r
+\r
+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance. May be NULL.\r
+  @param[in]  Head    TCP header of the segment that triggers the reset.\r
+  @param[in]  Len     Length of the segment that triggers the reset.\r
+  @param[in]  Local   Local IP address.\r
+  @param[in]  Remote  Remote peer's IP address.\r
+  @param[in]  Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+                      IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+  @retval     0       A reset was sent or there is no need to send it.\r
+  @retval     -1      No reset is sent.\r
+\r
+**/\r
+INTN\r
+TcpSendReset (\r
+  IN TCP_CB          *Tcb,\r
+  IN TCP_HEAD        *Head,\r
+  IN INT32           Len,\r
+  IN EFI_IP_ADDRESS  *Local,\r
+  IN EFI_IP_ADDRESS  *Remote,\r
+  IN UINT8           Version\r
+  )\r
+{\r
+  NET_BUF   *Nbuf;\r
+  TCP_HEAD  *Nhead;\r
+  UINT16    HeadSum;\r
+\r
+  //\r
+  // Don't respond to a Reset with reset.\r
+  //\r
+  if ((Head->Flag & TCP_FLG_RST) != 0) {\r
+    return 0;\r
+  }\r
+\r
+  Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
+\r
+  if (Nbuf == NULL) {\r
+    return -1;\r
+  }\r
+\r
+  Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
+                        Nbuf,\r
+                        sizeof (TCP_HEAD),\r
+                        NET_BUF_TAIL\r
+                        );\r
+\r
+  ASSERT (Nhead != NULL);\r
+\r
+  Nbuf->Tcp   = Nhead;\r
+  Nhead->Flag = TCP_FLG_RST;\r
+\r
+  //\r
+  // Derive Seq/ACK from the segment if no TCB\r
+  // is associated with it, otherwise derive from the Tcb.\r
+  //\r
+  if (Tcb == NULL) {\r
+\r
+    if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {\r
+      Nhead->Seq  = Head->Ack;\r
+      Nhead->Ack  = 0;\r
+    } else {\r
+      Nhead->Seq = 0;\r
+      TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
+      Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);\r
+    }\r
+  } else {\r
+\r
+    Nhead->Seq  = HTONL (Tcb->SndNxt);\r
+    Nhead->Ack  = HTONL (Tcb->RcvNxt);\r
+    TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
+  }\r
+\r
+  Nhead->SrcPort  = Head->DstPort;\r
+  Nhead->DstPort  = Head->SrcPort;\r
+  Nhead->HeadLen  = (UINT8) (sizeof (TCP_HEAD) >> 2);\r
+  Nhead->Res      = 0;\r
+  Nhead->Wnd      = HTONS (0xFFFF);\r
+  Nhead->Checksum = 0;\r
+  Nhead->Urg      = 0;\r
+\r
+  if (Version == IP_VERSION_4) {\r
+    HeadSum = NetPseudoHeadChecksum (Local->Addr[0], Remote->Addr[0], 6, 0);\r
+  } else {\r
+    HeadSum = NetIp6PseudoHeadChecksum (&Local->v6, &Remote->v6, 6, 0);\r
+  }\r
+\r
+  Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);\r
+\r
+  TcpSendIpPacket (Tcb, Nbuf, Local, Remote, Version);\r
+\r
+  NetbufFree (Nbuf);\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Verify that the segment is in good shape.\r
+\r
+  @param[in]  Nbuf    The buffer that contains the segment to be checked.\r
+\r
+  @retval     0       The segment is broken.\r
+  @retval     1       The segment is in good shape.\r
+\r
+**/\r
+INTN\r
+TcpVerifySegment (\r
+  IN NET_BUF *Nbuf\r
+  )\r
+{\r
+  TCP_HEAD  *Head;\r
+  TCP_SEG   *Seg;\r
+  UINT32    Len;\r
+\r
+  if (Nbuf == NULL) {\r
+    return 1;\r
+  }\r
+\r
+  NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
+\r
+  Seg   = TCPSEG_NETBUF (Nbuf);\r
+  Len   = Nbuf->TotalSize;\r
+  Head  = Nbuf->Tcp;\r
+\r
+  if (Head != NULL) {\r
+    if (Head->Flag != Seg->Flag) {\r
+      return 0;\r
+    }\r
+\r
+    Len -= (Head->HeadLen << 2);\r
+  }\r
+\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+    Len++;\r
+  }\r
+\r
+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+    Len++;\r
+  }\r
+\r
+  if (Seg->Seq + Len != Seg->End) {\r
+    return 0;\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
diff --git a/NetworkPkg/TcpDxe/TcpProto.h b/NetworkPkg/TcpDxe/TcpProto.h
new file mode 100644 (file)
index 0000000..88dfbb9
--- /dev/null
@@ -0,0 +1,342 @@
+/** @file\r
+  TCP protocol header file.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _TCP_PROTO_H_\r
+#define _TCP_PROTO_H_\r
+\r
+///\r
+/// Tcp states don't change their order. It is used as an\r
+/// index to mTcpOutFlag and other macros.\r
+///\r
+#define TCP_CLOSED       0\r
+#define TCP_LISTEN       1\r
+#define TCP_SYN_SENT     2\r
+#define TCP_SYN_RCVD     3\r
+#define TCP_ESTABLISHED  4\r
+#define TCP_FIN_WAIT_1   5\r
+#define TCP_FIN_WAIT_2   6\r
+#define TCP_CLOSING      7\r
+#define TCP_TIME_WAIT    8\r
+#define TCP_CLOSE_WAIT   9\r
+#define TCP_LAST_ACK     10\r
+\r
+\r
+///\r
+/// Flags in the TCP header\r
+///\r
+#define TCP_FLG_FIN      0x01\r
+#define TCP_FLG_SYN      0x02\r
+#define TCP_FLG_RST      0x04\r
+#define TCP_FLG_PSH      0x08\r
+#define TCP_FLG_ACK      0x10\r
+#define TCP_FLG_URG      0x20\r
+\r
+ //\r
+ // mask for all the flags\r
+ //\r
+#define TCP_FLG_FLAG     0x3F\r
+\r
+\r
+#define TCP_CONNECT_REFUSED      (-1) ///< TCP error status\r
+#define TCP_CONNECT_RESET        (-2) ///< TCP error status\r
+#define TCP_CONNECT_CLOSED       (-3) ///< TCP error status\r
+\r
+//\r
+// Current congestion status as suggested by RFC3782.\r
+//\r
+#define TCP_CONGEST_RECOVER      1  ///< During the NewReno fast recovery.\r
+#define TCP_CONGEST_LOSS         2  ///< Retxmit because of retxmit time out.\r
+#define TCP_CONGEST_OPEN         3  ///< TCP is opening its congestion window.\r
+\r
+//\r
+// TCP control flags\r
+//\r
+#define TCP_CTRL_NO_NAGLE        0x0001 ///< Disable Nagle algorithm\r
+#define TCP_CTRL_NO_KEEPALIVE    0x0002 ///< Disable keepalive timer.\r
+#define TCP_CTRL_NO_WS           0x0004 ///< Disable window scale option.\r
+#define TCP_CTRL_RCVD_WS         0x0008 ///< Received a wnd scale option in syn.\r
+#define TCP_CTRL_NO_TS           0x0010 ///< Disable Timestamp option.\r
+#define TCP_CTRL_RCVD_TS         0x0020 ///< Received a Timestamp option in syn.\r
+#define TCP_CTRL_SND_TS          0x0040 ///< Send Timestamp option to remote.\r
+#define TCP_CTRL_SND_URG         0x0080 ///< In urgent send mode.\r
+#define TCP_CTRL_RCVD_URG        0x0100 ///< In urgent receive mode.\r
+#define TCP_CTRL_SND_PSH         0x0200 ///< In PUSH send mode.\r
+#define TCP_CTRL_FIN_SENT        0x0400 ///< FIN is sent.\r
+#define TCP_CTRL_FIN_ACKED       0x0800 ///< FIN is ACKed.\r
+#define TCP_CTRL_TIMER_ON        0x1000 ///< At least one of the timer is on.\r
+#define TCP_CTRL_RTT_ON          0x2000 ///< The RTT measurement is on.\r
+#define TCP_CTRL_ACK_NOW         0x4000 ///< Send the ACK now, don't delay.\r
+\r
+//\r
+// Timer related values\r
+//\r
+#define TCP_TIMER_CONNECT        0                  ///< Connection establishment timer.\r
+#define TCP_TIMER_REXMIT         1                  ///< Retransmit timer.\r
+#define TCP_TIMER_PROBE          2                  ///< Window probe timer.\r
+#define TCP_TIMER_KEEPALIVE      3                  ///< Keepalive timer.\r
+#define TCP_TIMER_FINWAIT2       4                  ///< FIN_WAIT_2 timer.\r
+#define TCP_TIMER_2MSL           5                  ///< TIME_WAIT timer.\r
+#define TCP_TIMER_NUMBER         6                  ///< The total number of the TCP timer.\r
+#define TCP_TICK                 200                ///< Every TCP tick is 200ms.\r
+#define TCP_TICK_HZ              5                  ///< The frequence of TCP tick.\r
+#define TCP_RTT_SHIFT            3                  ///< SRTT & RTTVAR scaled by 8.\r
+#define TCP_RTO_MIN              TCP_TICK_HZ        ///< The minium value of RTO.\r
+#define TCP_RTO_MAX              (TCP_TICK_HZ * 60) ///< The maxium value of RTO.\r
+#define TCP_FOLD_RTT             4                  ///< Timeout threshod to fold RTT.\r
+\r
+//\r
+// Default values for some timers\r
+//\r
+#define TCP_MAX_LOSS             12                          ///< Default max times to retxmit.\r
+#define TCP_KEEPALIVE_IDLE_MIN   (TCP_TICK_HZ * 60 * 60 * 2) ///< First keepalive.\r
+#define TCP_KEEPALIVE_PERIOD     (TCP_TICK_HZ * 60)\r
+#define TCP_MAX_KEEPALIVE        8\r
+#define TCP_FIN_WAIT2_TIME       (2 * TCP_TICK_HZ)\r
+#define TCP_TIME_WAIT_TIME       (2 * TCP_TICK_HZ)\r
+#define TCP_PAWS_24DAY           (24 * 24 * 60 * 60 * TCP_TICK_HZ)\r
+#define TCP_CONNECT_TIME         (75 * TCP_TICK_HZ)\r
+\r
+//\r
+// The header space to be reserved before TCP data to accomodate :\r
+// 60byte IP head + 60byte TCP head + link layer head\r
+//\r
+#define TCP_MAX_HEAD             192\r
+\r
+//\r
+// Value ranges for some control option\r
+//\r
+#define TCP_RCV_BUF_SIZE         (2 * 1024 * 1024)\r
+#define TCP_RCV_BUF_SIZE_MIN     (8 * 1024)\r
+#define TCP_SND_BUF_SIZE         (2 * 1024 * 1024)\r
+#define TCP_SND_BUF_SIZE_MIN     (8 * 1024)\r
+#define TCP_BACKLOG              10\r
+#define TCP_BACKLOG_MIN          5\r
+#define TCP_MAX_LOSS_MIN         6\r
+#define TCP_CONNECT_TIME_MIN     (60 * TCP_TICK_HZ)\r
+#define TCP_MAX_KEEPALIVE_MIN    4\r
+#define TCP_KEEPALIVE_IDLE_MAX   (TCP_TICK_HZ * 60 * 60 * 4)\r
+#define TCP_KEEPALIVE_PERIOD_MIN (TCP_TICK_HZ * 30)\r
+#define TCP_FIN_WAIT2_TIME_MAX   (4 * TCP_TICK_HZ)\r
+#define TCP_TIME_WAIT_TIME_MAX   (60 * TCP_TICK_HZ)\r
+\r
+///\r
+/// TCP_CONNECTED: both ends have synchronized their ISN.\r
+///\r
+#define TCP_CONNECTED(state)     ((state) > TCP_SYN_RCVD)\r
+\r
+#define TCP_FIN_RCVD(State) \\r
+  ( \\r
+    ((State) == TCP_CLOSE_WAIT) || \\r
+    ((State) == TCP_LAST_ACK) || \\r
+    ((State) == TCP_CLOSING) || \\r
+    ((State) == TCP_TIME_WAIT) \\r
+  )\r
+\r
+#define TCP_LOCAL_CLOSED(State) \\r
+  ( \\r
+    ((State) == TCP_FIN_WAIT_1) || \\r
+    ((State) == TCP_FIN_WAIT_2) || \\r
+    ((State) == TCP_CLOSING) || \\r
+    ((State) == TCP_TIME_WAIT) || \\r
+    ((State) == TCP_LAST_ACK) \\r
+  )\r
+\r
+//\r
+// Get the TCP_SEG point from a net buffer's ProtoData.\r
+//\r
+#define TCPSEG_NETBUF(NBuf)     ((TCP_SEG *) ((NBuf)->ProtoData))\r
+\r
+//\r
+// Macros to compare sequence no\r
+//\r
+#define TCP_SEQ_LT(SeqA, SeqB)  ((INT32) ((SeqA) - (SeqB)) < 0)\r
+#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0)\r
+#define TCP_SEQ_GT(SeqA, SeqB)  ((INT32) ((SeqB) - (SeqA)) < 0)\r
+#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0)\r
+\r
+//\r
+// TCP_SEQ_BETWEEN return whether b <= m <= e\r
+//\r
+#define TCP_SEQ_BETWEEN(b, m, e)    ((e) - (b) >= (m) - (b))\r
+\r
+//\r
+// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2\r
+//\r
+#define TCP_SUB_SEQ(Seq1, Seq2)     ((UINT32) ((Seq1) - (Seq2)))\r
+\r
+//\r
+// Check whether Flag is on\r
+//\r
+#define TCP_FLG_ON(Value, Flag)     ((BOOLEAN) (((Value) & (Flag)) != 0))\r
+//\r
+// Set and Clear operation on a Flag\r
+//\r
+#define TCP_SET_FLG(Value, Flag)    ((Value) |= (Flag))\r
+#define TCP_CLEAR_FLG(Value, Flag)  ((Value) &= ~(Flag))\r
+\r
+//\r
+// Test whether two peers are equal\r
+//\r
+#define TCP_PEER_EQUAL(Pa, Pb, Ver) \\r
+  (((Pa)->Port == (Pb)->Port) && TcpIsIpEqual(&((Pa)->Ip), &((Pb)->Ip), Ver))\r
+\r
+//\r
+// Test whether Pa matches Pb, or Pa is more specific\r
+// than pb. Zero means wildcard.\r
+//\r
+#define TCP_PEER_MATCH(Pa, Pb, Ver) \\r
+  ( \\r
+    (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port)) && \\r
+    (TcpIsIpZero (&((Pb)->Ip), Ver) || TcpIsIpEqual (&((Pb)->Ip), &((Pa)->Ip), Ver)) \\r
+  )\r
+\r
+#define TCP_TIMER_ON(Flag, Timer)     ((Flag) & (1 << (Timer)))\r
+#define TCP_SET_TIMER(Flag, Timer)    ((Flag) = (UINT16) ((Flag) | (1 << (Timer))))\r
+#define TCP_CLEAR_TIMER(Flag, Timer)  ((Flag) = (UINT16) ((Flag) & (~(1 << (Timer)))))\r
+\r
+\r
+#define TCP_TIME_LT(Ta, Tb)           ((INT32) ((Ta) - (Tb)) < 0)\r
+#define TCP_TIME_LEQ(Ta, Tb)          ((INT32) ((Ta) - (Tb)) <= 0)\r
+#define TCP_SUB_TIME(Ta, Tb)          ((UINT32) ((Ta) - (Tb)))\r
+\r
+#define TCP_MAX_WIN                   0xFFFFU\r
+\r
+///\r
+/// TCP segmentation data.\r
+///\r
+typedef struct _TCP_SEG {\r
+  TCP_SEQNO Seq;  ///< Starting sequence number.\r
+  TCP_SEQNO End;  ///< The sequence of the last byte + 1, include SYN/FIN. End-Seq = SEG.LEN.\r
+  TCP_SEQNO Ack;  ///< ACK field in the segment.\r
+  UINT8     Flag; ///< TCP header flags.\r
+  UINT16    Urg;  ///< Valid if URG flag is set.\r
+  UINT32    Wnd;  ///< TCP window size field.\r
+} TCP_SEG;\r
+\r
+///\r
+/// Network endpoint, IP plus Port structure.\r
+///\r
+typedef struct _TCP_PEER {\r
+  EFI_IP_ADDRESS  Ip;     ///< IP address, in network byte order.\r
+  TCP_PORTNO      Port;   ///< Port number, in network byte order.\r
+} TCP_PEER;\r
+\r
+typedef struct _TCP_CONTROL_BLOCK  TCP_CB;\r
+\r
+///\r
+/// TCP control block: it includes various states.\r
+///\r
+struct _TCP_CONTROL_BLOCK {\r
+  LIST_ENTRY        List;     ///< Back and forward link entry\r
+  TCP_CB            *Parent;  ///< The parent TCP_CB structure\r
+\r
+  SOCKET            *Sk;      ///< The socket it controled.\r
+  TCP_PEER          LocalEnd; ///< Local endpoint.\r
+  TCP_PEER          RemoteEnd;///< Remote endpoint.\r
+\r
+  LIST_ENTRY        SndQue;   ///< Retxmission queue.\r
+  LIST_ENTRY        RcvQue;   ///< Reassemble queue.\r
+  UINT32            CtrlFlag; ///< Control flags, such as NO_NAGLE.\r
+  INT32             Error;    ///< Soft error status, such as TCP_CONNECT_RESET.\r
+\r
+  //\r
+  // RFC793 and RFC1122 defined variables\r
+  //\r
+  UINT8             State;      ///< TCP state, such as SYN_SENT, LISTEN.\r
+  UINT8             DelayedAck; ///< Number of delayed ACKs.\r
+  UINT16            HeadSum;    ///< Checksum of the fixed parts of pesudo\r
+                                ///< header: Src IP, Dst IP, 0, Protocol,\r
+                                ///< do not include the TCP length.\r
+\r
+  TCP_SEQNO         Iss;        ///< Initial Sending Sequence.\r
+  TCP_SEQNO         SndUna;     ///< First unacknowledged data.\r
+  TCP_SEQNO         SndNxt;     ///< Next data sequence to send.\r
+  TCP_SEQNO         SndPsh;     ///< Send PUSH point.\r
+  TCP_SEQNO         SndUp;      ///< Send urgent point.\r
+  UINT32            SndWnd;     ///< Window advertised by the remote peer.\r
+  UINT32            SndWndMax;  ///< Max send window advertised by the peer.\r
+  TCP_SEQNO         SndWl1;     ///< Seq number used for last window update.\r
+  TCP_SEQNO         SndWl2;     ///< Ack no of last window update.\r
+  UINT16            SndMss;     ///< Max send segment size.\r
+  TCP_SEQNO         RcvNxt;     ///< Next sequence no to receive.\r
+  UINT32            RcvWnd;     ///< Window advertised by the local peer.\r
+  TCP_SEQNO         RcvWl2;     ///< The RcvNxt (or ACK) of last window update.\r
+                                ///< It is necessary because of delayed ACK.\r
+\r
+  TCP_SEQNO         RcvUp;                   ///< Urgent point;\r
+  TCP_SEQNO         Irs;                     ///< Initial Receiving Sequence.\r
+  UINT16            RcvMss;                  ///< Max receive segment size.\r
+  UINT16            EnabledTimer;            ///< Which timer is currently enabled.\r
+  UINT32            Timer[TCP_TIMER_NUMBER]; ///< When the timer will expire.\r
+  INT32             NextExpire;  ///< Countdown offset for the nearest timer.\r
+  UINT32            Idle;        ///< How long the connection is in idle.\r
+  UINT32            ProbeTime;   ///< The time out value for current window prober.\r
+  BOOLEAN           ProbeTimerOn;///< If TRUE, the probe time is on.\r
+\r
+  //\r
+  // RFC1323 defined variables, about window scale,\r
+  // timestamp and PAWS\r
+  //\r
+  UINT8             SndWndScale;  ///< Wndscale received from the peer.\r
+  UINT8             RcvWndScale;  ///< Wndscale used to scale local buffer.\r
+  UINT32            TsRecent;     ///< TsRecent to echo to the remote peer.\r
+  UINT32            TsRecentAge;  ///< When this TsRecent is updated.\r
+\r
+  //\r
+  // RFC2988 defined variables. about RTT measurement\r
+  //\r
+  TCP_SEQNO         RttSeq;     ///< The seq of measured segment now.\r
+  UINT32            RttMeasure; ///< Currently measured RTT in heartbeats.\r
+  UINT32            SRtt;       ///< Smoothed RTT, scaled by 8.\r
+  UINT32            RttVar;     ///< RTT variance, scaled by 8.\r
+  UINT32            Rto;        ///< Current RTO, not scaled.\r
+\r
+  //\r
+  // RFC2581, and 3782 variables.\r
+  // Congestion control + NewReno fast recovery.\r
+  //\r
+  UINT32            CWnd;         ///< Sender's congestion window.\r
+  UINT32            Ssthresh;     ///< Slow start threshold.\r
+  TCP_SEQNO         Recover;      ///< Recover point for NewReno.\r
+  UINT16            DupAck;       ///< Number of duplicate ACKs.\r
+  UINT8             CongestState; ///< The current congestion state(RFC3782).\r
+  UINT8             LossTimes;    ///< Number of retxmit timeouts in a row.\r
+  TCP_SEQNO         LossRecover;  ///< Recover point for retxmit.\r
+\r
+  //\r
+  // configuration parameters, for EFI_TCP4_PROTOCOL specification\r
+  //\r
+  UINT32            KeepAliveIdle;   ///< Idle time before sending first probe.\r
+  UINT32            KeepAlivePeriod; ///< Interval for subsequent keep alive probe.\r
+  UINT8             MaxKeepAlive;    ///< Maxium keep alive probe times.\r
+  UINT8             KeepAliveProbes; ///< The number of keep alive probe.\r
+  UINT16            MaxRexmit;       ///< The maxium number of retxmit before abort.\r
+  UINT32            FinWait2Timeout; ///< The FIN_WAIT_2 timeout.\r
+  UINT32            TimeWaitTimeout; ///< The TIME_WAIT timeout.\r
+  UINT32            ConnectTimeout;  ///< The connect establishment timeout.\r
+\r
+  //\r
+  // configuration for tcp provided by user\r
+  //\r
+  BOOLEAN           UseDefaultAddr;\r
+  UINT8             Tos;\r
+  UINT8             Ttl;\r
+  EFI_IPv4_ADDRESS  SubnetMask;\r
+\r
+  IP_IO_IP_INFO     *IpInfo;        ///< Pointer reference to Ip used to send pkt\r
+  UINT32            Tick;           ///< 1 tick = 200ms\r
+};\r
+\r
+#endif\r
diff --git a/NetworkPkg/TcpDxe/TcpTimer.c b/NetworkPkg/TcpDxe/TcpTimer.c
new file mode 100644 (file)
index 0000000..cc52ad9
--- /dev/null
@@ -0,0 +1,593 @@
+/** @file\r
+  TCP timer related functions.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+UINT32    mTcpTick = 1000;\r
+\r
+/**\r
+  Connect timeout handler.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpConnectTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Timeout handler for TCP retransmission timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpRexmitTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Timeout handler for window probe timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpProbeTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Timeout handler for keepalive timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpKeepaliveTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Timeout handler for FIN_WAIT_2 timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpFinwait2Timeout (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+/**\r
+  Timeout handler for 2MSL timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+Tcp2MSLTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  );\r
+\r
+TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {\r
+  TcpConnectTimeout,\r
+  TcpRexmitTimeout,\r
+  TcpProbeTimeout,\r
+  TcpKeepaliveTimeout,\r
+  TcpFinwait2Timeout,\r
+  Tcp2MSLTimeout,\r
+};\r
+\r
+/**\r
+  Close the TCP connection.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpClose (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  NetbufFreeList (&Tcb->SndQue);\r
+  NetbufFreeList (&Tcb->RcvQue);\r
+\r
+  TcpSetState (Tcb, TCP_CLOSED);\r
+}\r
+\r
+/**\r
+  Backoff the RTO.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpBackoffRto (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  //\r
+  // Fold the RTT estimate if too many times, the estimate\r
+  // may be wrong, fold it. So the next time a valid\r
+  // measurement is sampled, we can start fresh.\r
+  //\r
+  if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) {\r
+    Tcb->RttVar += Tcb->SRtt >> 2;\r
+    Tcb->SRtt = 0;\r
+  }\r
+\r
+  Tcb->Rto <<= 1;\r
+\r
+  if (Tcb->Rto < TCP_RTO_MIN) {\r
+\r
+    Tcb->Rto = TCP_RTO_MIN;\r
+  } else if (Tcb->Rto > TCP_RTO_MAX) {\r
+\r
+    Tcb->Rto = TCP_RTO_MAX;\r
+  }\r
+}\r
+\r
+/**\r
+  Connect timeout handler.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpConnectTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  if (!TCP_CONNECTED (Tcb->State)) {\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    if (EFI_ABORTED == Tcb->Sk->SockError) {\r
+      SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
+    }\r
+\r
+    if (TCP_SYN_RCVD == Tcb->State) {\r
+      DEBUG (\r
+        (EFI_D_WARN,\r
+        "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",\r
+        Tcb)\r
+        );\r
+\r
+      TcpResetConnection (Tcb);\r
+\r
+    }\r
+\r
+    TcpClose (Tcb);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Timeout handler for TCP retransmission timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpRexmitTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  UINT32  FlightSize;\r
+\r
+  DEBUG (\r
+    (EFI_D_WARN,\r
+    "TcpRexmitTimeout: transmission timeout for TCB %p\n",\r
+    Tcb)\r
+    );\r
+\r
+  //\r
+  // Set the congestion window. FlightSize is the\r
+  // amount of data that has been sent but not\r
+  // yet ACKed.\r
+  //\r
+  FlightSize        = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
+  Tcb->Ssthresh     = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2);\r
+\r
+  Tcb->CWnd         = Tcb->SndMss;\r
+  Tcb->LossRecover  = Tcb->SndNxt;\r
+\r
+  Tcb->LossTimes++;\r
+  if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {\r
+\r
+    DEBUG (\r
+      (EFI_D_ERROR,\r
+      "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",\r
+      Tcb)\r
+      );\r
+\r
+    if (EFI_ABORTED == Tcb->Sk->SockError) {\r
+      SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
+    }\r
+\r
+    TcpClose (Tcb);\r
+    return ;\r
+  }\r
+\r
+  TcpBackoffRto (Tcb);\r
+  TcpRetransmit (Tcb, Tcb->SndUna);\r
+  TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
+\r
+  Tcb->CongestState = TCP_CONGEST_LOSS;\r
+\r
+  TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+}\r
+\r
+/**\r
+  Timeout handler for window probe timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpProbeTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  //\r
+  // This is the timer for sender's SWSA. RFC1122 requires\r
+  // a timer set for sender's SWSA, and suggest combine it\r
+  // with window probe timer. If data is sent, don't set\r
+  // the probe timer, since retransmit timer is on.\r
+  //\r
+  if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {\r
+\r
+    ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);\r
+    Tcb->ProbeTimerOn = FALSE;\r
+    return ;\r
+  }\r
+\r
+  TcpSendZeroProbe (Tcb);\r
+  TcpSetProbeTimer (Tcb);\r
+}\r
+\r
+/**\r
+  Timeout handler for keepalive timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpKeepaliveTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  Tcb->KeepAliveProbes++;\r
+\r
+  //\r
+  // Too many Keep-alive probes, drop the connection\r
+  //\r
+  if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {\r
+\r
+    if (EFI_ABORTED == Tcb->Sk->SockError) {\r
+      SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
+    }\r
+\r
+    TcpClose (Tcb);\r
+    return ;\r
+  }\r
+\r
+  TcpSendZeroProbe (Tcb);\r
+  TcpSetKeepaliveTimer (Tcb);\r
+}\r
+\r
+/**\r
+  Timeout handler for FIN_WAIT_2 timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpFinwait2Timeout (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  DEBUG (\r
+    (EFI_D_WARN,\r
+    "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",\r
+    Tcb)\r
+    );\r
+\r
+  TcpClose (Tcb);\r
+}\r
+\r
+/**\r
+  Timeout handler for 2MSL timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+Tcp2MSLTimeout (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  DEBUG (\r
+    (EFI_D_WARN,\r
+    "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",\r
+    Tcb)\r
+    );\r
+\r
+  TcpClose (Tcb);\r
+}\r
+\r
+/**\r
+  Update the timer status and the next expire time according to the timers\r
+  to expire in a specific future time slot.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpUpdateTimer (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  UINT16  Index;\r
+\r
+  //\r
+  // Don't use a too large value to init NextExpire\r
+  // since mTcpTick wraps around as sequence no does.\r
+  //\r
+  Tcb->NextExpire = TCP_EXPIRE_TIME;\r
+  TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);\r
+\r
+  for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {\r
+\r
+    if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&\r
+        TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)\r
+        ) {\r
+\r
+      Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);\r
+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Enable a TCP timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Timer    The index of the timer to be enabled.\r
+  @param[in]       TimeOut  The timeout value of this timer.\r
+\r
+**/\r
+VOID\r
+TcpSetTimer (\r
+  IN OUT TCP_CB *Tcb,\r
+  IN     UINT16 Timer,\r
+  IN     UINT32 TimeOut\r
+  )\r
+{\r
+  TCP_SET_TIMER (Tcb->EnabledTimer, Timer);\r
+  Tcb->Timer[Timer] = mTcpTick + TimeOut;\r
+\r
+  TcpUpdateTimer (Tcb);\r
+}\r
+\r
+/**\r
+  Clear one TCP timer.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+  @param[in]       Timer    The index of the timer to be cleared.\r
+\r
+**/\r
+VOID\r
+TcpClearTimer (\r
+  IN OUT TCP_CB *Tcb,\r
+  IN     UINT16 Timer\r
+  )\r
+{\r
+  TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);\r
+  TcpUpdateTimer (Tcb);\r
+}\r
+\r
+/**\r
+  Clear all TCP timers.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpClearAllTimer (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  Tcb->EnabledTimer = 0;\r
+  TcpUpdateTimer (Tcb);\r
+}\r
+\r
+/**\r
+  Enable the window prober timer and set the timeout value.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSetProbeTimer (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  if (!Tcb->ProbeTimerOn) {\r
+    Tcb->ProbeTime    = Tcb->Rto;\r
+    Tcb->ProbeTimerOn = TRUE;\r
+\r
+  } else {\r
+    Tcb->ProbeTime <<= 1;\r
+  }\r
+\r
+  if (Tcb->ProbeTime < TCP_RTO_MIN) {\r
+\r
+    Tcb->ProbeTime = TCP_RTO_MIN;\r
+  } else if (Tcb->ProbeTime > TCP_RTO_MAX) {\r
+\r
+    Tcb->ProbeTime = TCP_RTO_MAX;\r
+  }\r
+\r
+  TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);\r
+}\r
+\r
+/**\r
+  Enable the keepalive timer and set the timeout value.\r
+\r
+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSetKeepaliveTimer (\r
+  IN OUT TCP_CB *Tcb\r
+  )\r
+{\r
+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {\r
+    return ;\r
+\r
+  }\r
+\r
+  //\r
+  // Set the timer to KeepAliveIdle if either\r
+  // 1. the keepalive timer is off\r
+  // 2. The keepalive timer is on, but the idle\r
+  // is less than KeepAliveIdle, that means the\r
+  // connection is alive since our last probe.\r
+  //\r
+  if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||\r
+      (Tcb->Idle < Tcb->KeepAliveIdle)\r
+      ) {\r
+\r
+    TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);\r
+    Tcb->KeepAliveProbes = 0;\r
+\r
+  } else {\r
+\r
+    TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);\r
+  }\r
+}\r
+\r
+/**\r
+  Heart beat timer handler.\r
+\r
+  @param[in]  Context        Context of the timer event, ignored.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpTickingDpc (\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  LIST_ENTRY      *Entry;\r
+  LIST_ENTRY      *Next;\r
+  TCP_CB          *Tcb;\r
+  INT16           Index;\r
+\r
+  mTcpTick++;\r
+  mTcpGlobalIss += TCP_ISS_INCREMENT_2;\r
+\r
+  //\r
+  // Don't use LIST_FOR_EACH, which isn't delete safe.\r
+  //\r
+  for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {\r
+\r
+    Next  = Entry->ForwardLink;\r
+\r
+    Tcb   = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+    if (Tcb->State == TCP_CLOSED) {\r
+      continue;\r
+    }\r
+    //\r
+    // The connection is doing RTT measurement.\r
+    //\r
+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
+      Tcb->RttMeasure++;\r
+    }\r
+\r
+    Tcb->Idle++;\r
+\r
+    if (Tcb->DelayedAck != 0) {\r
+      TcpSendAck (Tcb);\r
+    }\r
+\r
+    if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) {\r
+      Tcb->Tick--;\r
+    }\r
+\r
+    //\r
+    // No timer is active or no timer expired\r
+    //\r
+    if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {\r
+\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Call the timeout handler for each expired timer.\r
+    //\r
+    for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {\r
+\r
+      if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {\r
+        //\r
+        // disable the timer before calling the handler\r
+        // in case the handler enables it again.\r
+        //\r
+        TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);\r
+        mTcpTimerHandler[Index](Tcb);\r
+\r
+        //\r
+        // The Tcb may have been deleted by the timer, or\r
+        // no other timer is set.\r
+        //\r
+        if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {\r
+          break;\r
+        }\r
+      }\r
+    }\r
+\r
+    //\r
+    // If the Tcb still exist or some timer is set, update the timer\r
+    //\r
+    if (Index == TCP_TIMER_NUMBER) {\r
+      TcpUpdateTimer (Tcb);\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Heart beat timer handler, queues the DPC at TPL_CALLBACK.\r
+\r
+  @param[in]  Event    Timer event signaled, ignored.\r
+  @param[in]  Context  Context of the timer event, ignored.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpTicking (\r
+  IN EFI_EVENT Event,\r
+  IN VOID      *Context\r
+  )\r
+{\r
+  QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);\r
+}\r
+\r
diff --git a/NetworkPkg/Udp6Dxe/ComponentName.c b/NetworkPkg/Udp6Dxe/ComponentName.c
new file mode 100644 (file)
index 0000000..d7df096
--- /dev/null
@@ -0,0 +1,313 @@
+/** @file\r
+  UEFI Component Name(2) protocol implementation for UDP6 driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Udp6Impl.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle OPTIONAL,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gUdp6ComponentName = {\r
+  Udp6ComponentNameGetDriverName,\r
+  Udp6ComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2 = {\r
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Udp6ComponentNameGetDriverName,\r
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Udp6ComponentNameGetControllerName,\r
+  "en"\r
+};\r
+\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUdp6DriverNameTable[] = {\r
+  {\r
+    "eng;en",\r
+    L"UDP6 Network Service Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+{\r
+  return LookupUnicodeString2 (\r
+           Language,\r
+           This->SupportedLanguages,\r
+           mUdp6DriverNameTable,\r
+           DriverName,\r
+           (BOOLEAN) (This == &gUdp6ComponentName)\r
+           );\r
+}\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle OPTIONAL,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
diff --git a/NetworkPkg/Udp6Dxe/Udp6Driver.c b/NetworkPkg/Udp6Dxe/Udp6Driver.c
new file mode 100644 (file)
index 0000000..1cfd5f1
--- /dev/null
@@ -0,0 +1,556 @@
+/** @file\r
+  Driver Binding functions and Service Binding functions for the Network driver module.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Udp6Impl.h"\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gUdp6DriverBinding = {\r
+  Udp6DriverBindingSupported,\r
+  Udp6DriverBindingStart,\r
+  Udp6DriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding = {\r
+  Udp6ServiceBindingCreateChild,\r
+  Udp6ServiceBindingDestroyChild\r
+};\r
+\r
+/**\r
+  Tests to see if this driver supports a given controller. If a child device is provided,\r
+  it further tests to see if this driver supports creating a handle for the specified child device.\r
+\r
+  This function checks to see if the driver specified by This supports the device specified by\r
+  ControllerHandle. Drivers will typically use the device path attached to\r
+  ControllerHandle and/or the services from the bus I/O abstraction attached to\r
+  ControllerHandle to determine if the driver supports ControllerHandle. This function\r
+  may be called many times during platform initialization. In order to reduce boot times, the tests\r
+  performed by this function must be very small, and take as little time as possible to execute. This\r
+  function must not change the state of any hardware devices, and this function must be aware that the\r
+  device specified by ControllerHandle may already be managed by the same driver or a\r
+  different driver. This function must match its calls to AllocatePages() with FreePages(),\r
+  AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().\r
+  Because ControllerHandle may have been previously started by the same driver, if a protocol is\r
+  already in the opened state, then it must not be closed with CloseProtocol(). This is required\r
+  to guarantee the state of ControllerHandle is not modified by this function.\r
+\r
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle     The handle of the controller to test. This handle\r
+                                   must support a protocol interface that supplies\r
+                                   an I/O abstraction to the driver.\r
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This\r
+                                   parameter is ignored by device drivers, and is optional for bus\r
+                                   drivers. For bus drivers, if this parameter is not NULL, then\r
+                                   the bus driver must determine if the bus controller specified\r
+                                   by ControllerHandle and the child controller specified\r
+                                   by RemainingDevicePath are both supported by this\r
+                                   bus driver.\r
+\r
+  @retval EFI_SUCCESS              The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is supported by the driver specified by This.\r
+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is already being managed by the driver\r
+                                   specified by This.\r
+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is already being managed by a different\r
+                                   driver or an application that requires exclusive access.\r
+                                   Currently not implemented.\r
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is not supported by the driver specified by This.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  //\r
+  // Test for the Udp6ServiceBinding Protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiUdp6ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+  //\r
+  // Test for the Ip6ServiceBinding Protocol\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiIp6ServiceBindingProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  This service is called by the EFI boot service ConnectController(). In order to make\r
+  drivers as small as possible, there are a few calling restrictions for\r
+  this service. ConnectController() must follow these\r
+  calling restrictions. If any other agent wishes to call Start() it\r
+  must also follow these calling restrictions.\r
+\r
+  @param[in]  This                   Protocol instance pointer.\r
+  @param[in]  ControllerHandle       Handle of device to bind the driver to.\r
+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                     device to start.\r
+\r
+  @retval EFI_SUCCES             This driver is added to ControllerHandle.\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource can't be allocated.\r
+  @retval other                  This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  UDP6_SERVICE_DATA  *Udp6Service;\r
+\r
+  //\r
+  // Allocate Private Context Data Structure.\r
+  //\r
+  Udp6Service = AllocateZeroPool (sizeof (UDP6_SERVICE_DATA));\r
+  if (Udp6Service == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto EXIT;\r
+  }\r
+\r
+  Status = Udp6CreateService (Udp6Service, This->DriverBindingHandle, ControllerHandle);\r
+  if (EFI_ERROR (Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Install the Udp6ServiceBindingProtocol on the ControllerHandle.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &ControllerHandle,\r
+                  &gEfiUdp6ServiceBindingProtocolGuid,\r
+                  &Udp6Service->ServiceBinding,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    Udp6CleanService (Udp6Service);\r
+    goto EXIT;\r
+  } else {\r
+    Status = Udp6SetVariableData (Udp6Service);\r
+  }\r
+\r
+EXIT:\r
+  if (EFI_ERROR (Status)) {\r
+    if (Udp6Service != NULL) {\r
+      FreePool (Udp6Service);\r
+    }\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  This service is called by the  EFI boot service DisconnectController(). In order to\r
+  make drivers as small as possible, there are a few calling\r
+  restrictions for this service. DisconnectController()\r
+  must follow these calling restrictions. If any other agent wishes\r
+  to call Stop(), it must also follow these calling restrictions.\r
+\r
+  @param[in]  This                   Protocol instance pointer.\r
+  @param[in]  ControllerHandle       Handle of device to stop the driver on.\r
+  @param[in]  NumberOfChildren       Number of Handles in ChildHandleBuffer. If the number\r
+                                     of children is zero stop the entire bus driver.\r
+  @param[in]  ChildHandleBuffer      List of Child Handles to Stop. It is optional.\r
+\r
+  @retval EFI_SUCCES             This driver is removed ControllerHandle.\r
+  @retval EFI_DEVICE_ERROR       Can't find the NicHandle from the ControllerHandle and specified GUID.\r
+  @retval other                  This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  EFI_HANDLE                    NicHandle;\r
+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;\r
+  UDP6_SERVICE_DATA             *Udp6Service;\r
+  UDP6_INSTANCE_DATA            *Instance;\r
+\r
+  //\r
+  // Find the NicHandle where UDP6 ServiceBinding Protocol is installed.\r
+  //\r
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid);\r
+  if (NicHandle == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Retrieve the UDP6 ServiceBinding Protocol.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  NicHandle,\r
+                  &gEfiUdp6ServiceBindingProtocolGuid,\r
+                  (VOID **) &ServiceBinding,\r
+                  This->DriverBindingHandle,\r
+                  NicHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (ServiceBinding);\r
+\r
+  if (NumberOfChildren == 0) {\r
+\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           NicHandle,\r
+           &gEfiUdp6ServiceBindingProtocolGuid,\r
+           &Udp6Service->ServiceBinding,\r
+           NULL\r
+           );\r
+\r
+    Udp6ClearVariableData (Udp6Service);\r
+\r
+    Udp6CleanService (Udp6Service);\r
+\r
+    FreePool (Udp6Service);\r
+  } else {\r
+\r
+    while (!IsListEmpty (&Udp6Service->ChildrenList)) {\r
+      Instance = NET_LIST_HEAD (&Udp6Service->ChildrenList, UDP6_INSTANCE_DATA, Link);\r
+\r
+      Status = ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Creates a child handle and installs a protocol.\r
+\r
+  The CreateChild() function installs a protocol on ChildHandle.\r
+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+  @param[in]       This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in, out]  ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+                               then a new handle is created. If it is a pointer to an existing UEFI handle,\r
+                               then the protocol is added to the existing UEFI handle.\r
+\r
+  @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
+  @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create\r
+                                the child.\r
+  @retval other                 The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ServiceBindingCreateChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN OUT EFI_HANDLE                *ChildHandle\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  UDP6_SERVICE_DATA   *Udp6Service;\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+  EFI_TPL             OldTpl;\r
+  VOID                *Ip6;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This);\r
+\r
+  //\r
+  // Allocate the instance private data structure.\r
+  //\r
+  Instance = AllocateZeroPool (sizeof (UDP6_INSTANCE_DATA));\r
+  if (Instance == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Udp6InitInstance (Udp6Service, Instance);\r
+\r
+  //\r
+  // Add an IpInfo for this instance.\r
+  //\r
+  Instance->IpInfo = IpIoAddIp (Udp6Service->IpIo);\r
+  if (Instance->IpInfo == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Install the Udp6Protocol for this instance.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiUdp6ProtocolGuid,\r
+                  &Instance->Udp6Proto,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Instance->ChildHandle = *ChildHandle;\r
+\r
+  //\r
+  // Open the default Ip6 protocol in the IP_IO BY_CHILD.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Udp6Service->IpIo->ChildHandle,\r
+                  &gEfiIp6ProtocolGuid,\r
+                  (VOID **) &Ip6,\r
+                  gUdp6DriverBinding.DriverBindingHandle,\r
+                  Instance->ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // Link this instance into the service context data and increase the ChildrenNumber.\r
+  //\r
+  InsertTailList (&Udp6Service->ChildrenList, &Instance->Link);\r
+  Udp6Service->ChildrenNumber++;\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  if (Instance->ChildHandle != NULL) {\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           Instance->ChildHandle,\r
+           &gEfiUdp6ProtocolGuid,\r
+           &Instance->Udp6Proto,\r
+           NULL\r
+           );\r
+  }\r
+\r
+  if (Instance->IpInfo != NULL) {\r
+    IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo);\r
+  }\r
+\r
+  Udp6CleanInstance (Instance);\r
+\r
+  FreePool (Instance);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Destroys a child handle with a set of I/O services.\r
+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+  last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  ChildHandle        Handle of the child to destroy.\r
+\r
+  @retval EFI_SUCCES             The I/O services were removed from the child\r
+                                 handle.\r
+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services\r
+                                 that are being removed.\r
+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.\r
+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because\r
+                                 its  I/O services are being used.\r
+  @retval other                  The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  UDP6_SERVICE_DATA   *Udp6Service;\r
+  EFI_UDP6_PROTOCOL   *Udp6Proto;\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+  EFI_TPL             OldTpl;\r
+\r
+  if ((This == NULL) || (ChildHandle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This);\r
+\r
+  //\r
+  // Try to get the Udp6 protocol from the ChildHandle.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ChildHandle,\r
+                  &gEfiUdp6ProtocolGuid,\r
+                  (VOID **) &Udp6Proto,\r
+                  gUdp6DriverBinding.DriverBindingHandle,\r
+                  ChildHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (Udp6Proto);\r
+\r
+  if (Instance->Destroyed) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Use the Destroyed flag to avoid the re-entering of the following code.\r
+  //\r
+  Instance->Destroyed = TRUE;\r
+\r
+  //\r
+  // Close the Ip6 protocol.\r
+  //\r
+  gBS->CloseProtocol (\r
+         Udp6Service->IpIo->ChildHandle,\r
+         &gEfiIp6ProtocolGuid,\r
+         gUdp6DriverBinding.DriverBindingHandle,\r
+         Instance->ChildHandle\r
+         );\r
+\r
+  //\r
+  // Uninstall the Udp6Protocol previously installed on the ChildHandle.\r
+  //\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                  ChildHandle,\r
+                  &gEfiUdp6ProtocolGuid,\r
+                  (VOID *) &Instance->Udp6Proto,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    Instance->Destroyed = FALSE;\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Reset the configuration in case the instance's consumer forgets to do this.\r
+  //\r
+  Udp6Proto->Configure (Udp6Proto, NULL);\r
+\r
+  //\r
+  // Remove the IpInfo this instance consumes.\r
+  //\r
+  IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo);\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // Remove this instance from the service context data's ChildrenList.\r
+  //\r
+  RemoveEntryList (&Instance->Link);\r
+  Udp6Service->ChildrenNumber--;\r
+\r
+  //\r
+  // Clean the instance.\r
+  //\r
+  Udp6CleanInstance (Instance);\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  FreePool (Instance);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This is the declaration of an EFI image entry point. This entry point is\r
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+  both device drivers and bus drivers.\r
+\r
+  The entry point for Udp6 driver that installs the driver binding\r
+  and component name protocol on its ImageHandle.\r
+\r
+  @param[in] ImageHandle        The firmware allocated handle for the UEFI image.\r
+  @param[in] SystemTable        A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverEntryPoint (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  //\r
+  // Install the Udp6DriverBinding and Udp6ComponentName protocols.\r
+  //\r
+\r
+  Status = EfiLibInstallDriverBindingComponentName2 (\r
+             ImageHandle,\r
+             SystemTable,\r
+             &gUdp6DriverBinding,\r
+             ImageHandle,\r
+             &gUdp6ComponentName,\r
+             &gUdp6ComponentName2\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Initialize the UDP random port.\r
+    //\r
+    mUdp6RandomPort = (UINT16)(\r
+                        ((UINT16) NetRandomInitSeed ()) %\r
+                         UDP6_PORT_KNOWN +\r
+                         UDP6_PORT_KNOWN\r
+                         );\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
diff --git a/NetworkPkg/Udp6Dxe/Udp6Driver.h b/NetworkPkg/Udp6Dxe/Udp6Driver.h
new file mode 100644 (file)
index 0000000..e5a923b
--- /dev/null
@@ -0,0 +1,182 @@
+/** @file\r
+  Driver Binding functions and Service Binding functions for the Network driver module.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _UDP6_DRIVER_H_\r
+#define _UDP6_DRIVER_H_\r
+\r
+#include <Protocol/DriverBinding.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DevicePath.h>\r
+\r
+/**\r
+  Tests to see if this driver supports a given controller. If a child device is provided,\r
+  it further tests to see if this driver supports creating a handle for the specified child device.\r
+\r
+  This function checks to see if the driver specified by This supports the device specified by\r
+  ControllerHandle. Drivers typically use the device path attached to\r
+  ControllerHandle and/or the services from the bus I/O abstraction attached to\r
+  ControllerHandle to determine if the driver supports ControllerHandle. This function\r
+  may be called many times during platform initialization. In order to reduce boot times, the tests\r
+  performed by this function must be very small, and take as little time as possible to execute. This\r
+  function must not change the state of any hardware devices, and this function must be aware that the\r
+  device specified by ControllerHandle may already be managed by the same driver or a\r
+  different driver. This function must match its calls to AllocatePages() with FreePages(),\r
+  AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().\r
+  Since ControllerHandle may have been previously started by the same driver, if a protocol is\r
+  already in the opened state, then it must not be closed with CloseProtocol(). This is required\r
+  to guarantee the state of ControllerHandle is not modified by this function.\r
+\r
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle     The handle of the controller to test. This handle\r
+                                   must support a protocol interface that supplies\r
+                                   an I/O abstraction to the driver.\r
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This\r
+                                   parameter is ignored by device drivers, and is optional for bus\r
+                                   drivers. For bus drivers, if this parameter is not NULL, then\r
+                                   the bus driver must determine if the bus controller specified\r
+                                   by ControllerHandle and the child controller specified\r
+                                   by RemainingDevicePath are both supported by this\r
+                                   bus driver.\r
+\r
+  @retval EFI_SUCCESS              The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is supported by the driver specified by This.\r
+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is already being managed by the driver\r
+                                   specified by This.\r
+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is already being managed by a different\r
+                                   driver or an application that requires exclusive access.\r
+                                   Currently not implemented.\r
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is not supported by the driver specified by This.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL\r
+  );\r
+\r
+/**\r
+  Start this driver on ControllerHandle.\r
+\r
+  This service is called by the  EFI boot service ConnectController(). In order to make\r
+  drivers as small as possible, there are a few calling restrictions for\r
+  this service. ConnectController() must follow these\r
+  calling restrictions. If any other agent wishes to call Start() it\r
+  must also follow these calling restrictions.\r
+\r
+  @param[in]  This                   Protocol instance pointer.\r
+  @param[in]  ControllerHandle       Handle of device to bind a driver to.\r
+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child\r
+                                     device to start.\r
+\r
+  @retval EFI_SUCCES             This driver is added to ControllerHandle.\r
+  @retval EFI_OUT_OF_RESOURCES   The required system resource can't be allocated.\r
+  @retval other                  This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL\r
+  );\r
+\r
+/**\r
+  Stop this driver on ControllerHandle.\r
+\r
+  This service is called by the  EFI boot service DisconnectController(). In order to\r
+  make drivers as small as possible, there are a few calling\r
+  restrictions for this service. DisconnectController()\r
+  must follow these calling restrictions. If any other agent wishes\r
+  to call Stop(), it must also follow these calling restrictions.\r
+\r
+  @param[in]  This                   Protocol instance pointer.\r
+  @param[in]  ControllerHandle       Handle of device to stop the driver on.\r
+  @param[in]  NumberOfChildren       Number of Handles in ChildHandleBuffer. If number\r
+                                     of children is zero, stop the entire bus driver.\r
+  @param[in]  ChildHandleBuffer      List of Child Handles to Stop. It is optional.\r
+\r
+  @retval EFI_SUCCESS            This driver removed ControllerHandle.\r
+  @retval EFI_DEVICE_ERROR       Can't find the NicHandle from the ControllerHandle and specified GUID.\r
+  @retval other                  This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  UINTN                        NumberOfChildren,\r
+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL\r
+  );\r
+\r
+/**\r
+  Creates a child handle and installs a protocol.\r
+\r
+  The CreateChild() function installs a protocol on ChildHandle.\r
+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+  @param[in]       This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+  @param[in, out]  ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+                               then a new handle is created. If it is a pointer to an existing UEFI handle,\r
+                               then the protocol is added to the existing UEFI handle.\r
+\r
+  @retval EFI_SUCCES            The protocol was added to ChildHandle.\r
+  @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create\r
+                                the child.\r
+  @retval other                 The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ServiceBindingCreateChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN OUT EFI_HANDLE                *ChildHandle\r
+  );\r
+\r
+/**\r
+  Destroys a child handle with a set of I/O services.\r
+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+  last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  ChildHandle        Handle of the child to destroy.\r
+\r
+  @retval EFI_SUCCES             The I/O services were removed from the child\r
+                                 handle.\r
+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services\r
+                                 that are being removed.\r
+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.\r
+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because\r
+                                 its I/O services are being used.\r
+  @retval other                  The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ServiceBindingDestroyChild (\r
+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                    ChildHandle\r
+  );\r
+\r
+#endif\r
+\r
diff --git a/NetworkPkg/Udp6Dxe/Udp6Dxe.inf b/NetworkPkg/Udp6Dxe/Udp6Dxe.inf
new file mode 100644 (file)
index 0000000..30b2bc1
--- /dev/null
@@ -0,0 +1,63 @@
+## @file Udp6Dxe.inf\r
+#  Component description file for Udp6 module.\r
+#\r
+#  Copyright (c) 2009 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = Udp6Dxe\r
+  FILE_GUID                      = D912C7BC-F098-4367-92BA-E911083C7B0E\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+\r
+  ENTRY_POINT                    = Udp6DriverEntryPoint\r
+  UNLOAD_IMAGE                   = NetLibDefaultUnload\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+  Udp6Driver.h\r
+  Udp6Driver.c\r
+  Udp6Impl.c\r
+  Udp6Impl.h\r
+  ComponentName.c\r
+  Udp6Main.c\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  BaseMemoryLib\r
+  MemoryAllocationLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  UefiRuntimeServicesTableLib\r
+  UefiLib\r
+  DebugLib\r
+  IpIoLib\r
+  NetLib\r
+  DpcLib\r
+\r
+\r
+[Protocols]\r
+  gEfiIp6ProtocolGuid                           # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiIp6ServiceBindingProtocolGuid             # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiUdp6ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiUdp6ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED\r
+\r
diff --git a/NetworkPkg/Udp6Dxe/Udp6Impl.c b/NetworkPkg/Udp6Dxe/Udp6Impl.c
new file mode 100644 (file)
index 0000000..8e25931
--- /dev/null
@@ -0,0 +1,2131 @@
+/** @file\r
+  Udp6 driver's whole implementation.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Udp6Impl.h"\r
+\r
+UINT16  mUdp6RandomPort;\r
+\r
+/**\r
+  This function checks and timeouts the I/O datagrams holding by the corresponding\r
+  service context.\r
+\r
+  @param[in]  Event              The event this function is registered to.\r
+  @param[in]  Context            The context data registered during the creation of\r
+                                 the Event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6CheckTimeout (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  );\r
+\r
+/**\r
+  This function finds the udp instance by the specified <Address, Port> pair.\r
+\r
+  @param[in]  InstanceList       Pointer to the head of the list linking the udp\r
+                                 instances.\r
+  @param[in]  Address            Pointer to the specified IPv6 address.\r
+  @param[in]  Port               The udp port number.\r
+\r
+  @retval TRUE     The specified <Address, Port> pair is found.\r
+  @retval FALSE    Otherwise.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6FindInstanceByPort (\r
+  IN LIST_ENTRY        *InstanceList,\r
+  IN EFI_IPv6_ADDRESS  *Address,\r
+  IN UINT16            Port\r
+  );\r
+\r
+/**\r
+  This function is the packet transmitting notify function registered to the IpIo\r
+  interface. It's called to signal the udp TxToken when the IpIo layer completes\r
+  transmitting of the udp datagram.\r
+\r
+  @param[in]  Status            The completion status of the output udp datagram.\r
+  @param[in]  Context           Pointer to the context data.\r
+  @param[in]  Sender            Specify a EFI_IP6_PROTOCOL for sending.\r
+  @param[in]  NotifyData        Pointer to the notify data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6DgramSent (\r
+  IN EFI_STATUS        Status,\r
+  IN VOID              *Context,\r
+  IN IP_IO_IP_PROTOCOL Sender,\r
+  IN VOID              *NotifyData\r
+  );\r
+\r
+/**\r
+  This function processes the received datagram passed up by the IpIo layer.\r
+\r
+  @param[in]  Status            The status of this udp datagram.\r
+  @param[in]  IcmpError         The IcmpError code, only available when Status is\r
+                                EFI_ICMP_ERROR.\r
+  @param[in]  NetSession        Pointer to the EFI_NET_SESSION_DATA.\r
+  @param[in]  Packet            Pointer to the NET_BUF containing the received udp\r
+                                datagram.\r
+  @param[in]  Context           Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6DgramRcvd (\r
+  IN EFI_STATUS            Status,\r
+  IN UINT8                 IcmpError,\r
+  IN EFI_NET_SESSION_DATA  *NetSession,\r
+  IN NET_BUF               *Packet,\r
+  IN VOID                  *Context\r
+  );\r
+\r
+/**\r
+  This function cancle the token specified by Arg in the Map.\r
+\r
+  @param[in]  Map             Pointer to the NET_MAP.\r
+  @param[in]  Item            Pointer to the NET_MAP_ITEM.\r
+  @param[in]  Arg             Pointer to the token to be cancelled, if NULL, all\r
+                              the tokens in this Map will be cancelled.\r
+                              This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS         The token is cancelled if Arg is NULL or the token\r
+                              is not the same as that in the Item if Arg is not\r
+                              NULL.\r
+  @retval EFI_ABORTED         Arg is not NULL, and the token specified by Arg is\r
+                              cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6CancelTokens (\r
+  IN NET_MAP       *Map,\r
+  IN NET_MAP_ITEM  *Item,\r
+  IN VOID          *Arg OPTIONAL\r
+  );\r
+\r
+/**\r
+  This function check if the received udp datagram matches with the Instance.\r
+\r
+  @param[in]  Instance           Pointer to the udp instance context data.\r
+  @param[in]  Udp6Session        Pointer to the EFI_UDP6_SESSION_DATA abstracted\r
+                                 from the received udp datagram.\r
+\r
+  @retval TRUE     The udp datagram matches the receiving requirements of the Instance.\r
+  @retval FALSE    The udp datagram doe not match the receiving requirements of the Instance.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6MatchDgram (\r
+  IN UDP6_INSTANCE_DATA     *Instance,\r
+  IN EFI_UDP6_SESSION_DATA  *Udp6Session\r
+  );\r
+\r
+/**\r
+  This function removes the Wrap specified by Context and releases relevant resources.\r
+\r
+  @param[in]  Event                  The Event this notify function is registered to.\r
+  @param[in]  Context                Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6RecycleRxDataWrap (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  );\r
+\r
+/**\r
+  This function wraps the Packet into RxData.\r
+\r
+  @param[in]  Instance           Pointer to the instance context data.\r
+  @param[in]  Packet             Pointer to the buffer containing the received\r
+                                 datagram.\r
+  @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this\r
+                                 datagram.\r
+\r
+  @return Pointer to the structure wrapping the RxData and the Packet.\r
+\r
+**/\r
+UDP6_RXDATA_WRAP *\r
+Udp6WrapRxData (\r
+  IN UDP6_INSTANCE_DATA     *Instance,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_UDP6_RECEIVE_DATA  *RxData\r
+  );\r
+\r
+/**\r
+  This function enqueues the received datagram into the instances' receiving queues.\r
+\r
+  @param[in]  Udp6Service        Pointer to the udp service context data.\r
+  @param[in]  Packet             Pointer to the buffer containing the received\r
+                                 datagram.\r
+  @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this\r
+                                 datagram.\r
+\r
+  @return The times this datagram is enqueued.\r
+\r
+**/\r
+UINTN\r
+Udp6EnqueueDgram (\r
+  IN UDP6_SERVICE_DATA      *Udp6Service,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_UDP6_RECEIVE_DATA  *RxData\r
+  );\r
+\r
+/**\r
+  This function delivers the datagrams enqueued in the instances.\r
+\r
+  @param[in]  Udp6Service            Pointer to the udp service context data.\r
+\r
+**/\r
+VOID\r
+Udp6DeliverDgram (\r
+  IN UDP6_SERVICE_DATA  *Udp6Service\r
+  );\r
+\r
+/**\r
+  This function demultiplexes the received udp datagram to the apropriate instances.\r
+\r
+  @param[in]  Udp6Service        Pointer to the udp service context data.\r
+  @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA abstrated from\r
+                                 the received datagram.\r
+  @param[in]  Packet             Pointer to the buffer containing the received udp\r
+                                 datagram.\r
+\r
+**/\r
+VOID\r
+Udp6Demultiplex (\r
+  IN UDP6_SERVICE_DATA     *Udp6Service,\r
+  IN EFI_NET_SESSION_DATA  *NetSession,\r
+  IN NET_BUF               *Packet\r
+  );\r
+\r
+/**\r
+  This function handles the received Icmp Error message and demultiplexes it to the\r
+  instance.\r
+\r
+  @param[in]       Udp6Service        Pointer to the udp service context data.\r
+  @param[in]       IcmpError          The icmp error code.\r
+  @param[in]       NetSession         Pointer to the EFI_NET_SESSION_DATA abstracted\r
+                                      from the received Icmp Error packet.\r
+  @param[in, out]  Packet             Pointer to the Icmp Error packet.\r
+\r
+**/\r
+VOID\r
+Udp6IcmpHandler (\r
+  IN UDP6_SERVICE_DATA     *Udp6Service,\r
+  IN UINT8                 IcmpError,\r
+  IN EFI_NET_SESSION_DATA  *NetSession,\r
+  IN OUT NET_BUF           *Packet\r
+  );\r
+\r
+/**\r
+  This function builds and sends out a icmp port unreachable message.\r
+\r
+  @param[in]  IpIo               Pointer to the IP_IO instance.\r
+  @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA of the packet\r
+                                 causes this icmp error message.\r
+  @param[in]  Udp6Header         Pointer to the udp header of the datagram causes\r
+                                 this icmp error message.\r
+\r
+**/\r
+VOID\r
+Udp6SendPortUnreach (\r
+  IN IP_IO                 *IpIo,\r
+  IN EFI_NET_SESSION_DATA  *NetSession,\r
+  IN VOID                  *Udp6Header\r
+  );\r
+\r
+/**\r
+  Find the key in the netmap\r
+\r
+  @param[in]  Map                    The netmap to search within.\r
+  @param[in]  Key                    The key to search.\r
+\r
+  @return The point to the item contains the Key, or NULL if Key isn't in the map.\r
+\r
+**/\r
+NET_MAP_ITEM *\r
+Udp6MapMultiCastAddr (\r
+  IN  NET_MAP               *Map,\r
+  IN  VOID                  *Key\r
+  );\r
+\r
+/**\r
+  Create the Udp service context data.\r
+\r
+  @param[in]  Udp6Service        Pointer to the UDP6_SERVICE_DATA.\r
+  @param[in]  ImageHandle        The image handle of this udp6 driver.\r
+  @param[in]  ControllerHandle   The controller handle this udp6 driver binds on.\r
+\r
+  @retval EFI_SUCCESS            The udp6 service context data was created and\r
+                                 initialized.\r
+  @retval EFI_OUT_OF_RESOURCES   Cannot allocate memory.\r
+  @retval Others                 An error condition occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6CreateService (\r
+  IN UDP6_SERVICE_DATA  *Udp6Service,\r
+  IN EFI_HANDLE         ImageHandle,\r
+  IN EFI_HANDLE         ControllerHandle\r
+  )\r
+{\r
+  EFI_STATUS       Status;\r
+  IP_IO_OPEN_DATA  OpenData;\r
+\r
+  ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA));\r
+\r
+  Udp6Service->Signature        = UDP6_SERVICE_DATA_SIGNATURE;\r
+  Udp6Service->ServiceBinding   = mUdp6ServiceBinding;\r
+  Udp6Service->ImageHandle      = ImageHandle;\r
+  Udp6Service->ControllerHandle = ControllerHandle;\r
+  Udp6Service->ChildrenNumber   = 0;\r
+\r
+  InitializeListHead (&Udp6Service->ChildrenList);\r
+\r
+  //\r
+  // Create the IpIo for this service context.\r
+  //\r
+  Udp6Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_6);\r
+  if (Udp6Service->IpIo == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Set the OpenData used to open the IpIo.\r
+  //\r
+  CopyMem (\r
+    &OpenData.IpConfigData.Ip6CfgData,\r
+    &mIp6IoDefaultIpConfigData,\r
+    sizeof (EFI_IP6_CONFIG_DATA)\r
+    );\r
+  OpenData.RcvdContext           = (VOID *) Udp6Service;\r
+  OpenData.SndContext            = NULL;\r
+  OpenData.PktRcvdNotify         = Udp6DgramRcvd;\r
+  OpenData.PktSentNotify         = Udp6DgramSent;\r
+\r
+  //\r
+  // Configure and start the IpIo.\r
+  //\r
+  Status = IpIoOpen (Udp6Service->IpIo, &OpenData);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create the event for Udp timeout checking.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  Udp6CheckTimeout,\r
+                  Udp6Service,\r
+                  &Udp6Service->TimeoutEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Start the timeout timer event.\r
+  //\r
+  Status = gBS->SetTimer (\r
+                  Udp6Service->TimeoutEvent,\r
+                  TimerPeriodic,\r
+                  UDP6_TIMEOUT_INTERVAL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  if (Udp6Service->TimeoutEvent != NULL) {\r
+    gBS->CloseEvent (Udp6Service->TimeoutEvent);\r
+  }\r
+\r
+  IpIoDestroy (Udp6Service->IpIo);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Clean the Udp service context data.\r
+\r
+  @param[in, out]  Udp6Service      Pointer to the UDP6_SERVICE_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6CleanService (\r
+  IN OUT UDP6_SERVICE_DATA  *Udp6Service\r
+  )\r
+{\r
+  //\r
+  // Close the TimeoutEvent timer.\r
+  //\r
+  gBS->CloseEvent (Udp6Service->TimeoutEvent);\r
+\r
+  //\r
+  // Destroy the IpIo.\r
+  //\r
+  IpIoDestroy (Udp6Service->IpIo);\r
+}\r
+\r
+\r
+/**\r
+  This function checks and times out the I/O datagrams listed in the\r
+  UDP6_SERVICE_DATA which is specified by the input parameter Context.\r
+\r
+\r
+  @param[in]  Event              The event this function registered to.\r
+  @param[in]  Context            The context data registered during the creation of\r
+                                 the Event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6CheckTimeout (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  UDP6_SERVICE_DATA   *Udp6Service;\r
+  LIST_ENTRY          *Entry;\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+  LIST_ENTRY          *WrapEntry;\r
+  LIST_ENTRY          *NextEntry;\r
+  UDP6_RXDATA_WRAP    *Wrap;\r
+\r
+  Udp6Service = (UDP6_SERVICE_DATA *) Context;\r
+  NET_CHECK_SIGNATURE (Udp6Service, UDP6_SERVICE_DATA_SIGNATURE);\r
+\r
+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+    //\r
+    // Iterate all the instances belonging to this service context.\r
+    //\r
+    Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+    NET_CHECK_SIGNATURE (Instance, UDP6_INSTANCE_DATA_SIGNATURE);\r
+\r
+    if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {\r
+      //\r
+      // Skip this instance if it's not configured or no receive timeout.\r
+      //\r
+      continue;\r
+    }\r
+\r
+    NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {\r
+      //\r
+      // Iterate all the rxdatas belonging to this udp instance.\r
+      //\r
+      Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP6_RXDATA_WRAP, Link);\r
+\r
+      if (Wrap->TimeoutTick < UDP6_TIMEOUT_INTERVAL / 10) {\r
+        //\r
+        // Remove this RxData if it timeouts.\r
+        //\r
+        Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);\r
+      } else {\r
+        Wrap->TimeoutTick -= UDP6_TIMEOUT_INTERVAL / 10;\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function intializes the new created udp instance.\r
+\r
+  @param[in]       Udp6Service      Pointer to the UDP6_SERVICE_DATA.\r
+  @param[in, out]  Instance         Pointer to the un-initialized UDP6_INSTANCE_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6InitInstance (\r
+  IN UDP6_SERVICE_DATA       *Udp6Service,\r
+  IN OUT UDP6_INSTANCE_DATA  *Instance\r
+  )\r
+{\r
+  //\r
+  // Set the signature.\r
+  //\r
+  Instance->Signature = UDP6_INSTANCE_DATA_SIGNATURE;\r
+\r
+  //\r
+  // Init the lists.\r
+  //\r
+  InitializeListHead (&Instance->Link);\r
+  InitializeListHead (&Instance->RcvdDgramQue);\r
+  InitializeListHead (&Instance->DeliveredDgramQue);\r
+\r
+  //\r
+  // Init the NET_MAPs.\r
+  //\r
+  NetMapInit (&Instance->TxTokens);\r
+  NetMapInit (&Instance->RxTokens);\r
+  NetMapInit (&Instance->McastIps);\r
+\r
+  //\r
+  // Save the pointer to the UDP6_SERVICE_DATA, and initialize other members.\r
+  //\r
+  Instance->Udp6Service = Udp6Service;\r
+  CopyMem (&Instance->Udp6Proto, &mUdp6Protocol, sizeof (EFI_UDP6_PROTOCOL));\r
+  Instance->IcmpError   = EFI_SUCCESS;\r
+  Instance->Configured  = FALSE;\r
+  Instance->IsNoMapping = FALSE;\r
+  Instance->Destroyed   = FALSE;\r
+}\r
+\r
+\r
+/**\r
+  This function cleans the udp instance.\r
+\r
+  @param[in, out]  Instance       Pointer to the UDP6_INSTANCE_DATA to clean.\r
+\r
+**/\r
+VOID\r
+Udp6CleanInstance (\r
+  IN OUT UDP6_INSTANCE_DATA  *Instance\r
+  )\r
+{\r
+  NetMapClean (&Instance->McastIps);\r
+  NetMapClean (&Instance->RxTokens);\r
+  NetMapClean (&Instance->TxTokens);\r
+}\r
+\r
+\r
+/**\r
+  This function finds the udp instance by the specified <Address, Port> pair.\r
+\r
+  @param[in]  InstanceList       Pointer to the head of the list linking the udp\r
+                                 instances.\r
+  @param[in]  Address            Pointer to the specified IPv6 address.\r
+  @param[in]  Port               The udp port number.\r
+\r
+  @retval TRUE     The specified <Address, Port> pair is found.\r
+  @retval FALSE    Otherwise.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6FindInstanceByPort (\r
+  IN LIST_ENTRY        *InstanceList,\r
+  IN EFI_IPv6_ADDRESS  *Address,\r
+  IN UINT16            Port\r
+  )\r
+{\r
+  LIST_ENTRY            *Entry;\r
+  UDP6_INSTANCE_DATA    *Instance;\r
+  EFI_UDP6_CONFIG_DATA  *ConfigData;\r
+\r
+  NET_LIST_FOR_EACH (Entry, InstanceList) {\r
+    //\r
+    // Iterate all the udp instances.\r
+    //\r
+    Instance   = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+    ConfigData = &Instance->ConfigData;\r
+\r
+    if (!Instance->Configured || ConfigData->AcceptAnyPort) {\r
+      //\r
+      // If the instance is not configured, or the configdata of the instance indicates\r
+      // this instance accepts any port, skip it.\r
+      //\r
+      continue;\r
+    }\r
+\r
+    if (EFI_IP6_EQUAL (&ConfigData->StationAddress, Address) &&\r
+        (ConfigData->StationPort == Port)\r
+        ) {\r
+      //\r
+      // If both the address and the port are the same, return TRUE.\r
+      //\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Return FALSE when matching fails.\r
+  //\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  This function tries to bind the udp instance according to the configured port\r
+  allocation stragety.\r
+\r
+  @param[in]  InstanceList       Pointer to the head of the list linking the udp\r
+                                 instances.\r
+  @param[in]  ConfigData         Pointer to the ConfigData of the instance to be\r
+                                 bound.\r
+\r
+  @retval EFI_SUCCESS            The bound operation completed successfully.\r
+  @retval EFI_ACCESS_DENIED      The <Address, Port> specified by the ConfigData is\r
+                                 already used by other instance.\r
+  @retval EFI_OUT_OF_RESOURCES   No available port resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6Bind (\r
+  IN LIST_ENTRY            *InstanceList,\r
+  IN EFI_UDP6_CONFIG_DATA  *ConfigData\r
+  )\r
+{\r
+  EFI_IPv6_ADDRESS  *StationAddress;\r
+  UINT16            StartPort;\r
+\r
+  if (ConfigData->AcceptAnyPort) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  StationAddress = &ConfigData->StationAddress;\r
+\r
+  if (ConfigData->StationPort != 0) {\r
+\r
+    if (!ConfigData->AllowDuplicatePort &&\r
+        Udp6FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)\r
+        ) {\r
+      //\r
+      // Do not allow duplicate ports and the port is already used by other instance.\r
+      //\r
+      return EFI_ACCESS_DENIED;\r
+    }\r
+  } else {\r
+    //\r
+    // Select a random port for this instance.\r
+    //\r
+    if (ConfigData->AllowDuplicatePort) {\r
+      //\r
+      // Just pick up the random port if the instance allows duplicate port.\r
+      //\r
+      ConfigData->StationPort = mUdp6RandomPort;\r
+    } else {\r
+\r
+      StartPort = mUdp6RandomPort;\r
+\r
+      while (Udp6FindInstanceByPort (InstanceList, StationAddress, mUdp6RandomPort)) {\r
+\r
+        mUdp6RandomPort++;\r
+        if (mUdp6RandomPort == 0) {\r
+          mUdp6RandomPort = UDP6_PORT_KNOWN;\r
+        }\r
+\r
+        if (mUdp6RandomPort == StartPort) {\r
+          //\r
+          // No available port.\r
+          //\r
+          return EFI_OUT_OF_RESOURCES;\r
+        }\r
+      }\r
+\r
+      ConfigData->StationPort = mUdp6RandomPort;\r
+    }\r
+\r
+    mUdp6RandomPort++;\r
+    if (mUdp6RandomPort == 0) {\r
+      mUdp6RandomPort = UDP6_PORT_KNOWN;\r
+    }\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  This function is used to check whether the NewConfigData has any un-reconfigurable\r
+  parameters changed compared to the OldConfigData.\r
+\r
+  @param[in]  OldConfigData    Pointer to the current ConfigData the udp instance\r
+                               uses.\r
+  @param[in]  NewConfigData    Pointer to the new ConfigData.\r
+\r
+  @retval TRUE     The instance is reconfigurable according to the NewConfigData.\r
+  @retval FALSE    Otherwise.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6IsReconfigurable (\r
+  IN EFI_UDP6_CONFIG_DATA  *OldConfigData,\r
+  IN EFI_UDP6_CONFIG_DATA  *NewConfigData\r
+  )\r
+{\r
+  if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) ||\r
+      (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) ||\r
+      (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort)\r
+      ) {\r
+    //\r
+    // The receiving filter parameters cannot be changed.\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+  if ((!NewConfigData->AcceptAnyPort) &&\r
+      (NewConfigData->StationPort != OldConfigData->StationPort)\r
+      ) {\r
+    //\r
+    // The port is not changeable.\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+  if (!EFI_IP6_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress)) {\r
+    //\r
+    //  The StationAddress is not the same.\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+\r
+  if (!EFI_IP6_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) {\r
+    //\r
+    // The remoteaddress is not the same.\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+  if (!NetIp6IsUnspecifiedAddr (&NewConfigData->RemoteAddress) &&\r
+      (NewConfigData->RemotePort != OldConfigData->RemotePort)\r
+      ) {\r
+    //\r
+    // The RemotePort differs if it's designated in the configdata.\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // All checks pass, return TRUE.\r
+  //\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  This function builds the Ip6 configdata from the Udp6ConfigData.\r
+\r
+  @param[in]       Udp6ConfigData         Pointer to the EFI_UDP6_CONFIG_DATA.\r
+  @param[in, out]  Ip6ConfigData          Pointer to the EFI_IP6_CONFIG_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6BuildIp6ConfigData (\r
+  IN EFI_UDP6_CONFIG_DATA      *Udp6ConfigData,\r
+  IN OUT EFI_IP6_CONFIG_DATA   *Ip6ConfigData\r
+  )\r
+{\r
+  CopyMem (\r
+    Ip6ConfigData,\r
+    &mIp6IoDefaultIpConfigData,\r
+    sizeof (EFI_IP6_CONFIG_DATA)\r
+    );\r
+  Ip6ConfigData->DefaultProtocol      = EFI_IP_PROTO_UDP;\r
+  Ip6ConfigData->AcceptPromiscuous    = Udp6ConfigData->AcceptPromiscuous;\r
+  IP6_COPY_ADDRESS (&Ip6ConfigData->StationAddress, &Udp6ConfigData->StationAddress);\r
+  IP6_COPY_ADDRESS (&Ip6ConfigData->DestinationAddress, &Udp6ConfigData->RemoteAddress);\r
+  //\r
+  // Use the -1 magic number to disable the receiving process of the ip instance.\r
+  //\r
+  Ip6ConfigData->ReceiveTimeout    = (UINT32) (-1);\r
+}\r
+\r
+\r
+/**\r
+  This function validates the TxToken. It returns the error code according to the spec.\r
+\r
+  @param[in]  Instance           Pointer to the udp instance context data.\r
+  @param[in]  TxToken            Pointer to the token to be checked.\r
+\r
+  @retval EFI_SUCCESS            The TxToken is valid.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 Token.Event is NULL;\r
+                                 Token.Packet.TxData is NULL;\r
+                                 Token.Packet.TxData.FragmentCount is zero;\r
+                                 Token.Packet.TxData.DataLength is not equal to the\r
+                                 sum of fragment lengths;\r
+                                 One or more of the\r
+                                 Token.Packet.TxData.FragmentTable[].FragmentLength\r
+                                 fields is zero;\r
+                                 One or more of the\r
+                                 Token.Packet.TxData.FragmentTable[].FragmentBuffer\r
+                                 fields is NULL;\r
+                                 UdpSessionData.DestinationAddress are not valid\r
+                                 unicast IPv6 addresses if the UdpSessionData is\r
+                                 not NULL;\r
+                                 UdpSessionData.DestinationPort and\r
+                                 ConfigData.RemotePort are all zero if the\r
+                                 UdpSessionData is not NULL.\r
+  @retval EFI_BAD_BUFFER_SIZE    The data length is greater than the maximum UDP\r
+                                 packet size.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6ValidateTxToken (\r
+  IN UDP6_INSTANCE_DATA         *Instance,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *TxToken\r
+  )\r
+{\r
+  EFI_UDP6_TRANSMIT_DATA  *TxData;\r
+  UINT32                  Index;\r
+  UINT32                  TotalLen;\r
+  EFI_UDP6_CONFIG_DATA    *ConfigData;\r
+  EFI_UDP6_SESSION_DATA   *UdpSessionData;\r
+\r
+\r
+  if (TxToken->Event == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  TxData = TxToken->Packet.TxData;\r
+\r
+  if ((TxData == NULL) || (TxData->FragmentCount == 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  TotalLen = 0;\r
+  for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
+\r
+    if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||\r
+        (TxData->FragmentTable[Index].FragmentLength == 0)\r
+        ) {\r
+      //\r
+      // If the FragmentBuffer is NULL, or the FragmentLeng is zero.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    TotalLen += TxData->FragmentTable[Index].FragmentLength;\r
+  }\r
+\r
+  if (TotalLen != TxData->DataLength) {\r
+    //\r
+    // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the\r
+    // DataLength.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ConfigData     = &Instance->ConfigData;\r
+  UdpSessionData = TxData->UdpSessionData;\r
+\r
+  if (UdpSessionData != NULL) {\r
+\r
+    if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) {\r
+      //\r
+      // Ambiguous; no avalaible DestinationPort for this token.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&\r
+        NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)\r
+        ) {\r
+      //\r
+      // The DestinationAddress is not specificed.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&\r
+        !NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)\r
+        ) {\r
+      //\r
+      // The ConfigData.RemoteAddress is not zero and the UdpSessionData.DestinationAddress\r
+      // is not zero too.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  } else if (NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)) {\r
+    //\r
+    // The configured RemoteAddress is all zero, and the user doesn't override the\r
+    // destination address.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (TxData->DataLength > UDP6_MAX_DATA_SIZE) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  This function checks whether the specified Token duplicates the one in the Map.\r
+\r
+  @param[in]  Map                Pointer to the NET_MAP.\r
+  @param[in]  Item               Pointer to the NET_MAP_ITEM contain the pointer to\r
+                                 the Token.\r
+  @param[in]  Context            Pointer to the Token to be checked.\r
+\r
+  @retval EFI_SUCCESS            The Token specified by Context differs from the\r
+                                 one in the Item.\r
+  @retval EFI_ACCESS_DENIED      The Token duplicates with the one in the Item.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6TokenExist (\r
+  IN NET_MAP       *Map,\r
+  IN NET_MAP_ITEM  *Item,\r
+  IN VOID          *Context\r
+  )\r
+{\r
+  EFI_UDP6_COMPLETION_TOKEN  *Token;\r
+  EFI_UDP6_COMPLETION_TOKEN  *TokenInItem;\r
+\r
+  Token       = (EFI_UDP6_COMPLETION_TOKEN *) Context;\r
+  TokenInItem = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;\r
+\r
+  if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {\r
+    //\r
+    // The Token duplicates with the TokenInItem in case either the two pointers are the\r
+    // same, or the Events of these two tokens are the same.\r
+    //\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  This function calculates the checksum for the Packet, utilizing the pre-calculated\r
+  pseudo HeadSum to reduce some overhead.\r
+\r
+  @param[in]  Packet           Pointer to the NET_BUF contains the udp datagram.\r
+  @param[in]  HeadSum          Checksum of the pseudo header, execpt the length\r
+                               field.\r
+\r
+  @return The 16-bit checksum of this udp datagram.\r
+\r
+**/\r
+UINT16\r
+Udp6Checksum (\r
+  IN NET_BUF *Packet,\r
+  IN UINT16  HeadSum\r
+  )\r
+{\r
+  UINT16  Checksum;\r
+\r
+  Checksum  = NetbufChecksum (Packet);\r
+  Checksum  = NetAddChecksum (Checksum, HeadSum);\r
+\r
+  Checksum  = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize));\r
+  Checksum  = (UINT16) (~Checksum);\r
+  return Checksum;\r
+}\r
+\r
+\r
+/**\r
+  This function removes the specified Token from the TokenMap.\r
+\r
+  @param[in]  TokenMap           Pointer to the NET_MAP containing the tokens.\r
+  @param[in]  Token              Pointer to the Token to be removed.\r
+\r
+  @retval EFI_SUCCESS            The specified Token is removed from the TokenMap.\r
+  @retval EFI_NOT_FOUND          The specified Token is not found in the TokenMap.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6RemoveToken (\r
+  IN NET_MAP                    *TokenMap,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token\r
+  )\r
+{\r
+  NET_MAP_ITEM  *Item;\r
+\r
+  //\r
+  // Find the Token first.\r
+  //\r
+  Item = NetMapFindKey (TokenMap, (VOID *) Token);\r
+\r
+  if (Item != NULL) {\r
+    //\r
+    // Remove the token if it's found in the map.\r
+    //\r
+    NetMapRemoveItem (TokenMap, Item, NULL);\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+  This function is the packet transmitting notify function registered to the IpIo\r
+  interface. It's called to signal the udp TxToken when IpIo layer completes the\r
+  transmitting of the udp datagram.\r
+\r
+  @param[in]  Status            The completion status of the output udp datagram.\r
+  @param[in]  Context           Pointer to the context data.\r
+  @param[in]  Sender            Specify a EFI_IP6_PROTOCOL for sending.\r
+  @param[in]  NotifyData        Pointer to the notify data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6DgramSent (\r
+  IN EFI_STATUS        Status,\r
+  IN VOID              *Context,\r
+  IN IP_IO_IP_PROTOCOL Sender,\r
+  IN VOID              *NotifyData\r
+  )\r
+{\r
+  UDP6_INSTANCE_DATA         *Instance;\r
+  EFI_UDP6_COMPLETION_TOKEN  *Token;\r
+\r
+  Instance = (UDP6_INSTANCE_DATA *) Context;\r
+  Token    = (EFI_UDP6_COMPLETION_TOKEN *) NotifyData;\r
+\r
+  if (Udp6RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) {\r
+    //\r
+    // The token may be cancelled. Only signal it if the remove operation succeeds.\r
+    //\r
+    Token->Status = Status;\r
+    gBS->SignalEvent (Token->Event);\r
+    DispatchDpc ();\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function processes the received datagram passed up by the IpIo layer.\r
+\r
+  @param[in]  Status            The status of this udp datagram.\r
+  @param[in]  IcmpError         The IcmpError code, only available when Status is\r
+                                EFI_ICMP_ERROR.\r
+  @param[in]  NetSession        Pointer to the EFI_NET_SESSION_DATA.\r
+  @param[in]  Packet            Pointer to the NET_BUF containing the received udp\r
+                                datagram.\r
+  @param[in]  Context           Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6DgramRcvd (\r
+  IN EFI_STATUS            Status,\r
+  IN UINT8                 IcmpError,\r
+  IN EFI_NET_SESSION_DATA  *NetSession,\r
+  IN NET_BUF               *Packet,\r
+  IN VOID                  *Context\r
+  )\r
+{\r
+  NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);\r
+\r
+  //\r
+  // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR.\r
+  //\r
+  if (Status == EFI_SUCCESS) {\r
+\r
+    //\r
+    // Demultiplex the received datagram.\r
+    //\r
+    Udp6Demultiplex ((UDP6_SERVICE_DATA *) Context, NetSession, Packet);\r
+  } else {\r
+    //\r
+    // Handle the ICMP6 Error packet.\r
+    //\r
+    Udp6IcmpHandler ((UDP6_SERVICE_DATA *) Context, IcmpError, NetSession, Packet);\r
+  }\r
+\r
+  //\r
+  // Dispatch the DPC queued by the NotifyFunction of the rx token's events\r
+  // that are signaled with received data.\r
+  //\r
+  DispatchDpc ();\r
+}\r
+\r
+\r
+/**\r
+  This function removes the multicast group specified by Arg from the Map.\r
+\r
+  @param[in]  Map                Pointer to the NET_MAP.\r
+  @param[in]  Item               Pointer to the NET_MAP_ITEM.\r
+  @param[in]  Arg                Pointer to the Arg, it's the pointer to a\r
+                                 multicast IPv6 Address. This parameter is\r
+                                 optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The multicast address is removed.\r
+  @retval EFI_ABORTED            The specified multicast address is removed, and the\r
+                                 Arg is not NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6LeaveGroup (\r
+  IN NET_MAP       *Map,\r
+  IN NET_MAP_ITEM  *Item,\r
+  IN VOID          *Arg OPTIONAL\r
+  )\r
+{\r
+  EFI_IPv6_ADDRESS  *McastIp;\r
+\r
+  McastIp = Arg;\r
+\r
+  if ((McastIp != NULL) &&\r
+      !EFI_IP6_EQUAL (McastIp, ((EFI_IPv6_ADDRESS *)Item->Key))\r
+      ) {\r
+    //\r
+    // McastIp is not NULL and the multicast address contained in the Item\r
+    // is not the same as McastIp.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  FreePool (Item->Key);\r
+\r
+  //\r
+  // Remove this Item.\r
+  //\r
+  NetMapRemoveItem (Map, Item, NULL);\r
+\r
+  if (McastIp != NULL) {\r
+    //\r
+    // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration.\r
+    //\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  This function cancle the token specified by Arg in the Map.\r
+\r
+  @param[in]  Map             Pointer to the NET_MAP.\r
+  @param[in]  Item            Pointer to the NET_MAP_ITEM.\r
+  @param[in]  Arg             Pointer to the token to be cancelled. If NULL, all\r
+                              the tokens in this Map will be cancelled.\r
+                              This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS         The token is cancelled if Arg is NULL, or the token\r
+                              is not the same as that in the Item, if Arg is not\r
+                              NULL.\r
+  @retval EFI_ABORTED         Arg is not NULL, and the token specified by Arg is\r
+                              cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6CancelTokens (\r
+  IN NET_MAP       *Map,\r
+  IN NET_MAP_ITEM  *Item,\r
+  IN VOID          *Arg OPTIONAL\r
+  )\r
+{\r
+  EFI_UDP6_COMPLETION_TOKEN  *TokenToCancel;\r
+  NET_BUF                    *Packet;\r
+  IP_IO                      *IpIo;\r
+\r
+  if ((Arg != NULL) && (Item->Key != Arg)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (Item->Value != NULL) {\r
+    //\r
+    // If the token is a transmit token, the corresponding Packet is recorded in\r
+    // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken\r
+    // will invoke Udp6DgramSent, the token will be signaled and this Item will\r
+    // be removed from the Map there.\r
+    //\r
+    Packet  = (NET_BUF *) (Item->Value);\r
+    IpIo    = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0]));\r
+\r
+    IpIoCancelTxToken (IpIo, Packet);\r
+  } else {\r
+    //\r
+    // The token is a receive token. Abort it and remove it from the Map.\r
+    //\r
+    TokenToCancel = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;\r
+    NetMapRemoveItem (Map, Item, NULL);\r
+\r
+    TokenToCancel->Status = EFI_ABORTED;\r
+    gBS->SignalEvent (TokenToCancel->Event);\r
+  }\r
+\r
+  if (Arg != NULL) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  This function removes all the Wrap datas in the RcvdDgramQue.\r
+\r
+  @param[in]  Instance    Pointer to the Udp6 Instance.\r
+\r
+**/\r
+VOID\r
+Udp6FlushRcvdDgram (\r
+  IN UDP6_INSTANCE_DATA  *Instance\r
+  )\r
+{\r
+  UDP6_RXDATA_WRAP  *Wrap;\r
+\r
+  while (!IsListEmpty (&Instance->RcvdDgramQue)) {\r
+    //\r
+    // Iterate all the Wraps in the RcvdDgramQue.\r
+    //\r
+    Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);\r
+\r
+    //\r
+    // The Wrap will be removed from the RcvdDgramQue by this function call.\r
+    //\r
+    Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);\r
+  }\r
+}\r
+\r
+\r
+\r
+/**\r
+  Cancel Udp6 tokens from the Udp6 instance.\r
+\r
+  @param[in]  Instance           Pointer to the udp instance context data.\r
+  @param[in]  Token              Pointer to the token to be canceled. If NULL, all\r
+                                 tokens in this instance will be cancelled.\r
+                                 This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The Token is cancelled.\r
+  @retval EFI_NOT_FOUND          The Token is not found.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6InstanceCancelToken (\r
+  IN UDP6_INSTANCE_DATA         *Instance,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  //\r
+  // Cancel this token from the TxTokens map.\r
+  //\r
+  Status = NetMapIterate (&Instance->TxTokens, Udp6CancelTokens, Token);\r
+\r
+  if ((Token != NULL) && (Status == EFI_ABORTED)) {\r
+    //\r
+    // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from\r
+    // the TxTokens and returns success.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Try to cancel this token from the RxTokens map in condition either the Token\r
+  // is NULL or the specified Token is not in TxTokens.\r
+  //\r
+  Status = NetMapIterate (&Instance->RxTokens, Udp6CancelTokens, Token);\r
+\r
+  if ((Token != NULL) && (Status == EFI_SUCCESS)) {\r
+    //\r
+    // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the\r
+    // TxTokens nor the RxTokens, or say, it's not found.\r
+    //\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  ASSERT ((Token != NULL) ||\r
+          ((0 == NetMapGetCount (&Instance->TxTokens)) &&\r
+          (0 == NetMapGetCount (&Instance->RxTokens)))\r
+          );\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  This function checks if the received udp datagram matches with the Instance.\r
+\r
+  @param[in]  Instance           Pointer to the udp instance context data.\r
+  @param[in]  Udp6Session        Pointer to the EFI_UDP6_SESSION_DATA abstracted\r
+                                 from the received udp datagram.\r
+\r
+  @retval TRUE     The udp datagram matches the receiving requirements of the Instance.\r
+  @retval FALSE    The udp datagram does not matche the receiving requirements of the Instance.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6MatchDgram (\r
+  IN UDP6_INSTANCE_DATA     *Instance,\r
+  IN EFI_UDP6_SESSION_DATA  *Udp6Session\r
+  )\r
+{\r
+  EFI_UDP6_CONFIG_DATA  *ConfigData;\r
+  EFI_IPv6_ADDRESS      Destination;\r
+\r
+  ConfigData = &Instance->ConfigData;\r
+\r
+  if (ConfigData->AcceptPromiscuous) {\r
+    //\r
+    // Always matches if this instance is in the promiscuous state.\r
+    //\r
+    return TRUE;\r
+  }\r
+\r
+  if ((!ConfigData->AcceptAnyPort && (Udp6Session->DestinationPort != ConfigData->StationPort)) ||\r
+      ((ConfigData->RemotePort != 0) && (Udp6Session->SourcePort != ConfigData->RemotePort))\r
+      ) {\r
+    //\r
+    // The local port or the remote port doesn't match.\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+  if (!NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) &&\r
+      !EFI_IP6_EQUAL (&ConfigData->RemoteAddress, &Udp6Session->SourceAddress)\r
+      ) {\r
+    //\r
+    // This datagram doesn't come from the instance's specified sender.\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+  if (NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress) ||\r
+      EFI_IP6_EQUAL (&Udp6Session->DestinationAddress, &ConfigData->StationAddress)\r
+      ) {\r
+    //\r
+    // The instance is configured to receive datagrams destinated to any station IP or\r
+    // the destination address of this datagram matches the configured station IP.\r
+    //\r
+    return TRUE;\r
+  }\r
+\r
+  IP6_COPY_ADDRESS (&Destination, &Udp6Session->DestinationAddress);\r
+\r
+  if (IP6_IS_MULTICAST (&Destination) &&\r
+      (NULL != Udp6MapMultiCastAddr (&Instance->McastIps, &Destination))\r
+      ) {\r
+    //\r
+    // It's a multicast packet and the multicast address is accepted by this instance.\r
+    //\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  This function removes the Wrap specified by Context and release relevant resources.\r
+\r
+  @param[in]  Event                  The Event this notify function registered to.\r
+  @param[in]  Context                Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6RecycleRxDataWrap (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  UDP6_RXDATA_WRAP  *Wrap;\r
+\r
+  Wrap = (UDP6_RXDATA_WRAP *) Context;\r
+\r
+  //\r
+  // Remove the Wrap from the list it belongs to.\r
+  //\r
+  RemoveEntryList (&Wrap->Link);\r
+\r
+  //\r
+  // Free the Packet associated with this Wrap.\r
+  //\r
+  NetbufFree (Wrap->Packet);\r
+\r
+  //\r
+  // Close the event.\r
+  //\r
+  gBS->CloseEvent (Wrap->RxData.RecycleSignal);\r
+\r
+  FreePool (Wrap);\r
+}\r
+\r
+\r
+/**\r
+  This function wraps the Packet into RxData.\r
+\r
+  @param[in]  Instance           Pointer to the instance context data.\r
+  @param[in]  Packet             Pointer to the buffer containing the received\r
+                                 datagram.\r
+  @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this\r
+                                 datagram.\r
+\r
+  @return Pointer to the structure wrapping the RxData and the Packet.\r
+\r
+**/\r
+UDP6_RXDATA_WRAP *\r
+Udp6WrapRxData (\r
+  IN UDP6_INSTANCE_DATA     *Instance,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_UDP6_RECEIVE_DATA  *RxData\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UDP6_RXDATA_WRAP      *Wrap;\r
+\r
+  //\r
+  // Allocate buffer for the Wrap.\r
+  //\r
+  Wrap = AllocateZeroPool (sizeof (UDP6_RXDATA_WRAP) +\r
+         (Packet->BlockOpNum - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA));\r
+  if (Wrap == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  InitializeListHead (&Wrap->Link);\r
+\r
+  CopyMem (&Wrap->RxData, RxData, sizeof(EFI_UDP6_RECEIVE_DATA));\r
+  //\r
+  // Create the Recycle event.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  Udp6RecycleRxDataWrap,\r
+                  Wrap,\r
+                  &Wrap->RxData.RecycleSignal\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Wrap);\r
+    return NULL;\r
+  }\r
+\r
+  Wrap->Packet      = Packet;\r
+  Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;\r
+\r
+  return Wrap;\r
+}\r
+\r
+\r
+/**\r
+  This function enqueues the received datagram into the instances' receiving queues.\r
+\r
+  @param[in]  Udp6Service        Pointer to the udp service context data.\r
+  @param[in]  Packet             Pointer to the buffer containing the received\r
+                                 datagram.\r
+  @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this\r
+                                 datagram.\r
+\r
+  @return The times this datagram is enqueued.\r
+\r
+**/\r
+UINTN\r
+Udp6EnqueueDgram (\r
+  IN UDP6_SERVICE_DATA      *Udp6Service,\r
+  IN NET_BUF                *Packet,\r
+  IN EFI_UDP6_RECEIVE_DATA  *RxData\r
+  )\r
+{\r
+  LIST_ENTRY          *Entry;\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+  UDP6_RXDATA_WRAP    *Wrap;\r
+  UINTN               Enqueued;\r
+\r
+  Enqueued = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+    //\r
+    // Iterate the instances.\r
+    //\r
+    Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+\r
+    if (!Instance->Configured) {\r
+      continue;\r
+    }\r
+\r
+    if (Udp6MatchDgram (Instance, &RxData->UdpSession)) {\r
+      //\r
+      // Wrap the RxData and put this Wrap into the instances RcvdDgramQue.\r
+      //\r
+      Wrap = Udp6WrapRxData (Instance, Packet, RxData);\r
+      if (Wrap == NULL) {\r
+        continue;\r
+      }\r
+\r
+      NET_GET_REF (Packet);\r
+\r
+      InsertTailList (&Instance->RcvdDgramQue, &Wrap->Link);\r
+\r
+      Enqueued++;\r
+    }\r
+  }\r
+\r
+  return Enqueued;\r
+}\r
+\r
+\r
+/**\r
+  This function delivers the received datagrams to the specified instance.\r
+\r
+  @param[in]  Instance               Pointer to the instance context data.\r
+\r
+**/\r
+VOID\r
+Udp6InstanceDeliverDgram (\r
+  IN UDP6_INSTANCE_DATA  *Instance\r
+  )\r
+{\r
+  UDP6_RXDATA_WRAP           *Wrap;\r
+  EFI_UDP6_COMPLETION_TOKEN  *Token;\r
+  NET_BUF                    *Dup;\r
+  EFI_UDP6_RECEIVE_DATA      *RxData;\r
+  EFI_TPL                    OldTpl;\r
+\r
+  if (!IsListEmpty (&Instance->RcvdDgramQue) &&\r
+      !NetMapIsEmpty (&Instance->RxTokens)\r
+      ) {\r
+\r
+    Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);\r
+\r
+    if (NET_BUF_SHARED (Wrap->Packet)) {\r
+      //\r
+      // Duplicate the Packet if it is shared between instances.\r
+      //\r
+      Dup = NetbufDuplicate (Wrap->Packet, NULL, 0);\r
+      if (Dup == NULL) {\r
+        return;\r
+      }\r
+\r
+      NetbufFree (Wrap->Packet);\r
+\r
+      Wrap->Packet = Dup;\r
+    }\r
+\r
+    NetListRemoveHead (&Instance->RcvdDgramQue);\r
+\r
+    Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);\r
+\r
+    //\r
+    // Build the FragmentTable and set the FragmentCount in RxData.\r
+    //\r
+    RxData                = &Wrap->RxData;\r
+    RxData->FragmentCount = Wrap->Packet->BlockOpNum;\r
+\r
+    NetbufBuildExt (\r
+      Wrap->Packet,\r
+      (NET_FRAGMENT *) RxData->FragmentTable,\r
+      &RxData->FragmentCount\r
+      );\r
+\r
+    Token->Status        = EFI_SUCCESS;\r
+    Token->Packet.RxData = &Wrap->RxData;\r
+\r
+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+    InsertTailList (&Instance->DeliveredDgramQue, &Wrap->Link);\r
+    gBS->RestoreTPL (OldTpl);\r
+\r
+    gBS->SignalEvent (Token->Event);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function delivers the datagrams enqueued in the instances.\r
+\r
+  @param[in]  Udp6Service            Pointer to the udp service context data.\r
+\r
+**/\r
+VOID\r
+Udp6DeliverDgram (\r
+  IN UDP6_SERVICE_DATA  *Udp6Service\r
+  )\r
+{\r
+  LIST_ENTRY          *Entry;\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+    //\r
+    // Iterate the instances.\r
+    //\r
+    Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+\r
+    if (!Instance->Configured) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Deliver the datagrams of this instance.\r
+    //\r
+    Udp6InstanceDeliverDgram (Instance);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function demultiplexes the received udp datagram to the appropriate instances.\r
+\r
+  @param[in]  Udp6Service        Pointer to the udp service context data.\r
+  @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA abstrated from\r
+                                 the received datagram.\r
+  @param[in]  Packet             Pointer to the buffer containing the received udp\r
+                                 datagram.\r
+\r
+**/\r
+VOID\r
+Udp6Demultiplex (\r
+  IN UDP6_SERVICE_DATA     *Udp6Service,\r
+  IN EFI_NET_SESSION_DATA  *NetSession,\r
+  IN NET_BUF               *Packet\r
+  )\r
+{\r
+  EFI_UDP_HEADER        *Udp6Header;\r
+  UINT16                 HeadSum;\r
+  EFI_UDP6_RECEIVE_DATA  RxData;\r
+  EFI_UDP6_SESSION_DATA  *Udp6Session;\r
+  UINTN                  Enqueued;\r
+\r
+  //\r
+  // Get the datagram header from the packet buffer.\r
+  //\r
+  Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+  ASSERT (Udp6Header != NULL);\r
+\r
+  if (Udp6Header->Checksum != 0) {\r
+    //\r
+    // check the checksum.\r
+    //\r
+    HeadSum = NetIp6PseudoHeadChecksum (\r
+                &NetSession->Source.v6,\r
+                &NetSession->Dest.v6,\r
+                EFI_IP_PROTO_UDP,\r
+                0\r
+                );\r
+\r
+    if (Udp6Checksum (Packet, HeadSum) != 0) {\r
+      //\r
+      // Wrong checksum.\r
+      //\r
+      return;\r
+    }\r
+  }\r
+\r
+  gRT->GetTime (&RxData.TimeStamp, NULL);\r
+\r
+  Udp6Session                  = &RxData.UdpSession;\r
+  Udp6Session->SourcePort      = NTOHS (Udp6Header->SrcPort);\r
+  Udp6Session->DestinationPort = NTOHS (Udp6Header->DstPort);\r
+\r
+  IP6_COPY_ADDRESS (&Udp6Session->SourceAddress, &NetSession->Source);\r
+  IP6_COPY_ADDRESS (&Udp6Session->DestinationAddress, &NetSession->Dest);\r
+\r
+  //\r
+  // Trim the UDP header.\r
+  //\r
+  NetbufTrim (Packet, UDP6_HEADER_SIZE, TRUE);\r
+\r
+  RxData.DataLength = (UINT32) Packet->TotalSize;\r
+\r
+  //\r
+  // Try to enqueue this datagram into the instances.\r
+  //\r
+  Enqueued = Udp6EnqueueDgram (Udp6Service, Packet, &RxData);\r
+\r
+  if (Enqueued == 0) {\r
+    //\r
+    // Send the port unreachable ICMP packet before we free this NET_BUF\r
+    //\r
+    Udp6SendPortUnreach (Udp6Service->IpIo, NetSession, Udp6Header);\r
+  }\r
+\r
+  //\r
+  // Try to free the packet before deliver it.\r
+  //\r
+  NetbufFree (Packet);\r
+\r
+  if (Enqueued > 0) {\r
+    //\r
+    // Deliver the datagram.\r
+    //\r
+    Udp6DeliverDgram (Udp6Service);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function builds and sends out a icmp port unreachable message.\r
+\r
+  @param[in]  IpIo               Pointer to the IP_IO instance.\r
+  @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA of the packet\r
+                                 causes this icmp error message.\r
+  @param[in]  Udp6Header         Pointer to the udp header of the datagram causes\r
+                                 this icmp error message.\r
+\r
+**/\r
+VOID\r
+Udp6SendPortUnreach (\r
+  IN IP_IO                 *IpIo,\r
+  IN EFI_NET_SESSION_DATA  *NetSession,\r
+  IN VOID                  *Udp6Header\r
+  )\r
+{\r
+  NET_BUF              *Packet;\r
+  UINT32               Len;\r
+  IP6_ICMP_ERROR_HEAD  *IcmpErrHdr;\r
+  UINT8                *Ptr;\r
+  IP_IO_OVERRIDE       Override;\r
+  IP_IO_IP_INFO        *IpSender;\r
+  EFI_IP6_MODE_DATA    *Ip6ModeData;\r
+  EFI_STATUS           Status;\r
+  EFI_IP6_PROTOCOL     *Ip6Protocol;\r
+\r
+  Ip6ModeData = NULL;\r
+\r
+  //\r
+  // An ICMPv6 error message MUST NOT be originated as A packet destined to\r
+  // 1) an IPv6 multicast address 2) The IPv6 Unspecified Address\r
+  //\r
+  if (NetSession->IpVersion == IP_VERSION_6) {\r
+    if (NetIp6IsUnspecifiedAddr (&NetSession->Dest.v6) ||\r
+      IP6_IS_MULTICAST (&NetSession->Dest.v6)\r
+      ) {\r
+      goto EXIT;\r
+    }\r
+  }\r
+\r
+\r
+  IpSender    = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest);\r
+\r
+  //\r
+  // Get the Ipv6 Mode Data.\r
+  //\r
+  Ip6ModeData = AllocateZeroPool (sizeof (EFI_IP6_MODE_DATA));\r
+  ASSERT (Ip6ModeData != NULL);\r
+\r
+  //\r
+  // If not finding the related IpSender use the default IpIo to send out\r
+  // the port unreachable ICMP message.\r
+  //\r
+  if (IpSender == NULL) {\r
+    Ip6Protocol = IpIo->Ip.Ip6;\r
+  } else {\r
+    Ip6Protocol = IpSender->Ip.Ip6;\r
+  }\r
+\r
+  Status = Ip6Protocol->GetModeData (\r
+                          Ip6Protocol,\r
+                          Ip6ModeData,\r
+                          NULL,\r
+                          NULL\r
+                          );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto EXIT;\r
+  }\r
+  //\r
+  // The ICMP6 packet length, includes whole invoking packet and ICMP6 error header.\r
+  //\r
+  Len = NetSession->IpHdrLen +\r
+        NTOHS(((EFI_UDP_HEADER *) Udp6Header)->Length) +\r
+        sizeof (IP6_ICMP_ERROR_HEAD);\r
+\r
+  //\r
+  // If the ICMP6 packet length larger than IP MTU, adjust its length to MTU.\r
+  //\r
+  if (Ip6ModeData->MaxPacketSize < Len) {\r
+    Len = Ip6ModeData->MaxPacketSize;\r
+  }\r
+\r
+  //\r
+  // Allocate buffer for the icmp error message.\r
+  //\r
+  Packet = NetbufAlloc (Len);\r
+  if (Packet == NULL) {\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Allocate space for the IP6_ICMP_ERROR_HEAD.\r
+  //\r
+  IcmpErrHdr = (IP6_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE);\r
+  ASSERT (IcmpErrHdr != NULL);\r
+\r
+  //\r
+  // Set the required fields for the icmp port unreachable message.\r
+  //\r
+  IcmpErrHdr->Head.Type     = ICMP_V6_DEST_UNREACHABLE;\r
+  IcmpErrHdr->Head.Code     = ICMP_V6_PORT_UNREACHABLE;\r
+  IcmpErrHdr->Head.Checksum = 0;\r
+  IcmpErrHdr->Fourth        = 0;\r
+\r
+  //\r
+  // Copy as much of invoking Packet as possible without the ICMPv6 packet\r
+  // exceeding the minimum Ipv6 MTU. The length of IP6_ICMP_ERROR_HEAD contains\r
+  // the length of EFI_IP6_HEADER, so when using the length of IP6_ICMP_ERROR_HEAD\r
+  // for pointer movement that fact should be considered.\r
+  //\r
+  Ptr = (VOID *) &IcmpErrHdr->Head;\r
+  Ptr = (UINT8 *) (UINTN) ((UINTN) Ptr + sizeof (IP6_ICMP_ERROR_HEAD) - sizeof (EFI_IP6_HEADER));\r
+  CopyMem (Ptr, NetSession->IpHdr.Ip6Hdr, NetSession->IpHdrLen);\r
+  CopyMem (\r
+    Ptr + NetSession->IpHdrLen,\r
+    Udp6Header,\r
+    Len - NetSession->IpHdrLen - sizeof (IP6_ICMP_ERROR_HEAD) + sizeof (EFI_IP6_HEADER)\r
+    );\r
+\r
+  //\r
+  // Set the checksum as zero, and IP6 driver will calcuate it with pseudo header.\r
+  //\r
+  IcmpErrHdr->Head.Checksum = 0;\r
+\r
+  //\r
+  // Fill the override data.\r
+  //\r
+  Override.Ip6OverrideData.FlowLabel     = 0;\r
+  Override.Ip6OverrideData.HopLimit      = 255;\r
+  Override.Ip6OverrideData.Protocol      = IP6_ICMP;\r
+\r
+  //\r
+  // Send out this icmp packet.\r
+  //\r
+  IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override);\r
+\r
+  NetbufFree (Packet);\r
+\r
+EXIT:\r
+  if (Ip6ModeData != NULL) {\r
+    FreePool (Ip6ModeData);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function handles the received Icmp Error message and de-multiplexes it to the\r
+  instance.\r
+\r
+  @param[in]       Udp6Service        Pointer to the udp service context data.\r
+  @param[in]       IcmpError          The icmp error code.\r
+  @param[in]       NetSession         Pointer to the EFI_NET_SESSION_DATA abstracted\r
+                                      from the received Icmp Error packet.\r
+  @param[in, out]  Packet             Pointer to the Icmp Error packet.\r
+\r
+**/\r
+VOID\r
+Udp6IcmpHandler (\r
+  IN UDP6_SERVICE_DATA     *Udp6Service,\r
+  IN UINT8                 IcmpError,\r
+  IN EFI_NET_SESSION_DATA  *NetSession,\r
+  IN OUT NET_BUF           *Packet\r
+  )\r
+{\r
+  EFI_UDP_HEADER         *Udp6Header;\r
+  EFI_UDP6_SESSION_DATA  Udp6Session;\r
+  LIST_ENTRY             *Entry;\r
+  UDP6_INSTANCE_DATA     *Instance;\r
+\r
+  Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+  ASSERT (Udp6Header != NULL);\r
+\r
+  IP6_COPY_ADDRESS (&Udp6Session.SourceAddress, &NetSession->Source);\r
+  IP6_COPY_ADDRESS (&Udp6Session.DestinationAddress, &NetSession->Dest);\r
+\r
+  Udp6Session.SourcePort      = NTOHS (Udp6Header->DstPort);\r
+  Udp6Session.DestinationPort = NTOHS (Udp6Header->SrcPort);\r
+\r
+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+    //\r
+    // Iterate all the instances.\r
+    //\r
+    Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+\r
+    if (!Instance->Configured) {\r
+      continue;\r
+    }\r
+\r
+    if (Udp6MatchDgram (Instance, &Udp6Session)) {\r
+      //\r
+      // Translate the Icmp Error code according to the udp spec.\r
+      //\r
+      Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_6, NULL, NULL);\r
+\r
+      if (IcmpError > ICMP_ERR_UNREACH_PORT) {\r
+        Instance->IcmpError = EFI_ICMP_ERROR;\r
+      }\r
+\r
+      //\r
+      // Notify the instance with the received Icmp Error.\r
+      //\r
+      Udp6ReportIcmpError (Instance);\r
+\r
+      break;\r
+    }\r
+  }\r
+\r
+  NetbufFree (Packet);\r
+}\r
+\r
+\r
+/**\r
+  This function reports the received ICMP error.\r
+\r
+  @param[in]  Instance          Pointer to the udp instance context data.\r
+\r
+**/\r
+VOID\r
+Udp6ReportIcmpError (\r
+  IN UDP6_INSTANCE_DATA  *Instance\r
+  )\r
+{\r
+  EFI_UDP6_COMPLETION_TOKEN  *Token;\r
+\r
+  if (NetMapIsEmpty (&Instance->RxTokens)) {\r
+    //\r
+    // There are no receive tokens to deliver the ICMP error.\r
+    //\r
+    return;\r
+  }\r
+\r
+  if (EFI_ERROR (Instance->IcmpError)) {\r
+    //\r
+    // Try to get a RxToken from the RxTokens map.\r
+    //\r
+    Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);\r
+\r
+    if (Token != NULL) {\r
+      //\r
+      // Report the error through the Token.\r
+      //\r
+      Token->Status = Instance->IcmpError;\r
+      gBS->SignalEvent (Token->Event);\r
+\r
+      //\r
+      // Clear the IcmpError.\r
+      //\r
+      Instance->IcmpError = EFI_SUCCESS;\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function is a dummy ext-free function for the NET_BUF created for the output\r
+  udp datagram.\r
+\r
+  @param[in]  Context                Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6NetVectorExtFree (\r
+  IN VOID  *Context\r
+  )\r
+{\r
+}\r
+\r
+\r
+/**\r
+  Set the Udp6 variable data.\r
+\r
+  @param[in]  Udp6Service            Udp6 service data.\r
+\r
+  @retval     EFI_OUT_OF_RESOURCES   There are not enough resources to set the\r
+                                     variable.\r
+  @retval     other                  Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6SetVariableData (\r
+  IN UDP6_SERVICE_DATA  *Udp6Service\r
+  )\r
+{\r
+  UINT32                  NumConfiguredInstance;\r
+  LIST_ENTRY              *Entry;\r
+  UINTN                   VariableDataSize;\r
+  EFI_UDP6_VARIABLE_DATA  *Udp6VariableData;\r
+  EFI_UDP6_SERVICE_POINT  *Udp6ServicePoint;\r
+  UDP6_INSTANCE_DATA      *Udp6Instance;\r
+  CHAR16                  *NewMacString;\r
+  EFI_STATUS              Status;\r
+\r
+  NumConfiguredInstance = 0;\r
+\r
+  //\r
+  // Go through the children list to count the configured children.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+    Udp6Instance = NET_LIST_USER_STRUCT_S (\r
+                     Entry,\r
+                     UDP6_INSTANCE_DATA,\r
+                     Link,\r
+                     UDP6_INSTANCE_DATA_SIGNATURE\r
+                     );\r
+\r
+    if (Udp6Instance->Configured) {\r
+      NumConfiguredInstance++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Calculate the size of the Udp6VariableData. As there may be no Udp6 child,\r
+  // we should add extra buffer for the service points only if the number of configured\r
+  // children is more than 1.\r
+  //\r
+  VariableDataSize = sizeof (EFI_UDP6_VARIABLE_DATA);\r
+\r
+  if (NumConfiguredInstance > 1) {\r
+    VariableDataSize += sizeof (EFI_UDP6_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
+  }\r
+\r
+  Udp6VariableData = AllocateZeroPool (VariableDataSize);\r
+  if (Udp6VariableData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Udp6VariableData->DriverHandle = Udp6Service->ImageHandle;\r
+  Udp6VariableData->ServiceCount = NumConfiguredInstance;\r
+\r
+  Udp6ServicePoint = &Udp6VariableData->Services[0];\r
+\r
+  //\r
+  // Go through the children list to fill the configured children's address pairs.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+    Udp6Instance = NET_LIST_USER_STRUCT_S (\r
+                     Entry,\r
+                     UDP6_INSTANCE_DATA,\r
+                     Link,\r
+                     UDP6_INSTANCE_DATA_SIGNATURE\r
+                     );\r
+\r
+    if (Udp6Instance->Configured) {\r
+      Udp6ServicePoint->InstanceHandle = Udp6Instance->ChildHandle;\r
+      Udp6ServicePoint->LocalPort      = Udp6Instance->ConfigData.StationPort;\r
+      Udp6ServicePoint->RemotePort     = Udp6Instance->ConfigData.RemotePort;\r
+\r
+      IP6_COPY_ADDRESS (\r
+        &Udp6ServicePoint->LocalAddress,\r
+        &Udp6Instance->ConfigData.StationAddress\r
+        );\r
+      IP6_COPY_ADDRESS (\r
+        &Udp6ServicePoint->RemoteAddress,\r
+        &Udp6Instance->ConfigData.RemoteAddress\r
+        );\r
+      Udp6ServicePoint++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Get the MAC string.\r
+  //\r
+  Status = NetLibGetMacString (\r
+             Udp6Service->ControllerHandle,\r
+             Udp6Service->ImageHandle,\r
+             &NewMacString\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  if (Udp6Service->MacString != NULL) {\r
+    //\r
+    // The variable is set already, we're going to update it.\r
+    //\r
+    if (StrCmp (Udp6Service->MacString, NewMacString) != 0) {\r
+      //\r
+      // The MAC address is changed, delete the previous variable first.\r
+      //\r
+      gRT->SetVariable (\r
+             Udp6Service->MacString,\r
+             &gEfiUdp6ServiceBindingProtocolGuid,\r
+             EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+             0,\r
+             NULL\r
+             );\r
+    }\r
+\r
+    FreePool (Udp6Service->MacString);\r
+  }\r
+\r
+  Udp6Service->MacString = NewMacString;\r
+\r
+  Status = gRT->SetVariable (\r
+                  Udp6Service->MacString,\r
+                  &gEfiUdp6ServiceBindingProtocolGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                  VariableDataSize,\r
+                  (VOID *) Udp6VariableData\r
+                  );\r
+\r
+EXIT:\r
+\r
+  FreePool (Udp6VariableData);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Clear the variable and free the resource.\r
+\r
+  @param[in, out]  Udp6Service            Udp6 service data.\r
+\r
+**/\r
+VOID\r
+Udp6ClearVariableData (\r
+  IN OUT UDP6_SERVICE_DATA  *Udp6Service\r
+  )\r
+{\r
+  ASSERT (Udp6Service->MacString != NULL);\r
+\r
+  gRT->SetVariable (\r
+         Udp6Service->MacString,\r
+         &gEfiUdp6ServiceBindingProtocolGuid,\r
+         EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+         0,\r
+         NULL\r
+         );\r
+\r
+  FreePool (Udp6Service->MacString);\r
+  Udp6Service->MacString = NULL;\r
+}\r
+\r
+\r
+/**\r
+  Find the key in the netmap.\r
+\r
+  @param[in]  Map                    The netmap to search within.\r
+  @param[in]  Key                    The key to search.\r
+\r
+  @return The point to the item contains the Key, or NULL, if Key isn't in the map.\r
+\r
+**/\r
+NET_MAP_ITEM *\r
+Udp6MapMultiCastAddr (\r
+  IN  NET_MAP               *Map,\r
+  IN  VOID                  *Key\r
+  )\r
+{\r
+  LIST_ENTRY        *Entry;\r
+  NET_MAP_ITEM      *Item;\r
+  EFI_IPv6_ADDRESS  *Addr;\r
+\r
+  ASSERT (Map != NULL);\r
+  NET_LIST_FOR_EACH (Entry, &Map->Used) {\r
+    Item  = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
+    Addr  = (EFI_IPv6_ADDRESS *) Item->Key;\r
+    if (EFI_IP6_EQUAL (Addr, Key)) {\r
+      return Item;\r
+    }\r
+  }\r
+  return NULL;\r
+}\r
+\r
diff --git a/NetworkPkg/Udp6Dxe/Udp6Impl.h b/NetworkPkg/Udp6Dxe/Udp6Impl.h
new file mode 100644 (file)
index 0000000..108e30b
--- /dev/null
@@ -0,0 +1,673 @@
+/** @file\r
+  Udp6 driver's whole implementation and internal data structures.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef _UDP6_IMPL_H_\r
+#define _UDP6_IMPL_H_\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Udp6.h>\r
+\r
+#include <Library/IpIoLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DpcLib.h>\r
+\r
+#include "Udp6Driver.h"\r
+\r
+extern EFI_COMPONENT_NAME2_PROTOCOL   gUdp6ComponentName2;\r
+extern EFI_COMPONENT_NAME_PROTOCOL    gUdp6ComponentName;\r
+extern EFI_SERVICE_BINDING_PROTOCOL   mUdp6ServiceBinding;\r
+extern EFI_UDP6_PROTOCOL              mUdp6Protocol;\r
+extern UINT16                         mUdp6RandomPort;\r
+\r
+//\r
+// Define time out 50 milliseconds\r
+//\r
+#define UDP6_TIMEOUT_INTERVAL (50 * TICKS_PER_MS)\r
+#define UDP6_HEADER_SIZE      sizeof (EFI_UDP_HEADER)\r
+#define UDP6_MAX_DATA_SIZE    65507\r
+#define UDP6_PORT_KNOWN       1024\r
+\r
+#define UDP6_SERVICE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', '6')\r
+#define UDP6_INSTANCE_DATA_SIGNATURE  SIGNATURE_32 ('U', 'd', 'p', 'S')\r
+\r
+#define UDP6_SERVICE_DATA_FROM_THIS(a) \\r
+  CR ( \\r
+  (a), \\r
+  UDP6_SERVICE_DATA, \\r
+  ServiceBinding, \\r
+  UDP6_SERVICE_DATA_SIGNATURE \\r
+  )\r
+\r
+#define UDP6_INSTANCE_DATA_FROM_THIS(a) \\r
+  CR ( \\r
+  (a), \\r
+  UDP6_INSTANCE_DATA, \\r
+  Udp6Proto, \\r
+  UDP6_INSTANCE_DATA_SIGNATURE \\r
+  )\r
+//\r
+// Udp6 service contest data\r
+//\r
+typedef struct _UDP6_SERVICE_DATA {\r
+  UINT32                        Signature;\r
+  EFI_SERVICE_BINDING_PROTOCOL  ServiceBinding;\r
+  EFI_HANDLE                    ImageHandle;\r
+  EFI_HANDLE                    ControllerHandle;\r
+  LIST_ENTRY                    ChildrenList;\r
+  UINTN                         ChildrenNumber;\r
+  IP_IO                         *IpIo;\r
+  EFI_EVENT                     TimeoutEvent;\r
+  CHAR16                        *MacString;\r
+} UDP6_SERVICE_DATA;\r
+\r
+typedef struct _UDP6_INSTANCE_DATA {\r
+  UINT32                Signature;\r
+  LIST_ENTRY            Link;\r
+  UDP6_SERVICE_DATA     *Udp6Service;\r
+  EFI_UDP6_PROTOCOL     Udp6Proto;\r
+  EFI_UDP6_CONFIG_DATA  ConfigData;\r
+  EFI_HANDLE            ChildHandle;\r
+  BOOLEAN               Configured;\r
+  BOOLEAN               IsNoMapping;\r
+  NET_MAP               TxTokens;\r
+  NET_MAP               RxTokens;\r
+  NET_MAP               McastIps;\r
+  LIST_ENTRY            RcvdDgramQue;\r
+  LIST_ENTRY            DeliveredDgramQue;\r
+  UINT16                HeadSum;\r
+  EFI_STATUS            IcmpError;\r
+  IP_IO_IP_INFO         *IpInfo;\r
+  BOOLEAN               Destroyed;\r
+} UDP6_INSTANCE_DATA;\r
+\r
+typedef struct _UDP6_RXDATA_WRAP {\r
+  LIST_ENTRY             Link;\r
+  NET_BUF                *Packet;\r
+  UINT32                 TimeoutTick;\r
+  EFI_UDP6_RECEIVE_DATA  RxData;\r
+} UDP6_RXDATA_WRAP;\r
+\r
+/**\r
+  Clean the Udp service context data.\r
+\r
+  @param[in, out]  Udp6Service      Pointer to the UDP6_SERVICE_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6CleanService (\r
+  IN OUT UDP6_SERVICE_DATA  *Udp6Service\r
+  );\r
+\r
+/**\r
+  Create the Udp service context data.\r
+\r
+  @param[in]  Udp6Service            Pointer to the UDP6_SERVICE_DATA.\r
+  @param[in]  ImageHandle            The image handle of this udp6 driver.\r
+  @param[in]  ControllerHandle       The controller handle this udp6 driver binds on.\r
+\r
+  @retval EFI_SUCCESS            The udp6 service context data was created and\r
+                                 initialized.\r
+  @retval EFI_OUT_OF_RESOURCES   Cannot allocate memory.\r
+  @retval Others                 An error condition occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6CreateService (\r
+  IN UDP6_SERVICE_DATA  *Udp6Service,\r
+  IN EFI_HANDLE         ImageHandle,\r
+  IN EFI_HANDLE         ControllerHandle\r
+  );\r
+\r
+/**\r
+  Set the Udp6 variable data.\r
+\r
+  @param[in]  Udp6Service            Udp6 service data.\r
+\r
+  @retval     EFI_OUT_OF_RESOURCES   There are not enough resources to set the\r
+                                     variable.\r
+  @retval     other                  Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6SetVariableData (\r
+  IN UDP6_SERVICE_DATA  *Udp6Service\r
+  );\r
+\r
+/**\r
+  This function cleans the udp instance.\r
+\r
+  @param[in, out]  Instance       Pointer to the UDP6_INSTANCE_DATA to clean.\r
+\r
+**/\r
+VOID\r
+Udp6CleanInstance (\r
+  IN OUT UDP6_INSTANCE_DATA  *Instance\r
+  );\r
+\r
+/**\r
+  Clear the variable and free the resource.\r
+\r
+  @param[in, out]  Udp6Service            Udp6 service data.\r
+\r
+**/\r
+VOID\r
+Udp6ClearVariableData (\r
+  IN OUT UDP6_SERVICE_DATA  *Udp6Service\r
+  );\r
+\r
+/**\r
+  This function intializes the new created udp instance.\r
+\r
+  @param[in]      Udp6Service      Pointer to the UDP6_SERVICE_DATA.\r
+  @param[in, out]  Instance         Pointer to the un-initialized UDP6_INSTANCE_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6InitInstance (\r
+  IN UDP6_SERVICE_DATA       *Udp6Service,\r
+  IN OUT UDP6_INSTANCE_DATA  *Instance\r
+  );\r
+\r
+/**\r
+  This function reports the received ICMP error.\r
+\r
+  @param[in]  Instance          Pointer to the udp instance context data.\r
+\r
+**/\r
+VOID\r
+Udp6ReportIcmpError (\r
+  IN UDP6_INSTANCE_DATA  *Instance\r
+  );\r
+\r
+/**\r
+  This function copies the current operational settings of this EFI UDPv6 Protocol\r
+  instance into user-supplied buffers. This function is used optionally to retrieve\r
+  the operational mode data of underlying networks or drivers.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[out] Udp6ConfigData     The buffer in which the current UDP configuration\r
+                                 data is returned. This parameter is optional and\r
+                                 may be NULL.\r
+  @param[out] Ip6ModeData        The buffer in which the current EFI IPv6 Protocol\r
+                                 mode data is returned. This parameter is optional\r
+                                 and may be NULL.\r
+  @param[out] MnpConfigData      The buffer in which the current managed network\r
+                                 configuration data is returned. This parameter\r
+                                 is optional and may be NULL.\r
+  @param[out] SnpModeData        The buffer in which the simple network mode data\r
+                                 is returned. This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The mode data was read.\r
+  @retval EFI_NOT_STARTED        When Udp6ConfigData is queried, no configuration\r
+                                 data is  available because this instance has not\r
+                                 been started.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6GetModeData (\r
+  IN  EFI_UDP6_PROTOCOL                *This,\r
+  OUT EFI_UDP6_CONFIG_DATA             *Udp6ConfigData OPTIONAL,\r
+  OUT EFI_IP6_MODE_DATA                *Ip6ModeData    OPTIONAL,\r
+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA  *MnpConfigData  OPTIONAL,\r
+  OUT EFI_SIMPLE_NETWORK_MODE          *SnpModeData    OPTIONAL\r
+  );\r
+\r
+/**\r
+  This function is used to do the following:\r
+  Initialize and start this instance of the EFI UDPv6 Protocol.\r
+  Change the filtering rules and operational parameters.\r
+  Reset this instance of the EFI UDPv6 Protocol.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  UdpConfigData      Pointer to the buffer to set the configuration\r
+                                 data. This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The configuration settings were set, changed, or\r
+                                 reset successfully.\r
+  @retval EFI_NO_MAPPING         When the UdpConifgData.UseAnyStationAddress is set\r
+                                 to true  and there is no address available for IP6\r
+                                 driver to binding  source address to this\r
+                                 instance.\r
+  @retval EFI_INVALID_PARAMETER  One or more following conditions are TRUE:\r
+                                 This is NULL.\r
+                                 UdpConfigData.StationAddress is not a valid\r
+                                 unicast IPv6 address.\r
+                                 UdpConfigData.RemoteAddress is not a valid unicast\r
+                                 IPv6  address, if it is not zero.\r
+  @retval EFI_ALREADY_STARTED    The EFI UDPv6 Protocol instance is already\r
+                                 started/configured and must be stopped/reset\r
+                                 before it can be reconfigured. Only TrafficClass,\r
+                                 HopLimit, ReceiveTimeout, and TransmitTimeout can\r
+                                 be reconfigured without stopping the current\r
+                                 instance of the EFI UDPv6 Protocol.\r
+  @retval EFI_ACCESS_DENIED      UdpConfigData.AllowDuplicatePort is FALSE, and\r
+                                 UdpConfigData.StationPort is already used by another\r
+                                 instance.\r
+  @retval EFI_OUT_OF_RESOURCES   The EFI UDPv6 Protocol driver cannot allocate\r
+                                 memory for this EFI UDPv6 Protocol instance.\r
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred, and\r
+                                 this instance was not opened.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Configure (\r
+  IN EFI_UDP6_PROTOCOL     *This,\r
+  IN EFI_UDP6_CONFIG_DATA  *UdpConfigData OPTIONAL\r
+  );\r
+\r
+/**\r
+  This function places a sending request to this instance of the EFI UDPv6 Protocol,\r
+  alongside the transmit data that was filled by the user.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to the completion token that will be\r
+                                 placed into the transmit queue.\r
+\r
+  @retval EFI_SUCCESS            The data has been queued for transmission.\r
+  @retval EFI_NOT_STARTED        This EFI UDPv6 Protocol instance has not been\r
+                                 started.\r
+  @retval EFI_NO_MAPPING         The under-layer IPv6 driver was responsible for\r
+                                 choosing a source address for this instance, but\r
+                                 no  source address was available for use.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 This is NULL. Token is NULL. Token.Event is NULL.\r
+                                 Token.Packet.TxData is NULL.\r
+                                 Token.Packet.TxData.FragmentCount is zero.\r
+                                 Token.Packet.TxData.DataLength is not equal to the\r
+                                 sum of fragment lengths.\r
+                                 One or more of the\r
+                                 Token.Packet.TxData.FragmentTable[]\r
+                                 .FragmentLength fields is zero.\r
+                                 One or more of the\r
+                                 Token.Packet.TxData.FragmentTable[]\r
+                                 .FragmentBuffer fields is NULL.\r
+                                 One or more of the\r
+                                 Token.Packet.TxData.UdpSessionData.\r
+                                 DestinationAddres are not valid unicast IPv6\r
+                                 addresses, if the  UdpSessionData is not NULL.\r
+                                 Token.Packet.TxData.UdpSessionData.\r
+                                 DestinationAddres is NULL\r
+                                 Token.Packet.TxData.UdpSessionData.\r
+                                 DestinatioPort is zero.\r
+                                 Token.Packet.TxData.UdpSessionData is\r
+                                 NULL and this  instance's\r
+                                 UdpConfigData.RemoteAddress is unspecified.\r
+  @retval EFI_ACCESS_DENIED      The transmit completion token with the same\r
+                                 Token.Event is already in the transmit queue.\r
+  @retval EFI_NOT_READY          The completion token could not be queued because\r
+                                 the transmit queue is full.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not queue the transmit data.\r
+  @retval EFI_NOT_FOUND          There is no route to the destination network or\r
+                                 address.\r
+  @retval EFI_BAD_BUFFER_SIZE    The data length is greater than the maximum UDP\r
+                                 packet size. Or the length of the IP header + UDP\r
+                                 header + data length is greater than MTU if\r
+                                 DoNotFragment is TRUE.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Transmit (\r
+  IN EFI_UDP6_PROTOCOL          *This,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token\r
+  );\r
+\r
+/**\r
+  This function places a completion token into the receive packet queue. This function\r
+  is always asynchronous.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to a token that is associated with the\r
+                                 receive data descriptor.\r
+\r
+  @retval EFI_SUCCESS            The receive completion token is cached.\r
+  @retval EFI_NOT_STARTED        This EFI UDPv6 Protocol instance has not been\r
+                                 started.\r
+  @retval EFI_NO_MAPPING         When using a default address, configuration (DHCP,\r
+                                 BOOTP, RARP, etc.) is not finished yet.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 This is NULL.\r
+                                 Token is NULL.\r
+                                 Token.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued\r
+                                 due to a lack of system resources (usually\r
+                                 memory).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+                                 The EFI UDPv6 Protocol instance has been reset to\r
+                                 startup defaults.\r
+  @retval EFI_ACCESS_DENIED      A receive completion token with the same\r
+                                 Token.Event is already in the receive queue.\r
+  @retval EFI_NOT_READY          The receive request could not be queued because\r
+                                 the receive  queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Receive (\r
+  IN EFI_UDP6_PROTOCOL          *This,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token\r
+  );\r
+\r
+/**\r
+  This function is used to abort a pending transmit or receive request.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to a token that has been issued by\r
+                                 EFI_UDP6_PROTOCOL.Transmit() or\r
+                                 EFI_UDP6_PROTOCOL.Receive(). This parameter is\r
+                                 optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The asynchronous I/O request is aborted and\r
+                                 Token.Event is  signaled. When Token is NULL, all\r
+                                 pending requests are aborted and their events are\r
+                                 signaled.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_NOT_STARTED        This instance has not been started.\r
+  @retval EFI_NO_MAPPING         When using the default address, configuration\r
+                                 (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+  @retval EFI_NOT_FOUND          When Token is not NULL, the asynchronous I/O\r
+                                 request is not found in the transmit or receive\r
+                                 queue. It either completed or was not issued by\r
+                                 Transmit() or Receive().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Cancel (\r
+  IN EFI_UDP6_PROTOCOL          *This,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL\r
+  );\r
+\r
+/**\r
+  This function can be used by network drivers and applications to increase the rate that\r
+  data packets are moved between the communications device and the transmit/receive queues.\r
+\r
+  @param[in] This                Pointer to the EFI_UDP6_PROTOCOL instance.\r
+\r
+  @retval EFI_SUCCESS            Incoming or outgoing data was processed.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+  @retval EFI_TIMEOUT            Data was dropped out of the transmit and/or\r
+                                 receive queue.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Poll (\r
+  IN EFI_UDP6_PROTOCOL  *This\r
+  );\r
+\r
+/**\r
+  This function is used to enable and disable the multicast group filtering.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  JoinFlag           Set to TRUE to join a multicast group. Set to\r
+                                 FALSE to leave one or all multicast groups.\r
+  @param[in]  MulticastAddress   Pointer to multicast group address to join or\r
+                                 leave. This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_NOT_STARTED        The EFI UDPv6 Protocol instance has not been\r
+                                 started.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate resources to join the group.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 This is NULL. JoinFlag is TRUE and\r
+                                 MulticastAddress is NULL. JoinFlag is TRUE and\r
+                                 *MulticastAddress is not a valid  multicast\r
+                                 address.\r
+  @retval EFI_ALREADY_STARTED    The group address is already in the group table\r
+                                 (when JoinFlag is TRUE).\r
+  @retval EFI_NOT_FOUND          The group address is not in the group table (when\r
+                                 JoinFlag is FALSE).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Groups (\r
+  IN EFI_UDP6_PROTOCOL  *This,\r
+  IN BOOLEAN            JoinFlag,\r
+  IN EFI_IPv6_ADDRESS   *MulticastAddress OPTIONAL\r
+  );\r
+\r
+/**\r
+  This function tries to bind the udp instance according to the configured port\r
+  allocation stragety.\r
+\r
+  @param[in]  InstanceList       Pointer to the head of the list linking the udp\r
+                                 instances.\r
+  @param[in]  ConfigData         Pointer to the ConfigData of the instance to be\r
+                                 bound.\r
+\r
+  @retval EFI_SUCCESS            The bound operation completed successfully.\r
+  @retval EFI_ACCESS_DENIED      The <Address, Port> specified by the ConfigData is\r
+                                 already used by another instance.\r
+  @retval EFI_OUT_OF_RESOURCES   No available port resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6Bind (\r
+  IN LIST_ENTRY            *InstanceList,\r
+  IN EFI_UDP6_CONFIG_DATA  *ConfigData\r
+  );\r
+\r
+/**\r
+  This function builds the Ip6 configdata from the Udp6ConfigData.\r
+\r
+  @param[in]       Udp6ConfigData         Pointer to the EFI_UDP6_CONFIG_DATA.\r
+  @param[in, out]  Ip6ConfigData          Pointer to the EFI_IP6_CONFIG_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6BuildIp6ConfigData (\r
+  IN EFI_UDP6_CONFIG_DATA      *Udp6ConfigData,\r
+  IN OUT EFI_IP6_CONFIG_DATA   *Ip6ConfigData\r
+  );\r
+\r
+/**\r
+  This function checks whether the specified Token duplicates with the one in the Map.\r
+\r
+  @param[in]  Map                Pointer to the NET_MAP.\r
+  @param[in]  Item               Pointer to the NET_MAP_ITEM contain the pointer to\r
+                                 the Token.\r
+  @param[in]  Context            Pointer to the Token to be checked.\r
+\r
+  @retval EFI_SUCCESS            The Token specified by Context differs from the\r
+                                 one in the Item.\r
+  @retval EFI_ACCESS_DENIED      The Token duplicates with the one in the Item.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6TokenExist (\r
+  IN NET_MAP       *Map,\r
+  IN NET_MAP_ITEM  *Item,\r
+  IN VOID          *Context\r
+  );\r
+\r
+/**\r
+  This function removes the specified Token from the TokenMap.\r
+\r
+  @param[in]  TokenMap           Pointer to the NET_MAP containing the tokens.\r
+  @param[in]  Token              Pointer to the Token to be removed.\r
+\r
+  @retval EFI_SUCCESS            The specified Token is removed from the TokenMap.\r
+  @retval EFI_NOT_FOUND          The specified Token is not found in the TokenMap.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6RemoveToken (\r
+  IN NET_MAP                    *TokenMap,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token\r
+  );\r
+\r
+/**\r
+  This function is used to check whether the NewConfigData has any un-reconfigurable\r
+  parameters changed compared to the OldConfigData.\r
+\r
+  @param[in]  OldConfigData    Pointer to the current ConfigData the udp instance\r
+                               uses.\r
+  @param[in]  NewConfigData    Pointer to the new ConfigData.\r
+\r
+  @retval TRUE     The instance is reconfigurable according to NewConfigData.\r
+  @retval FALSE   The instance is not reconfigurable according to NewConfigData.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6IsReconfigurable (\r
+  IN EFI_UDP6_CONFIG_DATA  *OldConfigData,\r
+  IN EFI_UDP6_CONFIG_DATA  *NewConfigData\r
+  );\r
+\r
+/**\r
+  This function removes the multicast group specified by Arg from the Map.\r
+\r
+  @param[in]  Map                Pointer to the NET_MAP.\r
+  @param[in]  Item               Pointer to the NET_MAP_ITEM.\r
+  @param[in]  Arg                Pointer to the Arg. It is the pointer to a\r
+                                 multicast IPv6 Address. This parameter is\r
+                                 optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The multicast address is removed.\r
+  @retval EFI_ABORTED            The specified multicast address is removed, and the\r
+                                 Arg is not NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6LeaveGroup (\r
+  IN NET_MAP       *Map,\r
+  IN NET_MAP_ITEM  *Item,\r
+  IN VOID          *Arg OPTIONAL\r
+  );\r
+\r
+/**\r
+  This function validates the TxToken, it returns the error code according to the spec.\r
+\r
+  @param[in]  Instance           Pointer to the udp instance context data.\r
+  @param[in]  TxToken            Pointer to the token to be checked.\r
+\r
+  @retval EFI_SUCCESS            The TxToken is valid.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 Token.Event is NULL.\r
+                                 Token.Packet.TxData is NULL.\r
+                                 Token.Packet.TxData.FragmentCount is zero.\r
+                                 Token.Packet.TxData.DataLength is not equal to the\r
+                                 sum of fragment lengths.\r
+                                 One or more of the\r
+                                 Token.Packet.TxData.FragmentTable[].FragmentLength\r
+                                 fields is zero.\r
+                                 One or more of the\r
+                                 Token.Packet.TxData.FragmentTable[].FragmentBuffer\r
+                                 fields is NULL.\r
+                                 UdpSessionData.DestinationAddress are not valid\r
+                                 unicast IPv6 addresses if the UdpSessionData is\r
+                                 not NULL.\r
+                                 UdpSessionData.DestinationPort and\r
+                                 ConfigData.RemotePort are all zero if the\r
+                                 UdpSessionData is not NULL.\r
+  @retval EFI_BAD_BUFFER_SIZE    The data length is greater than the maximum UDP\r
+                                 packet size.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6ValidateTxToken (\r
+  IN UDP6_INSTANCE_DATA         *Instance,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *TxToken\r
+  );\r
+\r
+/**\r
+  This function is a dummy ext-free function for the NET_BUF created for the output\r
+  udp datagram.\r
+\r
+  @param[in]  Context               Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6NetVectorExtFree (\r
+  IN VOID  *Context\r
+  );\r
+\r
+/**\r
+  This function calculates the checksum for the Packet, utilizing the pre-calculated\r
+  pseudo header to reduce overhead.\r
+\r
+  @param[in]  Packet           Pointer to the NET_BUF contains the udp datagram.\r
+  @param[in]  HeadSum          Checksum of the pseudo header execpt the length\r
+                               field.\r
+\r
+  @return The 16-bit checksum of this udp datagram.\r
+\r
+**/\r
+UINT16\r
+Udp6Checksum (\r
+  IN NET_BUF *Packet,\r
+  IN UINT16  HeadSum\r
+  );\r
+\r
+/**\r
+  This function delivers the received datagrams to the specified instance.\r
+\r
+  @param[in]  Instance               Pointer to the instance context data.\r
+\r
+**/\r
+VOID\r
+Udp6InstanceDeliverDgram (\r
+  IN UDP6_INSTANCE_DATA  *Instance\r
+  );\r
+\r
+/**\r
+  Cancel Udp6 tokens from the Udp6 instance.\r
+\r
+  @param[in]  Instance           Pointer to the udp instance context data.\r
+  @param[in]  Token              Pointer to the token to be canceled. If NULL, all\r
+                                 tokens in this instance will be cancelled.\r
+                                 This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The Token is cancelled.\r
+  @retval EFI_NOT_FOUND          The Token is not found.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6InstanceCancelToken (\r
+  IN UDP6_INSTANCE_DATA         *Instance,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL\r
+  );\r
+\r
+/**\r
+  This function removes all the Wrap datas in the RcvdDgramQue.\r
+\r
+  @param[in]  Instance    Pointer to the Udp6 Instance.\r
+\r
+**/\r
+VOID\r
+Udp6FlushRcvdDgram (\r
+  IN UDP6_INSTANCE_DATA  *Instance\r
+  );\r
+\r
+#endif\r
+\r
diff --git a/NetworkPkg/Udp6Dxe/Udp6Main.c b/NetworkPkg/Udp6Dxe/Udp6Main.c
new file mode 100644 (file)
index 0000000..0cad596
--- /dev/null
@@ -0,0 +1,855 @@
+/** @file\r
+  Contains all EFI_UDP6_PROTOCOL interfaces.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "Udp6Impl.h"\r
+\r
+EFI_UDP6_PROTOCOL  mUdp6Protocol = {\r
+  Udp6GetModeData,\r
+  Udp6Configure,\r
+  Udp6Groups,\r
+  Udp6Transmit,\r
+  Udp6Receive,\r
+  Udp6Cancel,\r
+  Udp6Poll\r
+};\r
+\r
+\r
+/**\r
+  This function copies the current operational settings of this EFI UDPv6 Protocol\r
+  instance into user-supplied buffers. This function is used optionally to retrieve\r
+  the operational mode data of underlying networks or drivers.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[out] Udp6ConfigData     The buffer in which the current UDP configuration\r
+                                 data is returned. This parameter is optional and\r
+                                 may be NULL.\r
+  @param[out] Ip6ModeData        The buffer in which the current EFI IPv6 Protocol\r
+                                 mode data is returned. This parameter is optional\r
+                                 and may be NULL.\r
+  @param[out] MnpConfigData      The buffer in which the current managed network\r
+                                 configuration data is returned. This parameter is\r
+                                 optional and may be NULL.\r
+  @param[out] SnpModeData        The buffer in which the simple network mode data\r
+                                 is returned. This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The mode data was read.\r
+  @retval EFI_NOT_STARTED        When Udp6ConfigData is queried, no configuration\r
+                                 data is  available because this instance has not\r
+                                 been started.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6GetModeData (\r
+  IN  EFI_UDP6_PROTOCOL                *This,\r
+  OUT EFI_UDP6_CONFIG_DATA             *Udp6ConfigData OPTIONAL,\r
+  OUT EFI_IP6_MODE_DATA                *Ip6ModeData    OPTIONAL,\r
+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA  *MnpConfigData  OPTIONAL,\r
+  OUT EFI_SIMPLE_NETWORK_MODE          *SnpModeData    OPTIONAL\r
+  )\r
+{\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+  EFI_IP6_PROTOCOL    *Ip;\r
+  EFI_TPL             OldTpl;\r
+  EFI_STATUS          Status;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured && (Udp6ConfigData != NULL)) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (Udp6ConfigData != NULL) {\r
+    //\r
+    // Set the Udp6ConfigData.\r
+    //\r
+    CopyMem (Udp6ConfigData, &Instance->ConfigData, sizeof (EFI_UDP6_CONFIG_DATA));\r
+  }\r
+\r
+  Ip = Instance->IpInfo->Ip.Ip6;\r
+\r
+  //\r
+  // Get the underlying Ip6ModeData, MnpConfigData and SnpModeData.\r
+  //\r
+  Status = Ip->GetModeData (Ip, Ip6ModeData, MnpConfigData, SnpModeData);\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is used to do the following:\r
+  Initialize and start this instance of the EFI UDPv6 Protocol.\r
+  Change the filtering rules and operational parameters.\r
+  Reset this instance of the EFI UDPv6 Protocol.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  UdpConfigData      Pointer to the buffer to set the configuration\r
+                                 data. This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The configuration settings were set, changed, or\r
+                                 reset successfully.\r
+  @retval EFI_NO_MAPPING         When the UdpConifgData.UseAnyStationAddress is set\r
+                                 to true and there is no address available for the IP6\r
+                                 driver to bind a source address to this instance.\r
+  @retval EFI_INVALID_PARAMETER  One or more following conditions are TRUE:\r
+                                 This is NULL.\r
+                                 UdpConfigData.StationAddress is not a valid\r
+                                 unicast IPv6 address.\r
+                                 UdpConfigData.RemoteAddress is not a valid unicast\r
+                                 IPv6  address if it is not zero.\r
+  @retval EFI_ALREADY_STARTED    The EFI UDPv6 Protocol instance is already\r
+                                 started/configured and must be stopped/reset\r
+                                 before it can be reconfigured. Only TrafficClass,\r
+                                 HopLimit, ReceiveTimeout, and  TransmitTimeout can\r
+                                 be reconfigured without stopping the  current\r
+                                 instance of the EFI UDPv6 Protocol.\r
+  @retval EFI_ACCESS_DENIED      UdpConfigData.AllowDuplicatePort is FALSE and\r
+                                 UdpConfigData.StationPort is already used by another\r
+                                 instance.\r
+  @retval EFI_OUT_OF_RESOURCES   The EFI UDPv6 Protocol driver cannot allocate\r
+                                 memory for this EFI UDPv6 Protocol instance.\r
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred, and\r
+                                 this instance was not opened.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Configure (\r
+  IN EFI_UDP6_PROTOCOL     *This,\r
+  IN EFI_UDP6_CONFIG_DATA  *UdpConfigData OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS           Status;\r
+  UDP6_INSTANCE_DATA   *Instance;\r
+  UDP6_SERVICE_DATA    *Udp6Service;\r
+  EFI_TPL              OldTpl;\r
+  EFI_IPv6_ADDRESS     StationAddress;\r
+  EFI_IPv6_ADDRESS     RemoteAddress;\r
+  EFI_IP6_CONFIG_DATA  Ip6ConfigData;\r
+  EFI_IPv6_ADDRESS     LocalAddr;\r
+  EFI_IPv6_ADDRESS     RemoteAddr;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured && (UdpConfigData == NULL)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Udp6Service = Instance->Udp6Service;\r
+  Status      = EFI_SUCCESS;\r
+  ASSERT (Udp6Service != NULL);\r
+\r
+  OldTpl      = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (UdpConfigData != NULL) {\r
+\r
+    IP6_COPY_ADDRESS (&StationAddress, &UdpConfigData->StationAddress);\r
+    IP6_COPY_ADDRESS (&RemoteAddress, &UdpConfigData->RemoteAddress);\r
+\r
+    if ((!NetIp6IsUnspecifiedAddr (&StationAddress) && !NetIp6IsValidUnicast (&StationAddress)) ||\r
+        (!NetIp6IsUnspecifiedAddr (&RemoteAddress) && !NetIp6IsValidUnicast (&RemoteAddress))\r
+        ){\r
+      //\r
+      // If not use default address, and StationAddress is not a valid unicast\r
+      // if it is not IPv6 address or RemoteAddress is not a valid unicast IPv6\r
+      // address if it is not 0.\r
+      //\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    if (Instance->Configured) {\r
+      //\r
+      // The instance is already configured, try to do the re-configuration.\r
+      //\r
+      if (!Udp6IsReconfigurable (&Instance->ConfigData, UdpConfigData)) {\r
+        //\r
+        // If the new configuration data wants to change some unreconfigurable\r
+        // settings, return EFI_ALREADY_STARTED.\r
+        //\r
+        Status = EFI_ALREADY_STARTED;\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      //\r
+      // Save the reconfigurable parameters.\r
+      //\r
+      Instance->ConfigData.TrafficClass    = UdpConfigData->TrafficClass;\r
+      Instance->ConfigData.HopLimit        = UdpConfigData->HopLimit;\r
+      Instance->ConfigData.ReceiveTimeout  = UdpConfigData->ReceiveTimeout;\r
+      Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;\r
+    } else {\r
+      //\r
+      // Construct the Ip configuration data from the UdpConfigData.\r
+      //\r
+      Udp6BuildIp6ConfigData (UdpConfigData, &Ip6ConfigData);\r
+\r
+      //\r
+      // Configure the Ip instance wrapped in the IpInfo.\r
+      //\r
+      Status = IpIoConfigIp (Instance->IpInfo, &Ip6ConfigData);\r
+      if (EFI_ERROR (Status)) {\r
+        if (Status == EFI_NO_MAPPING) {\r
+          Instance->IsNoMapping = TRUE;\r
+        }\r
+\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      Instance->IsNoMapping = FALSE;\r
+\r
+      //\r
+      // Save the configuration data.\r
+      //\r
+      CopyMem (\r
+        &Instance->ConfigData,\r
+        UdpConfigData,\r
+        sizeof (EFI_UDP6_CONFIG_DATA)\r
+        );\r
+      IP6_COPY_ADDRESS (&Instance->ConfigData.StationAddress, &Ip6ConfigData.StationAddress);\r
+      //\r
+      // Try to allocate the required port resource.\r
+      //\r
+      Status = Udp6Bind (&Udp6Service->ChildrenList, &Instance->ConfigData);\r
+      if (EFI_ERROR (Status)) {\r
+        //\r
+        // Reset the ip instance if bind fails.\r
+        //\r
+        IpIoConfigIp (Instance->IpInfo, NULL);\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      //\r
+      // Pre calculate the checksum for the pseudo head, ignore the UDP length first.\r
+      //\r
+      IP6_COPY_ADDRESS (&LocalAddr, &Instance->ConfigData.StationAddress);\r
+      IP6_COPY_ADDRESS (&RemoteAddr, &Instance->ConfigData.RemoteAddress);\r
+\r
+      Instance->HeadSum = NetIp6PseudoHeadChecksum (\r
+                            &LocalAddr,\r
+                            &RemoteAddr,\r
+                            EFI_IP_PROTO_UDP,\r
+                            0\r
+                            );\r
+\r
+      Instance->Configured = TRUE;\r
+    }\r
+  } else {\r
+    //\r
+    // UdpConfigData is NULL, reset the instance.\r
+    //\r
+    Instance->Configured  = FALSE;\r
+    Instance->IsNoMapping = FALSE;\r
+\r
+    //\r
+    // Reset the Ip instance wrapped in the IpInfo.\r
+    //\r
+    IpIoConfigIp (Instance->IpInfo, NULL);\r
+\r
+    //\r
+    // Cancel all the user tokens.\r
+    //\r
+    Status = Instance->Udp6Proto.Cancel (&Instance->Udp6Proto, NULL);\r
+\r
+    //\r
+    // Remove the buffered RxData for this instance.\r
+    //\r
+    Udp6FlushRcvdDgram (Instance);\r
+\r
+    ASSERT (IsListEmpty (&Instance->DeliveredDgramQue));\r
+  }\r
+\r
+  Status = Udp6SetVariableData (Instance->Udp6Service);\r
+\r
+ON_EXIT:\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is used to enable and disable the multicast group filtering.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  JoinFlag           Set to TRUE to join a multicast group. Set to\r
+                                 FALSE to leave one or all multicast groups.\r
+  @param[in]  MulticastAddress   Pointer to multicast group address to join or\r
+                                 leave. This parameter is optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The operation completed successfully.\r
+  @retval EFI_NOT_STARTED        The EFI UDPv6 Protocol instance has not been\r
+                                 started.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not allocate resources to join the group.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 This is NULL.\r
+                                 JoinFlag is TRUE and MulticastAddress is NULL.\r
+                                 JoinFlag is TRUE and *MulticastAddress is not a\r
+                                 valid  multicast address.\r
+  @retval EFI_ALREADY_STARTED    The group address is already in the group table\r
+                                 (when JoinFlag is TRUE).\r
+  @retval EFI_NOT_FOUND          The group address is not in the group table (when\r
+                                 JoinFlag is FALSE).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Groups (\r
+  IN EFI_UDP6_PROTOCOL  *This,\r
+  IN BOOLEAN            JoinFlag,\r
+  IN EFI_IPv6_ADDRESS   *MulticastAddress OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+  EFI_IP6_PROTOCOL    *Ip;\r
+  EFI_TPL             OldTpl;\r
+  EFI_IPv6_ADDRESS    *McastIp;\r
+\r
+  if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  McastIp = NULL;\r
+\r
+  if (JoinFlag) {\r
+    if (!IP6_IS_MULTICAST (MulticastAddress)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    McastIp = AllocateCopyPool (sizeof (EFI_IPv6_ADDRESS), MulticastAddress);\r
+    if (McastIp == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  Ip      = Instance->IpInfo->Ip.Ip6;\r
+\r
+  OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // Invoke the Ip instance the Udp6 instance consumes to do the group operation.\r
+  //\r
+  Status = Ip->Groups (Ip, JoinFlag, MulticastAddress);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Keep a local copy of the configured multicast IPs because IpIo receives\r
+  // datagrams from the 0 station address IP instance and then UDP delivers to\r
+  // the matched instance. This copy of multicast IPs is used to avoid receive\r
+  // the mutlicast datagrams destinated to multicast IPs the other instances configured.\r
+  //\r
+  if (JoinFlag) {\r
+\r
+    Status = NetMapInsertTail (&Instance->McastIps, (VOID *) McastIp, NULL);\r
+  } else {\r
+\r
+    NetMapIterate (&Instance->McastIps, Udp6LeaveGroup, MulticastAddress);\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    if (McastIp != NULL) {\r
+      FreePool (McastIp);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+\r
+/**\r
+  This function places a sending request to this instance of the EFI UDPv6 Protocol,\r
+  alongside the transmit data that was filled by the user.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to the completion token that will be\r
+                                 placed into the transmit queue.\r
+\r
+  @retval EFI_SUCCESS            The data was queued for transmission.\r
+  @retval EFI_NOT_STARTED        This EFI UDPv6 Protocol instance has not been\r
+                                 started.\r
+  @retval EFI_NO_MAPPING         The under-layer IPv6 driver was responsible for\r
+                                 choosing a source address for this instance, but\r
+                                 no  source address was available for use.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:\r
+                                 This is NULL.\r
+                                 Token is NULL. Token.Event is NULL.\r
+                                 Token.Packet.TxData is NULL.\r
+                                 Token.Packet.TxData.FragmentCount is zero.\r
+                                 Token.Packet.TxData.DataLength is not equal to the\r
+                                 sum of fragment lengths.\r
+                                 One or more of the\r
+                                 Token.Packet.TxData.FragmentTable[].FragmentLength\r
+                                 fields is zero.\r
+                                 One or more of the\r
+                                 Token.Packet.TxData.FragmentTable[].FragmentBuffer\r
+                                 fields is NULL. One or more of the\r
+                                 Token.Packet.TxData.UdpSessionData.DestinationAddres\r
+                                 are not valid unicast IPv6\r
+                                 addresses if the  UdpSessionData is not NULL.\r
+                                 Token.Packet.TxData.UdpSessionData.\r
+                                 DestinationAddress is NULL\r
+                                 Token.Packet.TxData.UdpSessionData.\r
+                                 DestinatioPort\r
+                                 is zero.\r
+                                 Token.Packet.TxData.UdpSessionData is NULL and this\r
+                                 instance's UdpConfigData.RemoteAddress  is unspecified.\r
+  @retval EFI_ACCESS_DENIED      The transmit completion token with the same\r
+                                 Token.Event is already in the transmit queue.\r
+  @retval EFI_NOT_READY          The completion token could not be queued because\r
+                                 the transmit queue is full.\r
+  @retval EFI_OUT_OF_RESOURCES   Could not queue the transmit data.\r
+  @retval EFI_NOT_FOUND          There is no route to the destination network or\r
+                                 address.\r
+  @retval EFI_BAD_BUFFER_SIZE    The data length is greater than the maximum UDP\r
+                                 packet size. Or, the length of the IP header + UDP\r
+                                 header + data length is greater than MTU if\r
+                                 DoNotFragment is TRUE.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Transmit (\r
+  IN EFI_UDP6_PROTOCOL          *This,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  UDP6_INSTANCE_DATA      *Instance;\r
+  EFI_TPL                 OldTpl;\r
+  NET_BUF                 *Packet;\r
+  EFI_UDP_HEADER          *Udp6Header;\r
+  EFI_UDP6_CONFIG_DATA    *ConfigData;\r
+  EFI_IPv6_ADDRESS        Source;\r
+  EFI_IPv6_ADDRESS        Destination;\r
+  EFI_UDP6_TRANSMIT_DATA  *TxData;\r
+  EFI_UDP6_SESSION_DATA   *UdpSessionData;\r
+  UDP6_SERVICE_DATA       *Udp6Service;\r
+  IP_IO_OVERRIDE          Override;\r
+  UINT16                  HeadSum;\r
+  EFI_IP_ADDRESS          IpDestAddr;\r
+\r
+  if ((This == NULL) || (Token == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // Validate the Token, if the token is invalid return the error code.\r
+  //\r
+  Status = Udp6ValidateTxToken (Instance, Token);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token)) ||\r
+      EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token))\r
+      ){\r
+    //\r
+    // Try to find a duplicate token in the two token maps, if found, return\r
+    // EFI_ACCESS_DENIED.\r
+    //\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  TxData = Token->Packet.TxData;\r
+\r
+  //\r
+  // Create a net buffer to hold the user buffer and the udp header.\r
+  //\r
+  Packet = NetbufFromExt (\r
+             (NET_FRAGMENT *) TxData->FragmentTable,\r
+             TxData->FragmentCount,\r
+             UDP6_HEADER_SIZE,\r
+             0,\r
+             Udp6NetVectorExtFree,\r
+             NULL\r
+             );\r
+  if (Packet == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Store the IpIo in ProtoData.\r
+  //\r
+  Udp6Service                        = Instance->Udp6Service;\r
+  *((UINTN *) &Packet->ProtoData[0]) = (UINTN) (Udp6Service->IpIo);\r
+\r
+  Udp6Header = (EFI_UDP_HEADER *) NetbufAllocSpace (Packet, UDP6_HEADER_SIZE, TRUE);\r
+  ASSERT (Udp6Header != NULL);\r
+  ConfigData = &Instance->ConfigData;\r
+\r
+  //\r
+  // Fill the udp header.\r
+  //\r
+  Udp6Header->SrcPort      = HTONS (ConfigData->StationPort);\r
+  Udp6Header->DstPort      = HTONS (ConfigData->RemotePort);\r
+  Udp6Header->Length       = HTONS ((UINT16) Packet->TotalSize);\r
+  Udp6Header->Checksum     = 0;\r
+  //\r
+  // Set the UDP Header in NET_BUF, this UDP header is for IP6 can fast get the\r
+  // Udp header for pseudoHeadCheckSum.\r
+  //\r
+  Packet->Udp    = Udp6Header;\r
+  UdpSessionData = TxData->UdpSessionData;\r
+\r
+  if (UdpSessionData != NULL) {\r
+    //\r
+    // Set the Destination according to the specified\r
+    // UdpSessionData.\r
+    //\r
+\r
+    if (UdpSessionData->DestinationPort != 0) {\r
+      Udp6Header->DstPort = HTONS (UdpSessionData->DestinationPort);\r
+    }\r
+\r
+    IP6_COPY_ADDRESS (&Source, &ConfigData->StationAddress);\r
+    if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress)) {\r
+      IP6_COPY_ADDRESS (&Destination, &UdpSessionData->DestinationAddress);\r
+    } else {\r
+      IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress);\r
+    }\r
+\r
+    //\r
+    //Calculate the pseudo head checksum using the overridden parameters.\r
+    //\r
+    if (!NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress)) {\r
+      HeadSum = NetIp6PseudoHeadChecksum (\r
+                  &Source,\r
+                  &Destination,\r
+                  EFI_IP_PROTO_UDP,\r
+                  0\r
+                  );\r
+\r
+      //\r
+      // calculate the checksum.\r
+      //\r
+      Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum);\r
+      if (Udp6Header->Checksum == 0) {\r
+        //\r
+        // If the calculated checksum is 0, fill the Checksum field with all ones.\r
+        //\r
+        Udp6Header->Checksum = 0XFFFF;\r
+      }\r
+    } else {\r
+      //\r
+      // Set the checksum is zero if the ConfigData->StationAddress is unspcified\r
+      // and the Ipv6 will fill the correct value of this checksum.\r
+      //\r
+      Udp6Header->Checksum = 0;\r
+\r
+    }\r
+  } else {\r
+    //\r
+    // UdpSessionData is NULL, use the address and port information previously configured.\r
+    //\r
+    IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress);\r
+\r
+    HeadSum = Instance->HeadSum;\r
+    //\r
+    // calculate the checksum.\r
+    //\r
+    Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum);\r
+    if (Udp6Header->Checksum == 0) {\r
+      //\r
+      // If the calculated checksum is 0, fill the Checksum field with all ones.\r
+      //\r
+      Udp6Header->Checksum = 0xffff;\r
+    }\r
+  }\r
+\r
+\r
+\r
+  //\r
+  // Fill the IpIo Override data.\r
+  //\r
+  Override.Ip6OverrideData.Protocol  = EFI_IP_PROTO_UDP;\r
+  Override.Ip6OverrideData.HopLimit  = ConfigData->HopLimit;\r
+  Override.Ip6OverrideData.FlowLabel = 0;\r
+\r
+  //\r
+  // Save the token into the TxToken map.\r
+  //\r
+  Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet);\r
+  if (EFI_ERROR (Status)) {\r
+    goto FREE_PACKET;\r
+  }\r
+\r
+  //\r
+  // Send out this datagram through IpIo.\r
+  //\r
+  if (UdpSessionData != NULL){\r
+    IP6_COPY_ADDRESS (&(IpDestAddr.v6), &Destination);\r
+  } else {\r
+    ZeroMem (&IpDestAddr.v6, sizeof (EFI_IPv6_ADDRESS));\r
+  }\r
+\r
+  Status = IpIoSend (\r
+             Udp6Service->IpIo,\r
+             Packet,\r
+             Instance->IpInfo,\r
+             Instance,\r
+             Token,\r
+             &IpDestAddr,\r
+             &Override\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // Remove this token from the TxTokens.\r
+    //\r
+    Udp6RemoveToken (&Instance->TxTokens, Token);\r
+  }\r
+\r
+FREE_PACKET:\r
+\r
+  NetbufFree (Packet);\r
+\r
+ON_EXIT:\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function places a completion token into the receive packet queue. This function\r
+  is always asynchronous.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to a token that is associated with the\r
+                                 receive data descriptor.\r
+\r
+  @retval EFI_SUCCESS            The receive completion token was cached.\r
+  @retval EFI_NOT_STARTED        This EFI UDPv6 Protocol instance has not been\r
+                                 started.\r
+  @retval EFI_NO_MAPPING         When using a default address, configuration (DHCP,\r
+                                 BOOTP, RARP, etc.) is not finished yet.\r
+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:\r
+                                 This is NULL. Token is NULL. Token.Event is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued\r
+                                 due to a lack of system resources (usually\r
+                                 memory).\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+                                 The EFI UDPv6 Protocol instance has been reset to\r
+                                 startup defaults.\r
+  @retval EFI_ACCESS_DENIED      A receive completion token with the same\r
+                                 Token.Event is already in the receive queue.\r
+  @retval EFI_NOT_READY          The receive request could not be queued because\r
+                                 the receive  queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Receive (\r
+  IN EFI_UDP6_PROTOCOL          *This,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+  EFI_TPL             OldTpl;\r
+\r
+  if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token)) ||\r
+      EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token))\r
+      ){\r
+    //\r
+    // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or\r
+    // RxTokens map.\r
+    //\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Token->Packet.RxData = NULL;\r
+\r
+  //\r
+  // Save the token into the RxTokens map.\r
+  //\r
+  Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_NOT_READY;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // If there is an icmp error, report it.\r
+  //\r
+  Udp6ReportIcmpError (Instance);\r
+\r
+  //\r
+  // Try to delivered the received datagrams.\r
+  //\r
+  Udp6InstanceDeliverDgram (Instance);\r
+\r
+  //\r
+  // Dispatch the DPC queued by the NotifyFunction of Token->Event.\r
+  //\r
+  DispatchDpc ();\r
+\r
+ON_EXIT:\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is used to abort a pending transmit or receive request.\r
+\r
+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.\r
+  @param[in]  Token              Pointer to a token that has been issued by\r
+                                 EFI_UDP6_PROTOCOL.Transmit() or\r
+                                 EFI_UDP6_PROTOCOL.Receive(). This parameter is\r
+                                 optional and may be NULL.\r
+\r
+  @retval EFI_SUCCESS            The asynchronous I/O request was aborted, and\r
+                                 Token.Event was  signaled. When Token is NULL, all\r
+                                 pending requests are aborted and their events are\r
+                                 signaled.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_NOT_STARTED        This instance has not been started.\r
+  @retval EFI_NO_MAPPING         When using the default address, configuration\r
+                                 (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+  @retval EFI_NOT_FOUND          When Token is not NULL, the asynchronous I/O\r
+                                 request is not found in the transmit or receive\r
+                                 queue. It is either completed or not issued by\r
+                                 Transmit() or Receive().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Cancel (\r
+  IN EFI_UDP6_PROTOCOL          *This,\r
+  IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+  EFI_TPL             OldTpl;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+  if (!Instance->Configured) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // Cancle the tokens specified by Token for this instance.\r
+  //\r
+  Status = Udp6InstanceCancelToken (Instance, Token);\r
+\r
+  //\r
+  // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.\r
+  //\r
+  DispatchDpc ();\r
+\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function can be used by network drivers and applications to increase the rate that\r
+  data packets are moved between the communications device and the transmit/receive queues.\r
+\r
+  @param[in] This                Pointer to the EFI_UDP6_PROTOCOL instance.\r
+\r
+  @retval EFI_SUCCESS            Incoming or outgoing data was processed.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL.\r
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.\r
+  @retval EFI_TIMEOUT            Data was dropped out of the transmit and/or\r
+                                 receive queue.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Poll (\r
+  IN EFI_UDP6_PROTOCOL  *This\r
+  )\r
+{\r
+  UDP6_INSTANCE_DATA  *Instance;\r
+  EFI_IP6_PROTOCOL    *Ip;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+  Ip       = Instance->IpInfo->Ip.Ip6;\r
+\r
+  //\r
+  // Invode the Ip instance consumed by the udp instance to do the poll operation.\r
+  //\r
+  return Ip->Poll (Ip);\r
+}\r
diff --git a/NetworkPkg/UefiPxeBcDxe/ComponentName.c b/NetworkPkg/UefiPxeBcDxe/ComponentName.c
new file mode 100644 (file)
index 0000000..4228519
--- /dev/null
@@ -0,0 +1,312 @@
+/** @file\r
+  UEFI Component Name(2) protocol implementation for UefiPxeBc driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  );\r
+\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  );\r
+\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL    gPxeBcComponentName  = {\r
+  PxeBcComponentNameGetDriverName,\r
+  PxeBcComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL   gPxeBcComponentName2 = {\r
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PxeBcComponentNameGetDriverName,\r
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PxeBcComponentNameGetControllerName,\r
+  "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE       mPxeBcDriverNameTable[] = {\r
+  {\r
+    "eng;en",\r
+    L"UEFI PXE Base Code Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+  This function retrieves the user-readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user-readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **DriverName\r
+  )\r
+{\r
+  return LookupUnicodeString2(\r
+           Language,\r
+           This->SupportedLanguages,\r
+           mPxeBcDriverNameTable,\r
+           DriverName,\r
+           (BOOLEAN)(This == &gPxeBcComponentName)\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user-readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user-readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user-readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   ControllerHandle,\r
+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,\r
+  IN  CHAR8                        *Language,\r
+  OUT CHAR16                       **ControllerName\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
new file mode 100644 (file)
index 0000000..dd1d76d
--- /dev/null
@@ -0,0 +1,1170 @@
+/** @file\r
+  Boot functions implementation for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+/**\r
+  Display the string of the boot item.\r
+\r
+  If the length of the boot item string beyond 70 Char, just display 70 Char.\r
+\r
+  @param[in]  Str           The pointer to the string.\r
+  @param[in]  Len           The length of the string.\r
+\r
+**/\r
+VOID\r
+PxeBcDisplayBootItem (\r
+  IN UINT8                 *Str,\r
+  IN UINT8                 Len\r
+  )\r
+{\r
+  UINT8                    Tmp;\r
+\r
+  //\r
+  // Cut off the chars behind 70th.\r
+  //\r
+  Len       = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);\r
+  Tmp       = Str[Len];\r
+  Str[Len]  = 0;\r
+  AsciiPrint ("%a \n", Str);\r
+\r
+  //\r
+  // Restore the original 70th char.\r
+  //\r
+  Str[Len]  = Tmp;\r
+}\r
+\r
+\r
+/**\r
+  Select and maintain the boot prompt if needed.\r
+\r
+  @param[in]  Private          Pointer to PxeBc private data.\r
+\r
+  @retval EFI_SUCCESS          Selected boot prompt done.\r
+  @retval EFI_TIMEOUT          Selected boot prompt timed out.\r
+  @retval EFI_NOT_FOUND        The proxy offer is not Pxe10.\r
+  @retval EFI_ABORTED          User cancelled the operation.\r
+  @retval EFI_NOT_READY        Reading the input key from the keyboard has not finish.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcSelectBootPrompt (\r
+  IN PXEBC_PRIVATE_DATA      *Private\r
+  )\r
+{\r
+  PXEBC_DHCP_PACKET_CACHE    *Cache;\r
+  PXEBC_VENDOR_OPTION        *VendorOpt;\r
+  EFI_PXE_BASE_CODE_MODE     *Mode;\r
+  EFI_EVENT                  TimeoutEvent;\r
+  EFI_EVENT                  DescendEvent;\r
+  EFI_INPUT_KEY              InputKey;\r
+  EFI_STATUS                 Status;\r
+  UINT32                     OfferType;\r
+  UINT8                      Timeout;\r
+  UINT8                      *Prompt;\r
+  UINT8                      PromptLen;\r
+  INT32                      SecCol;\r
+  INT32                      SecRow;\r
+\r
+  TimeoutEvent = NULL;\r
+  DescendEvent = NULL;\r
+  Mode         = Private->PxeBc.Mode;\r
+  Cache        = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;\r
+  OfferType    = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;\r
+\r
+  //\r
+  // Only ProxyPxe10 offer needs boot prompt.\r
+  //\r
+  if (OfferType != PxeOfferTypeProxyPxe10) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.\r
+  //\r
+  ASSERT (!Mode->UsingIpv6);\r
+\r
+  VendorOpt = &Cache->Dhcp4.VendorOpt;\r
+  if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Timeout   = VendorOpt->MenuPrompt->Timeout;\r
+  Prompt    = VendorOpt->MenuPrompt->Prompt;\r
+  PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);\r
+\r
+  //\r
+  // The valid scope of Timeout refers to PXE2.1 spec.\r
+  //\r
+  if (Timeout == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  if (Timeout == 255) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  //\r
+  // Create and start a timer as timeout event.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &TimeoutEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  TimeoutEvent,\r
+                  TimerRelative,\r
+                  Timeout * TICKS_PER_SECOND\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Create and start a periodic timer as descend event by second.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &DescendEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  DescendEvent,\r
+                  TimerPeriodic,\r
+                  TICKS_PER_SECOND\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Display the boot item and cursor on the screen.\r
+  //\r
+  SecCol = gST->ConOut->Mode->CursorColumn;\r
+  SecRow = gST->ConOut->Mode->CursorRow;\r
+\r
+  PxeBcDisplayBootItem (Prompt, PromptLen);\r
+\r
+  gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);\r
+  AsciiPrint ("(%d) ", Timeout--);\r
+\r
+  while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+    if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {\r
+      gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);\r
+      AsciiPrint ("(%d) ", Timeout--);\r
+    }\r
+    if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {\r
+      gBS->Stall (10 * TICKS_PER_MS);\r
+      continue;\r
+    }\r
+    //\r
+    // Parse the input key by user.\r
+    //\r
+    if (InputKey.ScanCode == 0) {\r
+\r
+      switch (InputKey.UnicodeChar) {\r
+\r
+      case CTRL ('c'):\r
+        Status = EFI_ABORTED;\r
+        break;\r
+\r
+      case CTRL ('m'):\r
+      case 'm':\r
+      case 'M':\r
+        Status = EFI_TIMEOUT;\r
+        break;\r
+\r
+      default:\r
+        continue;\r
+      }\r
+\r
+    } else {\r
+\r
+      switch (InputKey.ScanCode) {\r
+\r
+      case SCAN_F8:\r
+        Status = EFI_TIMEOUT;\r
+        break;\r
+\r
+      case SCAN_ESC:\r
+        Status = EFI_ABORTED;\r
+        break;\r
+\r
+      default:\r
+        continue;\r
+      }\r
+    }\r
+\r
+    break;\r
+  }\r
+\r
+  //\r
+  // Reset the cursor on the screen.\r
+  //\r
+  gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);\r
+\r
+ON_EXIT:\r
+  if (DescendEvent != NULL) {\r
+    gBS->CloseEvent (DescendEvent);\r
+  }\r
+  if (TimeoutEvent != NULL) {\r
+    gBS->CloseEvent (TimeoutEvent);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Select the boot menu by user's input.\r
+\r
+  @param[in]  Private         Pointer to PxeBc private data.\r
+  @param[out] Type            The type of the menu.\r
+  @param[in]  UseDefaultItem  Use default item or not.\r
+\r
+  @retval EFI_ABORTED     User cancel operation.\r
+  @retval EFI_SUCCESS     Select the boot menu success.\r
+  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcSelectBootMenu (\r
+  IN  PXEBC_PRIVATE_DATA              *Private,\r
+  OUT UINT16                          *Type,\r
+  IN  BOOLEAN                         UseDefaultItem\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE     *Mode;\r
+  PXEBC_DHCP_PACKET_CACHE    *Cache;\r
+  PXEBC_VENDOR_OPTION        *VendorOpt;\r
+  EFI_INPUT_KEY              InputKey;\r
+  UINT32                     OfferType;\r
+  UINT8                      MenuSize;\r
+  UINT8                      MenuNum;\r
+  INT32                      TopRow;\r
+  UINT16                     Select;\r
+  UINT16                     LastSelect;\r
+  UINT8                      Index;\r
+  BOOLEAN                    Finish;\r
+  CHAR8                      Blank[PXEBC_DISPLAY_MAX_LINE];\r
+  PXEBC_BOOT_MENU_ENTRY      *MenuItem;\r
+  PXEBC_BOOT_MENU_ENTRY      *MenuArray[PXEBC_MENU_MAX_NUM];\r
+\r
+  Finish    = FALSE;\r
+  Select    = 1;\r
+  Index     = 0;\r
+  *Type     = 0;\r
+  Mode      = Private->PxeBc.Mode;\r
+  Cache     = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;\r
+  OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;\r
+\r
+  //\r
+  // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.\r
+  //\r
+  ASSERT (!Mode->UsingIpv6);\r
+  ASSERT (OfferType == PxeOfferTypeProxyPxe10);\r
+\r
+  VendorOpt = &Cache->Dhcp4.VendorOpt;\r
+  if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Display the boot menu on the screen.\r
+  //\r
+  SetMem (Blank, sizeof(Blank), ' ');\r
+\r
+  MenuSize  = VendorOpt->BootMenuLen;\r
+  MenuItem  = VendorOpt->BootMenu;\r
+\r
+  if (MenuSize == 0) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {\r
+    ASSERT (MenuItem != NULL);\r
+    MenuArray[Index]  = MenuItem;\r
+    MenuSize          = (UINT8) (MenuSize - (MenuItem->DescLen + 3));\r
+    MenuItem          = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);\r
+    Index++;\r
+  }\r
+\r
+  if (UseDefaultItem) {\r
+    ASSERT (MenuArray[0] != NULL);\r
+    CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));\r
+    *Type = NTOHS (*Type);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  MenuNum = Index;\r
+\r
+  for (Index = 0; Index < MenuNum; Index++) {\r
+    ASSERT (MenuArray[Index] != NULL);\r
+    PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);\r
+  }\r
+\r
+  TopRow = gST->ConOut->Mode->CursorRow - MenuNum;\r
+\r
+  //\r
+  // Select the boot item by user in the boot menu.\r
+  //\r
+  do {\r
+    //\r
+    // Highlight selected row.\r
+    //\r
+    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);\r
+    ASSERT (Select < PXEBC_MENU_MAX_NUM);\r
+    ASSERT (MenuArray[Select] != NULL);\r
+    Blank[MenuArray[Select]->DescLen] = 0;\r
+    AsciiPrint ("%a\r", Blank);\r
+    PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);\r
+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);\r
+    LastSelect = Select;\r
+\r
+    while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {\r
+      gBS->Stall (10 * TICKS_PER_MS);\r
+    }\r
+\r
+    if (InputKey.ScanCode != 0) {\r
+      switch (InputKey.UnicodeChar) {\r
+      case CTRL ('c'):\r
+        InputKey.ScanCode = SCAN_ESC;\r
+        break;\r
+\r
+      case CTRL ('j'):  /* linefeed */\r
+      case CTRL ('m'):  /* return */\r
+        Finish = TRUE;\r
+        break;\r
+\r
+      case CTRL ('i'):  /* tab */\r
+      case ' ':\r
+      case 'd':\r
+      case 'D':\r
+        InputKey.ScanCode = SCAN_DOWN;\r
+        break;\r
+\r
+      case CTRL ('h'):  /* backspace */\r
+      case 'u':\r
+      case 'U':\r
+        InputKey.ScanCode = SCAN_UP;\r
+        break;\r
+\r
+      default:\r
+        InputKey.ScanCode = 0;\r
+      }\r
+    }\r
+\r
+    switch (InputKey.ScanCode) {\r
+    case SCAN_LEFT:\r
+    case SCAN_UP:\r
+      if (Select != 0) {\r
+        Select--;\r
+      }\r
+      break;\r
+\r
+    case SCAN_DOWN:\r
+    case SCAN_RIGHT:\r
+      if (++Select == MenuNum) {\r
+        Select--;\r
+      }\r
+      break;\r
+\r
+    case SCAN_PAGE_UP:\r
+    case SCAN_HOME:\r
+      Select = 0;\r
+      break;\r
+\r
+    case SCAN_PAGE_DOWN:\r
+    case SCAN_END:\r
+      Select = (UINT16) (MenuNum - 1);\r
+      break;\r
+\r
+    case SCAN_ESC:\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    //\r
+    // Unhighlight the last selected row.\r
+    //\r
+    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);\r
+    ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);\r
+    ASSERT (MenuArray[LastSelect] != NULL);\r
+    Blank[MenuArray[LastSelect]->DescLen] = 0;\r
+    AsciiPrint ("%a\r", Blank);\r
+    PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);\r
+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);\r
+  } while (!Finish);\r
+\r
+  //\r
+  // Swap the byte order.\r
+  //\r
+  ASSERT (Select < PXEBC_MENU_MAX_NUM);\r
+  ASSERT (MenuArray[Select] != NULL);\r
+  CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));\r
+  *Type = NTOHS (*Type);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse out the boot information from the last Dhcp4 reply packet.\r
+\r
+  @param[in, out] Private      Pointer to PxeBc private data.\r
+  @param[out]     BufferSize   Size of the boot file to be downloaded.\r
+\r
+  @retval EFI_SUCCESS          Successfully parsed out all the boot information.\r
+  @retval Others               Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4BootInfo (\r
+  IN OUT PXEBC_PRIVATE_DATA   *Private,\r
+     OUT UINT64               *BufferSize\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;\r
+  EFI_PXE_BASE_CODE_MODE      *Mode;\r
+  EFI_STATUS                  Status;\r
+  PXEBC_DHCP4_PACKET_CACHE    *Cache4;\r
+  UINT16                      Value;\r
+\r
+  PxeBc       = &Private->PxeBc;\r
+  Mode        = PxeBc->Mode;\r
+  Status      = EFI_SUCCESS;\r
+  *BufferSize = 0;\r
+\r
+  //\r
+  // Get the last received Dhcp4 reply packet.\r
+  //\r
+  if (Mode->PxeReplyReceived) {\r
+    Cache4 = &Private->PxeReply.Dhcp4;\r
+  } else if (Mode->ProxyOfferReceived) {\r
+    Cache4 = &Private->ProxyOffer.Dhcp4;\r
+  } else {\r
+    Cache4 = &Private->DhcpAck.Dhcp4;\r
+  }\r
+\r
+  //\r
+  // Parse the boot server Ipv4 address by next server address.\r
+  // If this field isn't available, use option 54 instead.\r
+  //\r
+  CopyMem (\r
+    &Private->ServerIp,\r
+    &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,\r
+    sizeof (EFI_IPv4_ADDRESS)\r
+    );\r
+\r
+  if (Private->ServerIp.Addr[0] == 0) {\r
+    CopyMem (\r
+      &Private->ServerIp,\r
+      Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+  }\r
+\r
+  //\r
+  // Parse the boot file name by option.\r
+  //\r
+  ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);\r
+  Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;\r
+\r
+  if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {\r
+    //\r
+    // Parse the boot file size by option.\r
+    //\r
+    CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));\r
+    Value       = NTOHS (Value);\r
+    //\r
+    // The field of boot file size is 512 bytes in unit.\r
+    //\r
+    *BufferSize = 512 * Value;\r
+  } else {\r
+    //\r
+    // Get the bootfile size by tftp command if no option available.\r
+    //\r
+    Status = PxeBc->Mtftp (\r
+                      PxeBc,\r
+                      EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
+                      NULL,\r
+                      FALSE,\r
+                      BufferSize,\r
+                      &Private->BlockSize,\r
+                      &Private->ServerIp,\r
+                      Private->BootFileName,\r
+                      NULL,\r
+                      FALSE\r
+                      );\r
+  }\r
+\r
+  //\r
+  // Save the value of boot file size.\r
+  //\r
+  Private->BootFileSize = (UINTN) *BufferSize;\r
+\r
+  //\r
+  // Display all the information: boot server address, boot file name and boot file size.\r
+  //\r
+  AsciiPrint ("\n  Server IP address is ");\r
+  PxeBcShowIp4Addr (&Private->ServerIp.v4);\r
+  AsciiPrint ("\n  NBP filename is %a", Private->BootFileName);\r
+  AsciiPrint ("\n  NBP filesize is %d Bytes", Private->BootFileSize);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Parse out the boot information from the last Dhcp6 reply packet.\r
+\r
+  @param[in, out] Private      Pointer to PxeBc private data.\r
+  @param[out]     BufferSize   Size of the boot file to be downloaded.\r
+\r
+  @retval EFI_SUCCESS          Successfully parsed out all the boot information.\r
+  @retval EFI_BUFFER_TOO_SMALL\r
+  @retval Others               Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6BootInfo (\r
+  IN OUT PXEBC_PRIVATE_DATA   *Private,\r
+     OUT UINT64               *BufferSize\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;\r
+  EFI_PXE_BASE_CODE_MODE      *Mode;\r
+  EFI_STATUS                  Status;\r
+  PXEBC_DHCP6_PACKET_CACHE    *Cache6;\r
+  UINT16                      Value;\r
+\r
+  PxeBc       = &Private->PxeBc;\r
+  Mode        = PxeBc->Mode;\r
+  Status      = EFI_SUCCESS;\r
+  *BufferSize = 0;\r
+\r
+  //\r
+  // Get the last received Dhcp6 reply packet.\r
+  //\r
+  if (Mode->PxeReplyReceived) {\r
+    Cache6 = &Private->PxeReply.Dhcp6;\r
+  } else if (Mode->ProxyOfferReceived) {\r
+    Cache6 = &Private->ProxyOffer.Dhcp6;\r
+  } else {\r
+    Cache6 = &Private->DhcpAck.Dhcp6;\r
+  }\r
+\r
+  ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
+\r
+  //\r
+  // Parse (m)tftp server ip address and bootfile name.\r
+  //\r
+  Status = PxeBcExtractBootFileUrl (\r
+             &Private->BootFileName,\r
+             &Private->ServerIp.v6,\r
+             (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
+             NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Parse the value of boot file size.\r
+  //\r
+  if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {\r
+    //\r
+    // Parse it out if have the boot file parameter option.\r
+    //\r
+    Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    //\r
+    // The field of boot file size is 512 bytes in unit.\r
+    //\r
+    *BufferSize = 512 * Value;\r
+  } else {\r
+    //\r
+    // Send get file size command by tftp if option unavailable.\r
+    //\r
+    Status = PxeBc->Mtftp (\r
+                      PxeBc,\r
+                      EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
+                      NULL,\r
+                      FALSE,\r
+                      BufferSize,\r
+                      &Private->BlockSize,\r
+                      &Private->ServerIp,\r
+                      Private->BootFileName,\r
+                      NULL,\r
+                      FALSE\r
+                      );\r
+  }\r
+\r
+  //\r
+  // Save the value of boot file size.\r
+  //\r
+  Private->BootFileSize = (UINTN) *BufferSize;\r
+\r
+  //\r
+  // Display all the information: boot server address, boot file name and boot file size.\r
+  //\r
+  AsciiPrint ("\n  Server IP address is ");\r
+  PxeBcShowIp6Addr (&Private->ServerIp.v6);\r
+  AsciiPrint ("\n  NBP filename is %a", Private->BootFileName);\r
+  AsciiPrint ("\n  NBP filesize is %d Bytes", Private->BootFileSize);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Extract the discover information and boot server entry from the\r
+  cached packets if unspecified.\r
+\r
+  @param[in]      Private      Pointer to PxeBc private data.\r
+  @param[in]      Type         The type of bootstrap to perform.\r
+  @param[in, out] Info         Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.\r
+  @param[out]     BootEntry    Pointer to PXEBC_BOOT_SVR_ENTRY.\r
+  @param[out]     SrvList      Pointer to EFI_PXE_BASE_CODE_SRVLIST.\r
+\r
+  @retval EFI_SUCCESS       Successfully extracted the information.\r
+  @retval EFI_DEVICE_ERROR  Failed to extract the information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractDiscoverInfo (\r
+  IN     PXEBC_PRIVATE_DATA               *Private,\r
+  IN     UINT16                           Type,\r
+  IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO  *Info,\r
+     OUT PXEBC_BOOT_SVR_ENTRY             **BootEntry,\r
+     OUT EFI_PXE_BASE_CODE_SRVLIST        **SrvList\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE          *Mode;\r
+  PXEBC_DHCP4_PACKET_CACHE        *Cache4;\r
+  PXEBC_VENDOR_OPTION             *VendorOpt;\r
+  PXEBC_BOOT_SVR_ENTRY            *Entry;\r
+  BOOLEAN                         IsFound;\r
+\r
+  Mode = Private->PxeBc.Mode;\r
+\r
+  if (Mode->UsingIpv6) {\r
+    Info->IpCnt    = 1;\r
+    Info->UseUCast = TRUE;\r
+\r
+    Info->SrvList[0].Type              = Type;\r
+    Info->SrvList[0].AcceptAnyResponse = FALSE;\r
+\r
+    //\r
+    // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.\r
+    //\r
+    CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));\r
+\r
+    *SrvList  = Info->SrvList;\r
+  } else {\r
+    Entry     = NULL;\r
+    IsFound   = FALSE;\r
+    Cache4    = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;\r
+    VendorOpt = &Cache4->VendorOpt;\r
+\r
+    if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {\r
+      //\r
+      // Address is not acquired or no discovery options.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Parse the boot server entry from the vendor option in the last cached packet.\r
+    //\r
+    Info->UseMCast    = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);\r
+    Info->UseBCast    = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);\r
+    Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);\r
+    Info->UseUCast    = Info->MustUseList;\r
+\r
+    if (Info->UseMCast) {\r
+      //\r
+      // Get the multicast discover ip address from vendor option if has.\r
+      //\r
+      CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));\r
+    }\r
+\r
+    Info->IpCnt = 0;\r
+\r
+    if (Info->MustUseList) {\r
+      Entry = VendorOpt->BootSvr;\r
+\r
+      while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {\r
+        if (Entry->Type == HTONS (Type)) {\r
+          IsFound = TRUE;\r
+          break;\r
+        }\r
+        Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);\r
+      }\r
+\r
+      if (!IsFound) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      Info->IpCnt = Entry->IpCnt;\r
+    }\r
+\r
+    *BootEntry = Entry;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Build the discover packet and send out for boot server.\r
+\r
+  @param[in]  Private               Pointer to PxeBc private data.\r
+  @param[in]  Type                  PxeBc option boot item type.\r
+  @param[in]  Layer                 Pointer to option boot item layer.\r
+  @param[in]  UseBis                Use BIS or not.\r
+  @param[in]  DestIp                Pointer to the destination address.\r
+  @param[in]  IpCount               The count of the server address.\r
+  @param[in]  SrvList               Pointer to the server address list.\r
+\r
+  @retval     EFI_SUCCESS           Successfully discovered boot file.\r
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.\r
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.\r
+  @retval     Others                Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDiscoverBootServer (\r
+  IN  PXEBC_PRIVATE_DATA                *Private,\r
+  IN  UINT16                            Type,\r
+  IN  UINT16                            *Layer,\r
+  IN  BOOLEAN                           UseBis,\r
+  IN  EFI_IP_ADDRESS                    *DestIp,\r
+  IN  UINT16                            IpCount,\r
+  IN  EFI_PXE_BASE_CODE_SRVLIST         *SrvList\r
+  )\r
+{\r
+  if (Private->PxeBc.Mode->UsingIpv6) {\r
+    return PxeBcDhcp6Discover (\r
+             Private,\r
+             Type,\r
+             Layer,\r
+             UseBis,\r
+             DestIp\r
+             );\r
+  } else {\r
+    return PxeBcDhcp4Discover (\r
+             Private,\r
+             Type,\r
+             Layer,\r
+             UseBis,\r
+             DestIp,\r
+             IpCount,\r
+             SrvList\r
+             );\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Discover all the boot information for boot file.\r
+\r
+  @param[in, out] Private      Pointer to PxeBc private data.\r
+  @param[out]     BufferSize   Size of the boot file to be downloaded.\r
+\r
+  @retval EFI_SUCCESS          Successfully obtained all the boot information .\r
+  @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
+  @retval EFI_ABORTED          User cancel current operation.\r
+  @retval Others               Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDiscoverBootFile (\r
+  IN OUT PXEBC_PRIVATE_DATA   *Private,\r
+     OUT UINT64               *BufferSize\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;\r
+  EFI_PXE_BASE_CODE_MODE      *Mode;\r
+  EFI_STATUS                  Status;\r
+  UINT16                      Type;\r
+  UINT16                      Layer;\r
+  BOOLEAN                     UseBis;\r
+\r
+  PxeBc = &Private->PxeBc;\r
+  Mode  = PxeBc->Mode;\r
+  Type  = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;\r
+  Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;\r
+\r
+  //\r
+  // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and\r
+  // other pxe boot information.\r
+  //\r
+  Status = PxeBc->Dhcp (PxeBc, TRUE);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Select a boot server from boot server list.\r
+  //\r
+  Status = PxeBcSelectBootPrompt (Private);\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    //\r
+    // Choose by user's input.\r
+    //\r
+    Status = PxeBcSelectBootMenu (Private, &Type, TRUE);\r
+  } else if (Status == EFI_TIMEOUT) {\r
+    //\r
+    // Choose by default item.\r
+    //\r
+    Status = PxeBcSelectBootMenu (Private, &Type, FALSE);\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+\r
+    if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {\r
+      //\r
+      // Local boot(PXE bootstrap server) need abort\r
+      //\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    //\r
+    // Start to discover the boot server to get (m)tftp server ip address, bootfile\r
+    // name and bootfile size.\r
+    //\r
+    UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);\r
+    Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Parse the boot information.\r
+  //\r
+  if (Mode->UsingIpv6) {\r
+    Status = PxeBcDhcp6BootInfo (Private, BufferSize);\r
+  } else {\r
+    Status = PxeBcDhcp4BootInfo (Private, BufferSize);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Install PxeBaseCodeCallbackProtocol if not installed before.\r
+\r
+  @param[in, out] Private           Pointer to PxeBc private data.\r
+  @param[out]     NewMakeCallback   If TRUE, it is a new callback.\r
+                                    Otherwise, it is not new callback.\r
+  @retval EFI_SUCCESS          PxeBaseCodeCallbackProtocol installed succesfully.\r
+  @retval Others               Failed to install PxeBaseCodeCallbackProtocol.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcInstallCallback (\r
+  IN OUT PXEBC_PRIVATE_DATA   *Private,\r
+     OUT BOOLEAN              *NewMakeCallback\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;\r
+  EFI_STATUS                  Status;\r
+\r
+  //\r
+  // Check whether PxeBaseCodeCallbackProtocol already installed.\r
+  //\r
+  PxeBc  = &Private->PxeBc;\r
+  Status = gBS->HandleProtocol (\r
+                  Private->Controller,\r
+                  &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+                  (VOID **) &Private->PxeBcCallback\r
+                  );\r
+  if (Status == EFI_UNSUPPORTED) {\r
+\r
+    CopyMem (\r
+      &Private->LoadFileCallback,\r
+      &gPxeBcCallBackTemplate,\r
+      sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)\r
+      );\r
+\r
+    //\r
+    // Install a default callback if user didn't offer one.\r
+    //\r
+    Status = gBS->InstallProtocolInterface (\r
+                    &Private->Controller,\r
+                    &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+                    EFI_NATIVE_INTERFACE,\r
+                    &Private->LoadFileCallback\r
+                    );\r
+\r
+    (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);\r
+\r
+    Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);\r
+    if (EFI_ERROR (Status)) {\r
+      PxeBc->Stop (PxeBc);\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Uninstall PxeBaseCodeCallbackProtocol.\r
+\r
+  @param[in]  Private           Pointer to PxeBc private data.\r
+  @param[in]  NewMakeCallback   If TRUE, it is a new callback.\r
+                                Otherwise, it is not new callback.\r
+\r
+**/\r
+VOID\r
+PxeBcUninstallCallback (\r
+  IN PXEBC_PRIVATE_DATA        *Private,\r
+  IN BOOLEAN                   NewMakeCallback\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_PROTOCOL   *PxeBc;\r
+\r
+  PxeBc = &Private->PxeBc;\r
+\r
+  if (NewMakeCallback) {\r
+\r
+    NewMakeCallback = FALSE;\r
+\r
+    PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);\r
+\r
+    gBS->UninstallProtocolInterface (\r
+          Private->Controller,\r
+          &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+          &Private->LoadFileCallback\r
+          );\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Download one of boot file in the list, and it's special for IPv6.\r
+\r
+  @param[in]      Private           Pointer to PxeBc private data.\r
+  @param[in, out] BufferSize        Size of user buffer for input;\r
+                                    required buffer size for output.\r
+  @param[in]      Buffer            Pointer to user buffer.\r
+\r
+  @retval EFI_SUCCESS               Read one of boot file in the list successfully.\r
+  @retval EFI_BUFFER_TOO_SMALL      The buffer size is not enough for boot file.\r
+  @retval EFI_NOT_FOUND             There is no proper boot file available.\r
+  @retval Others                    Failed to download boot file in the list.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcReadBootFileList (\r
+  IN     PXEBC_PRIVATE_DATA           *Private,\r
+  IN OUT UINT64                       *BufferSize,\r
+  IN     VOID                         *Buffer           OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;\r
+\r
+  PxeBc        = &Private->PxeBc;\r
+\r
+  //\r
+  // Try to download the boot file if everything is ready.\r
+  //\r
+  if (Buffer != NULL) {\r
+    Status = PxeBc->Mtftp (\r
+                      PxeBc,\r
+                      EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
+                      Buffer,\r
+                      FALSE,\r
+                      BufferSize,\r
+                      &Private->BlockSize,\r
+                      &Private->ServerIp,\r
+                      Private->BootFileName,\r
+                      NULL,\r
+                      FALSE\r
+                      );\r
+\r
+\r
+  } else {\r
+    Status      = EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Load boot file into user buffer.\r
+\r
+  @param[in]      Private           Pointer to PxeBc private data.\r
+  @param[in, out] BufferSize        Size of user buffer for input;\r
+                                    required buffer size for output.\r
+  @param[in]      Buffer            Pointer to user buffer.\r
+\r
+  @retval EFI_SUCCESS          Get all the boot information successfully.\r
+  @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
+  @retval EFI_ABORTED          User cancelled the current operation.\r
+  @retval Others               Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcLoadBootFile (\r
+  IN     PXEBC_PRIVATE_DATA           *Private,\r
+  IN OUT UINTN                        *BufferSize,\r
+  IN     VOID                         *Buffer         OPTIONAL\r
+  )\r
+{\r
+  BOOLEAN                             NewMakeCallback;\r
+  UINT64                              RequiredSize;\r
+  UINT64                              CurrentSize;\r
+  EFI_STATUS                          Status;\r
+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;\r
+  EFI_PXE_BASE_CODE_MODE              *PxeBcMode;\r
+\r
+  NewMakeCallback = FALSE;\r
+  PxeBc           = &Private->PxeBc;\r
+  PxeBcMode       = &Private->Mode;\r
+  CurrentSize     = *BufferSize;\r
+  RequiredSize    = 0;\r
+\r
+  //\r
+  // Install pxebc callback protocol if hasn't been installed yet.\r
+  //\r
+  Status = PxeBcInstallCallback (Private, &NewMakeCallback);\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Private->BootFileSize == 0) {\r
+    //\r
+    // Discover the boot information about the bootfile if hasn't.\r
+    //\r
+    Status = PxeBcDiscoverBootFile (Private, &RequiredSize);\r
+\r
+    if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {\r
+      //\r
+      // It's error if the required buffer size is beyond the system scope.\r
+      //\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto ON_EXIT;\r
+    } else if (RequiredSize > 0) {\r
+      //\r
+      // Get the right buffer size of the bootfile required.\r
+      //\r
+      if (CurrentSize < RequiredSize || Buffer == NULL) {\r
+        //\r
+        // It's buffer too small if the size of user buffer is smaller than the required.\r
+        //\r
+        CurrentSize = RequiredSize;\r
+        Status      = EFI_BUFFER_TOO_SMALL;\r
+        goto ON_EXIT;\r
+      }\r
+      CurrentSize = RequiredSize;\r
+    } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {\r
+      //\r
+      // Try to download another bootfile in list if failed to get the filesize of the last one.\r
+      // It's special for the case of IPv6.\r
+      //\r
+      Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);\r
+      goto ON_EXIT;\r
+    }\r
+  } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {\r
+    //\r
+    // It's buffer too small if the size of user buffer is smaller than the required.\r
+    //\r
+    CurrentSize = Private->BootFileSize;\r
+    Status      = EFI_BUFFER_TOO_SMALL;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Begin to download the bootfile if everything is ready.\r
+  //\r
+  AsciiPrint ("\n Downloading NBP file...\n");\r
+  if (PxeBcMode->UsingIpv6) {\r
+    Status = PxeBcReadBootFileList (\r
+               Private,\r
+               &CurrentSize,\r
+               Buffer\r
+               );\r
+  } else {\r
+    Status = PxeBc->Mtftp (\r
+                      PxeBc,\r
+                      EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
+                      Buffer,\r
+                      FALSE,\r
+                      &CurrentSize,\r
+                      &Private->BlockSize,\r
+                      &Private->ServerIp,\r
+                      Private->BootFileName,\r
+                      NULL,\r
+                      FALSE\r
+                      );\r
+  }\r
+\r
+ON_EXIT:\r
+  *BufferSize = (UINTN) CurrentSize;\r
+  PxeBcUninstallCallback(Private, NewMakeCallback);\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    AsciiPrint ("\n  Succeed to download NBP file.\n");\r
+    return EFI_SUCCESS;\r
+  } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {\r
+    AsciiPrint ("\n  PXE-E05: Buffer size is smaller than the requested file.\n");\r
+  } else if (Status == EFI_DEVICE_ERROR) {\r
+    AsciiPrint ("\n  PXE-E07: Network device error.\n");\r
+  } else if (Status == EFI_OUT_OF_RESOURCES) {\r
+    AsciiPrint ("\n  PXE-E09: Could not allocate I/O buffers.\n");\r
+  } else if (Status == EFI_NO_MEDIA) {\r
+    AsciiPrint ("\n  PXE-E12: Could not detect network connection.\n");\r
+  } else if (Status == EFI_NO_RESPONSE) {\r
+    AsciiPrint ("\n  PXE-E16: No offer received.\n");\r
+  } else if (Status == EFI_TIMEOUT) {\r
+    AsciiPrint ("\n  PXE-E18: Server response timeout.\n");\r
+  } else if (Status == EFI_ABORTED) {\r
+    AsciiPrint ("\n  PXE-E21: Remote boot cancelled.\n");\r
+  } else if (Status == EFI_ICMP_ERROR) {\r
+    AsciiPrint ("\n  PXE-E22: Client received ICMP error from server.\n");\r
+  } else if (Status == EFI_TFTP_ERROR) {\r
+    AsciiPrint ("\n  PXE-E23: Client received TFTP error from server.\n");\r
+  } else if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    AsciiPrint ("\n  PXE-E99: Unexpected network error.\n");\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h
new file mode 100644 (file)
index 0000000..ef18907
--- /dev/null
@@ -0,0 +1,100 @@
+/** @file\r
+  Boot functions declaration for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_BOOT_H__\r
+#define __EFI_PXEBC_BOOT_H__\r
+\r
+#define PXEBC_DISPLAY_MAX_LINE             70\r
+#define PXEBC_DEFAULT_UDP_OVERHEAD_SIZE    8\r
+#define PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE   4\r
+\r
+#define PXEBC_IS_SIZE_OVERFLOWED(x)   ((sizeof (UINTN) < sizeof (UINT64)) && ((x) > 0xFFFFFFFF))\r
+\r
+\r
+/**\r
+  Extract the discover information and boot server entry from the\r
+  cached packets if unspecified.\r
+\r
+  @param[in]      Private      Pointer to PxeBc private data.\r
+  @param[in]      Type         The type of bootstrap to perform.\r
+  @param[in, out] Info         Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.\r
+  @param[out]     BootEntry    Pointer to PXEBC_BOOT_SVR_ENTRY.\r
+  @param[out]     SrvList      Pointer to EFI_PXE_BASE_CODE_SRVLIST.\r
+\r
+  @retval EFI_SUCCESS       Successfully extracted the information.\r
+  @retval EFI_DEVICE_ERROR  Failed to extract the information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractDiscoverInfo (\r
+  IN     PXEBC_PRIVATE_DATA               *Private,\r
+  IN     UINT16                           Type,\r
+  IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO  *Info,\r
+     OUT PXEBC_BOOT_SVR_ENTRY             **BootEntry,\r
+     OUT EFI_PXE_BASE_CODE_SRVLIST        **SrvList\r
+  );\r
+\r
+\r
+/**\r
+  Build the discover packet and send out for boot.\r
+\r
+  @param[in]  Private               Pointer to PxeBc private data.\r
+  @param[in]  Type                  PxeBc option boot item type.\r
+  @param[in]  Layer                 Pointer to option boot item layer.\r
+  @param[in]  UseBis                Use BIS or not.\r
+  @param[in]  DestIp                Pointer to the server address.\r
+  @param[in]  IpCount               The total count of the server address.\r
+  @param[in]  SrvList               Pointer to the server address list.\r
+\r
+  @retval     EFI_SUCCESS           Successfully discovered boot file.\r
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.\r
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.\r
+  @retval     Others                Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDiscoverBootServer (\r
+  IN  PXEBC_PRIVATE_DATA                *Private,\r
+  IN  UINT16                            Type,\r
+  IN  UINT16                            *Layer,\r
+  IN  BOOLEAN                           UseBis,\r
+  IN  EFI_IP_ADDRESS                    *DestIp,\r
+  IN  UINT16                            IpCount,\r
+  IN  EFI_PXE_BASE_CODE_SRVLIST         *SrvList\r
+  );\r
+\r
+\r
+/**\r
+  Load boot file into user buffer.\r
+\r
+  @param[in]      Private           Pointer to PxeBc private data.\r
+  @param[in, out] BufferSize        Size of user buffer for input;\r
+                                    required buffer size for output.\r
+  @param[in]      Buffer            Pointer to user buffer.\r
+\r
+  @retval EFI_SUCCESS          Successfully obtained all the boot information.\r
+  @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
+  @retval EFI_ABORTED          User cancelled the current operation.\r
+  @retval Others               Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcLoadBootFile (\r
+  IN     PXEBC_PRIVATE_DATA           *Private,\r
+  IN OUT UINTN                        *BufferSize,\r
+  IN     VOID                         *Buffer         OPTIONAL\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c
new file mode 100644 (file)
index 0000000..08415d9
--- /dev/null
@@ -0,0 +1,1599 @@
+/** @file\r
+  Functions implementation related with DHCPv4 for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+//\r
+// This is a map from the interested DHCP4 option tags' index to the tag value.\r
+//\r
+UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {\r
+  PXEBC_DHCP4_TAG_BOOTFILE_LEN,\r
+  PXEBC_DHCP4_TAG_VENDOR,\r
+  PXEBC_DHCP4_TAG_OVERLOAD,\r
+  PXEBC_DHCP4_TAG_MSG_TYPE,\r
+  PXEBC_DHCP4_TAG_SERVER_ID,\r
+  PXEBC_DHCP4_TAG_CLASS_ID,\r
+  PXEBC_DHCP4_TAG_BOOTFILE\r
+};\r
+\r
+//\r
+// There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec.\r
+//\r
+UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32};\r
+\r
+\r
+/**\r
+  Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer.\r
+\r
+  @param[in]  Buffer              Pointer to the option buffer.\r
+  @param[in]  Length              Length of the option buffer.\r
+  @param[in]  OptTag              Tag of the required option.\r
+\r
+  @retval     NULL                Failed to find the required option.\r
+  @retval     Others              The position of the required option.\r
+\r
+**/\r
+EFI_DHCP4_PACKET_OPTION *\r
+PxeBcParseDhcp4Options (\r
+  IN UINT8                      *Buffer,\r
+  IN UINT32                     Length,\r
+  IN UINT8                      OptTag\r
+  )\r
+{\r
+  EFI_DHCP4_PACKET_OPTION       *Option;\r
+  UINT32                        Offset;\r
+\r
+  Option  = (EFI_DHCP4_PACKET_OPTION *) Buffer;\r
+  Offset  = 0;\r
+\r
+  while (Offset < Length && Option->OpCode != PXEBC_DHCP4_TAG_EOP) {\r
+\r
+    if (Option->OpCode == OptTag) {\r
+      //\r
+      // Found the required option.\r
+      //\r
+      return Option;\r
+    }\r
+\r
+    //\r
+    // Skip the current option to the next.\r
+    //\r
+    if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) {\r
+      Offset++;\r
+    } else {\r
+      Offset += Option->Length + 2;\r
+    }\r
+\r
+    Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Parse the PXE vender options and extract the information from them.\r
+\r
+  @param[in]  Dhcp4Option        Pointer to vendor options in buffer.\r
+  @param[in]  VendorOption       Pointer to structure to store information in vendor options.\r
+\r
+**/\r
+VOID\r
+PxeBcParseVendorOptions (\r
+  IN EFI_DHCP4_PACKET_OPTION    *Dhcp4Option,\r
+  IN PXEBC_VENDOR_OPTION        *VendorOption\r
+  )\r
+{\r
+  UINT32                        *BitMap;\r
+  UINT8                         VendorOptionLen;\r
+  EFI_DHCP4_PACKET_OPTION       *PxeOption;\r
+  UINT8                         Offset;\r
+\r
+  BitMap          = VendorOption->BitMap;\r
+  VendorOptionLen = Dhcp4Option->Length;\r
+  PxeOption       = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0];\r
+  Offset          = 0;\r
+\r
+  ASSERT (PxeOption != NULL);\r
+\r
+  while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) {\r
+    //\r
+    // Parse all the interesting PXE vendor options one by one.\r
+    //\r
+    switch (PxeOption->OpCode) {\r
+\r
+    case PXEBC_VENDOR_TAG_MTFTP_IP:\r
+\r
+      CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_MTFTP_CPORT:\r
+\r
+      CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort));\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_MTFTP_SPORT:\r
+\r
+      CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort));\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT:\r
+\r
+      VendorOption->MtftpTimeout = *PxeOption->Data;\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_MTFTP_DELAY:\r
+\r
+      VendorOption->MtftpDelay = *PxeOption->Data;\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_DISCOVER_CTRL:\r
+\r
+      VendorOption->DiscoverCtrl = *PxeOption->Data;\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_DISCOVER_MCAST:\r
+\r
+      CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_BOOT_SERVERS:\r
+\r
+      VendorOption->BootSvrLen  = PxeOption->Length;\r
+      VendorOption->BootSvr     = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data;\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_BOOT_MENU:\r
+\r
+      VendorOption->BootMenuLen = PxeOption->Length;\r
+      VendorOption->BootMenu    = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data;\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_MENU_PROMPT:\r
+\r
+      VendorOption->MenuPromptLen = PxeOption->Length;\r
+      VendorOption->MenuPrompt    = (PXEBC_MENU_PROMPT *) PxeOption->Data;\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_MCAST_ALLOC:\r
+\r
+      CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));\r
+      CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock));\r
+      CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange));\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES:\r
+\r
+      VendorOption->CredTypeLen = PxeOption->Length;\r
+      VendorOption->CredType    = (UINT32 *) PxeOption->Data;\r
+      break;\r
+\r
+    case PXEBC_VENDOR_TAG_BOOT_ITEM:\r
+\r
+      CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType));\r
+      CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer));\r
+      break;\r
+\r
+    default:\r
+      //\r
+      // Not interesting PXE vendor options.\r
+      //\r
+      break;\r
+    }\r
+\r
+    //\r
+    // Set the bit map for the special PXE options.\r
+    //\r
+    SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode);\r
+\r
+    //\r
+    // Continue to the next option.\r
+    //\r
+    if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) {\r
+      Offset++;\r
+    } else {\r
+      Offset = (UINT8) (Offset + PxeOption->Length + 2);\r
+    }\r
+\r
+    PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Build the options buffer for the DHCPv4 request packet.\r
+\r
+  @param[in]  Private             Pointer to PxeBc private data.\r
+  @param[out] OptList             Pointer to the option pointer array.\r
+  @param[in]  Buffer              Pointer to the buffer to contain the option list.\r
+  @param[in]  NeedMsgType         If TRUE, it is necessary to include the Msg type option.\r
+                                  Otherwise, it is not necessary.\r
+\r
+  @return     Index               The count of the built-in options.\r
+\r
+**/\r
+UINT32\r
+PxeBcBuildDhcp4Options (\r
+  IN  PXEBC_PRIVATE_DATA       *Private,\r
+  OUT EFI_DHCP4_PACKET_OPTION  **OptList,\r
+  IN  UINT8                    *Buffer,\r
+  IN  BOOLEAN                  NeedMsgType\r
+  )\r
+{\r
+  UINT32                       Index;\r
+  PXEBC_DHCP4_OPTION_ENTRY     OptEnt;\r
+  UINT16                       Value;\r
+\r
+  Index      = 0;\r
+  OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer;\r
+\r
+  if (NeedMsgType) {\r
+    //\r
+    // Append message type.\r
+    //\r
+    OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_MSG_TYPE;\r
+    OptList[Index]->Length  = 1;\r
+    OptEnt.Mesg             = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data;\r
+    OptEnt.Mesg->Type       = PXEBC_DHCP4_MSG_TYPE_REQUEST;\r
+    Index++;\r
+    OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+    //\r
+    // Append max message size.\r
+    //\r
+    OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_MAXMSG;\r
+    OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE);\r
+    OptEnt.MaxMesgSize      = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data;\r
+    Value                   = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);\r
+    CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16));\r
+    Index++;\r
+    OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+  }\r
+\r
+  //\r
+  // Append parameter request list option.\r
+  //\r
+  OptList[Index]->OpCode    = PXEBC_DHCP4_TAG_PARA_LIST;\r
+  OptList[Index]->Length    = 35;\r
+  OptEnt.Para               = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;\r
+  OptEnt.Para->ParaList[0]  = PXEBC_DHCP4_TAG_NETMASK;\r
+  OptEnt.Para->ParaList[1]  = PXEBC_DHCP4_TAG_TIME_OFFSET;\r
+  OptEnt.Para->ParaList[2]  = PXEBC_DHCP4_TAG_ROUTER;\r
+  OptEnt.Para->ParaList[3]  = PXEBC_DHCP4_TAG_TIME_SERVER;\r
+  OptEnt.Para->ParaList[4]  = PXEBC_DHCP4_TAG_NAME_SERVER;\r
+  OptEnt.Para->ParaList[5]  = PXEBC_DHCP4_TAG_DNS_SERVER;\r
+  OptEnt.Para->ParaList[6]  = PXEBC_DHCP4_TAG_HOSTNAME;\r
+  OptEnt.Para->ParaList[7]  = PXEBC_DHCP4_TAG_BOOTFILE_LEN;\r
+  OptEnt.Para->ParaList[8]  = PXEBC_DHCP4_TAG_DOMAINNAME;\r
+  OptEnt.Para->ParaList[9]  = PXEBC_DHCP4_TAG_ROOTPATH;\r
+  OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH;\r
+  OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU;\r
+  OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL;\r
+  OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST;\r
+  OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN;\r
+  OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER;\r
+  OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER;\r
+  OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR;\r
+  OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP;\r
+  OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE;\r
+  OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID;\r
+  OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1;\r
+  OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2;\r
+  OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID;\r
+  OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP;\r
+  OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE;\r
+  OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID;\r
+  OptEnt.Para->ParaList[27] = 0x80;\r
+  OptEnt.Para->ParaList[28] = 0x81;\r
+  OptEnt.Para->ParaList[29] = 0x82;\r
+  OptEnt.Para->ParaList[30] = 0x83;\r
+  OptEnt.Para->ParaList[31] = 0x84;\r
+  OptEnt.Para->ParaList[32] = 0x85;\r
+  OptEnt.Para->ParaList[33] = 0x86;\r
+  OptEnt.Para->ParaList[34] = 0x87;\r
+  Index++;\r
+  OptList[Index]            = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+  //\r
+  // Append UUID/Guid-based client identifier option\r
+  //\r
+  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_UUID;\r
+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID);\r
+  OptEnt.Uuid             = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data;\r
+  OptEnt.Uuid->Type       = 0;\r
+  Index++;\r
+  OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+  if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {\r
+    //\r
+    // Zero the Guid to indicate NOT programable if failed to get system Guid.\r
+    //\r
+    ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));\r
+  }\r
+\r
+  //\r
+  // Append client network device interface option\r
+  //\r
+  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_UNDI;\r
+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI);\r
+  OptEnt.Undi             = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data;\r
+\r
+  if (Private->Nii != NULL) {\r
+    OptEnt.Undi->Type     = Private->Nii->Type;\r
+    OptEnt.Undi->MajorVer = Private->Nii->MajorVer;\r
+    OptEnt.Undi->MinorVer = Private->Nii->MinorVer;\r
+  } else {\r
+    OptEnt.Undi->Type     = DEFAULT_UNDI_TYPE;\r
+    OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;\r
+    OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;\r
+  }\r
+\r
+  Index++;\r
+  OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+  //\r
+  // Append client system architecture option\r
+  //\r
+  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_ARCH;\r
+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH);\r
+  OptEnt.Arch             = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data;\r
+  Value                   = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);\r
+  CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));\r
+  Index++;\r
+  OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+  //\r
+  // Append vendor class identify option\r
+  //\r
+  OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_CLASS_ID;\r
+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID);\r
+  OptEnt.Clid             = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data;\r
+  CopyMem (\r
+    OptEnt.Clid,\r
+    DEFAULT_CLASS_ID_DATA,\r
+    sizeof (PXEBC_DHCP4_OPTION_CLID)\r
+    );\r
+  PxeBcUintnToAscDecWithFormat (\r
+    EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,\r
+    OptEnt.Clid->ArchitectureType,\r
+    sizeof (OptEnt.Clid->ArchitectureType)\r
+    );\r
+\r
+  if (Private->Nii != NULL) {\r
+    CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));\r
+    PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));\r
+    PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));\r
+  }\r
+\r
+  Index++;\r
+\r
+  return Index;\r
+}\r
+\r
+\r
+/**\r
+  Create a template DHCPv4 packet as a seed.\r
+\r
+  @param[out] Seed           Pointer to the seed packet.\r
+  @param[in]  Udp4           Pointer to EFI_UDP4_PROTOCOL.\r
+\r
+**/\r
+VOID\r
+PxeBcSeedDhcp4Packet (\r
+  OUT EFI_DHCP4_PACKET       *Seed,\r
+  IN  EFI_UDP4_PROTOCOL      *Udp4\r
+  )\r
+{\r
+  EFI_SIMPLE_NETWORK_MODE    Mode;\r
+  EFI_DHCP4_HEADER           *Header;\r
+\r
+  //\r
+  // Get IfType and HwAddressSize from SNP mode data.\r
+  //\r
+  Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode);\r
+\r
+  Seed->Size            = sizeof (EFI_DHCP4_PACKET);\r
+  Seed->Length          = sizeof (Seed->Dhcp4);\r
+  Header                = &Seed->Dhcp4.Header;\r
+  ZeroMem (Header, sizeof (EFI_DHCP4_HEADER));\r
+  Header->OpCode        = PXEBC_DHCP4_OPCODE_REQUEST;\r
+  Header->HwType        = Mode.IfType;\r
+  Header->HwAddrLen     = (UINT8) Mode.HwAddressSize;\r
+  CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen);\r
+\r
+  Seed->Dhcp4.Magik     = PXEBC_DHCP4_MAGIC;\r
+  Seed->Dhcp4.Option[0] = PXEBC_DHCP4_TAG_EOP;\r
+}\r
+\r
+\r
+/**\r
+  Cache the DHCPv4 packet.\r
+\r
+  @param[in]  Dst          Pointer to the cache buffer for DHCPv4 packet.\r
+  @param[in]  Src          Pointer to the DHCPv4 packet to be cached.\r
+\r
+**/\r
+VOID\r
+PxeBcCacheDhcp4Packet (\r
+  IN EFI_DHCP4_PACKET     *Dst,\r
+  IN EFI_DHCP4_PACKET     *Src\r
+  )\r
+{\r
+  ASSERT (Dst->Size >= Src->Length);\r
+\r
+  CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);\r
+  Dst->Length = Src->Length;\r
+}\r
+\r
+\r
+/**\r
+  Parse the cached DHCPv4 packet, including all the options.\r
+\r
+  @param[in]  Cache4           Pointer to cached DHCPv4 packet.\r
+\r
+  @retval     EFI_SUCCESS      Parsed the DHCPv4 packet successfully.\r
+  @retval     EFI_DEVICE_ERROR Failed to parse and invalid packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcParseDhcp4Packet (\r
+  IN PXEBC_DHCP4_PACKET_CACHE    *Cache4\r
+  )\r
+{\r
+  EFI_DHCP4_PACKET               *Offer;\r
+  EFI_DHCP4_PACKET_OPTION        **Options;\r
+  EFI_DHCP4_PACKET_OPTION        *Option;\r
+  PXEBC_OFFER_TYPE               OfferType;\r
+  UINTN                          Index;\r
+  BOOLEAN                        IsProxyOffer;\r
+  BOOLEAN                        IsPxeOffer;\r
+  UINT8                          *Ptr8;\r
+\r
+  IsProxyOffer = FALSE;\r
+  IsPxeOffer   = FALSE;\r
+\r
+  ZeroMem (Cache4->OptList, sizeof (Cache4->OptList));\r
+  ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt));\r
+\r
+  Offer   = &Cache4->Packet.Offer;\r
+  Options = Cache4->OptList;\r
+\r
+  //\r
+  // Parse DHCPv4 options in this offer, and store the pointers.\r
+  //\r
+  for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {\r
+    Options[Index] = PxeBcParseDhcp4Options (\r
+                       Offer->Dhcp4.Option,\r
+                       GET_OPTION_BUFFER_LEN (Offer),\r
+                       mInterestedDhcp4Tags[Index]\r
+                       );\r
+  }\r
+\r
+  //\r
+  // The offer with "yiaddr" is a proxy offer.\r
+  //\r
+  if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) {\r
+    IsProxyOffer = TRUE;\r
+  }\r
+\r
+  //\r
+  // The offer with "PXEClient" is a PXE offer.\r
+  //\r
+  Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID];\r
+  if ((Option != NULL) && (Option->Length >= 9) &&\r
+      (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {\r
+    IsPxeOffer = TRUE;\r
+  }\r
+\r
+  //\r
+  // Parse PXE vendor options in this offer, and store the contents/pointers.\r
+  //\r
+  Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR];\r
+  if (IsPxeOffer && Option != NULL) {\r
+    PxeBcParseVendorOptions (Option, &Cache4->VendorOpt);\r
+  }\r
+\r
+  //\r
+  // Check whether bootfilename and serverhostname overloaded, refers to rfc-2132 in details.\r
+  // If overloaded, parse the buffer as nested DHCPv4 options, or else just parse as bootfilename\r
+  // and serverhostname option.\r
+  //\r
+  Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD];\r
+  if (Option != NULL && (Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) {\r
+\r
+    Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = PxeBcParseDhcp4Options (\r
+                                                (UINT8 *) Offer->Dhcp4.Header.BootFileName,\r
+                                                sizeof (Offer->Dhcp4.Header.BootFileName),\r
+                                                PXEBC_DHCP4_TAG_BOOTFILE\r
+                                                );\r
+    //\r
+    // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null\r
+    // terminated string. So force to append null terminated character at the end of string.\r
+    //\r
+    if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {\r
+      Ptr8 =  (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];\r
+      Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length;\r
+      *Ptr8 =  '\0';\r
+    }\r
+\r
+  } else if ((Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) &&\r
+            (Offer->Dhcp4.Header.BootFileName[0] != 0)) {\r
+    //\r
+    // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it.\r
+    // Do not count dhcp option header here, or else will destory the serverhostname.\r
+    //\r
+    Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *)\r
+                                                (&Offer->Dhcp4.Header.BootFileName[0] -\r
+                                                OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));\r
+\r
+  }\r
+\r
+  //\r
+  // Determine offer type of the DHCPv4 packet.\r
+  //\r
+  Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE];\r
+  if (Option == NULL || Option->Data[0] == 0) {\r
+    //\r
+    // It's a Bootp offer.\r
+    //\r
+    OfferType = PxeOfferTypeBootp;\r
+\r
+    Option    = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE];\r
+    if (Option == NULL) {\r
+      //\r
+      // If the Bootp offer without bootfilename, discard it.\r
+      //\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  } else {\r
+\r
+    if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {\r
+      //\r
+      // It's a PXE10 offer with PXEClient and discover vendor option.\r
+      //\r
+      OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10;\r
+    } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {\r
+      //\r
+      // It's a WFM11a offer with PXEClient and mtftp vendor option.\r
+      // But multi-cast download is not supported currently, so discard it.\r
+      //\r
+      return EFI_DEVICE_ERROR;\r
+    } else if (IsPxeOffer) {\r
+      //\r
+      // It's a BINL offer only with PXEClient.\r
+      //\r
+      OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;\r
+    } else {\r
+      //\r
+      // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet.\r
+      //\r
+      OfferType = PxeOfferTypeDhcpOnly;\r
+    }\r
+  }\r
+\r
+  Cache4->OfferType = OfferType;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Cache the DHCPv4 ack packet, and parse it on demand.\r
+\r
+  @param[in]  Private             Pointer to PxeBc private data.\r
+  @param[in]  Ack                 Pointer to the DHCPv4 ack packet.\r
+  @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.\r
+\r
+**/\r
+VOID\r
+PxeBcCopyDhcp4Ack (\r
+  IN PXEBC_PRIVATE_DATA   *Private,\r
+  IN EFI_DHCP4_PACKET     *Ack,\r
+  IN BOOLEAN              Verified\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+\r
+  Mode = Private->PxeBc.Mode;\r
+\r
+  PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack);\r
+\r
+  if (Verified) {\r
+    //\r
+    // Parse the ack packet and store it into mode data if needed.\r
+    //\r
+    PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4);\r
+    CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length);\r
+    Mode->DhcpAckReceived = TRUE;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Cache the DHCPv4 proxy offer packet according to the received order.\r
+\r
+  @param[in]  Private               Pointer to PxeBc private data.\r
+  @param[in]  OfferIndex            The received order of offer packets.\r
+\r
+**/\r
+VOID\r
+PxeBcCopyProxyOffer (\r
+  IN PXEBC_PRIVATE_DATA   *Private,\r
+  IN UINT32               OfferIndex\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+  EFI_DHCP4_PACKET        *Offer;\r
+\r
+  ASSERT (OfferIndex < Private->OfferNum);\r
+  ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);\r
+\r
+  Mode  = Private->PxeBc.Mode;\r
+  Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer;\r
+\r
+  //\r
+  // Cache the proxy offer packet and parse it.\r
+  //\r
+  PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer);\r
+  PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4);\r
+\r
+  //\r
+  // Store this packet into mode data.\r
+  //\r
+  CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length);\r
+  Mode->ProxyOfferReceived = TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Retry to request bootfile name by the BINL offer.\r
+\r
+  @param[in]  Private              Pointer to PxeBc private data.\r
+  @param[in]  Index                The received order of offer packets.\r
+\r
+  @retval     EFI_SUCCESS          Successfully retried to request bootfile name.\r
+  @retval     EFI_DEVICE_ERROR     Failed to retry bootfile name.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcRetryBinlOffer (\r
+  IN PXEBC_PRIVATE_DATA     *Private,\r
+  IN UINT32                 Index\r
+  )\r
+{\r
+  EFI_DHCP4_PACKET          *Offer;\r
+  EFI_IP_ADDRESS            ServerIp;\r
+  EFI_STATUS                Status;\r
+  PXEBC_DHCP4_PACKET_CACHE  *Cache4;\r
+  EFI_DHCP4_PACKET          *Reply;\r
+\r
+  ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
+  ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl ||\r
+          Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl);\r
+\r
+  Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;\r
+\r
+  //\r
+  // Prefer to siaddr in header as next server address. If it's zero, then use option 54.\r
+  //\r
+  if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) {\r
+    CopyMem (\r
+      &ServerIp.Addr[0],\r
+      Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+  } else {\r
+    CopyMem (\r
+      &ServerIp.Addr[0],\r
+      &Offer->Dhcp4.Header.ServerAddr,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+  }\r
+\r
+  Private->IsDoDiscover = FALSE;\r
+  Cache4                = &Private->ProxyOffer.Dhcp4;\r
+  Reply                 = &Cache4->Packet.Offer;\r
+\r
+  //\r
+  // Send another request packet for bootfile name.\r
+  //\r
+  Status = PxeBcDhcp4Discover (\r
+             Private,\r
+             0,\r
+             NULL,\r
+             FALSE,\r
+             &ServerIp,\r
+             0,\r
+             NULL\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Parse the reply for the last request packet.\r
+  //\r
+  Status = PxeBcParseDhcp4Packet (Cache4);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Cache4->OfferType != PxeOfferTypeProxyPxe10 &&\r
+      Cache4->OfferType != PxeOfferTypeProxyWfm11a &&\r
+      Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {\r
+    //\r
+    // This BINL ack doesn't have discovery option set or multicast option set\r
+    // or bootfile name specified.\r
+    //\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Store the reply into mode data.\r
+  //\r
+  Private->PxeBc.Mode->ProxyOfferReceived = TRUE;\r
+  CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount.\r
+\r
+  @param[in]  Private               Pointer to PxeBc private data.\r
+  @param[in]  RcvdOffer             Pointer to the received offer packet.\r
+\r
+**/\r
+VOID\r
+PxeBcCacheDhcp4Offer (\r
+  IN PXEBC_PRIVATE_DATA     *Private,\r
+  IN EFI_DHCP4_PACKET       *RcvdOffer\r
+  )\r
+{\r
+  PXEBC_DHCP4_PACKET_CACHE  *Cache4;\r
+  EFI_DHCP4_PACKET          *Offer;\r
+  PXEBC_OFFER_TYPE          OfferType;\r
+\r
+  ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM);\r
+  Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;\r
+  Offer  = &Cache4->Packet.Offer;\r
+\r
+  //\r
+  // Cache the content of DHCPv4 packet firstly.\r
+  //\r
+  PxeBcCacheDhcp4Packet (Offer, RcvdOffer);\r
+\r
+  //\r
+  // Validate the DHCPv4 packet, and parse the options and offer type.\r
+  //\r
+  if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) {\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.\r
+  //\r
+  OfferType = Cache4->OfferType;\r
+  ASSERT (OfferType < PxeOfferTypeMax);\r
+\r
+  if (OfferType == PxeOfferTypeBootp) {\r
+    //\r
+    // It's a Bootp offer, only cache the first one, and discard the others.\r
+    //\r
+    if (Private->OfferCount[OfferType] == 0) {\r
+      Private->OfferIndex[OfferType][0] = Private->OfferNum;\r
+      Private->OfferCount[OfferType]    = 1;\r
+    } else {\r
+      return;\r
+    }\r
+  } else {\r
+    ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);\r
+    if (IS_PROXY_DHCP_OFFER (Offer)) {\r
+      //\r
+      // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.\r
+      //\r
+      Private->IsProxyRecved = TRUE;\r
+\r
+      if (OfferType == PxeOfferTypeProxyBinl) {\r
+        //\r
+        // Cache all proxy BINL offers.\r
+        //\r
+        Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
+        Private->OfferCount[OfferType]++;\r
+      } else if (Private->OfferCount[OfferType] > 0) {\r
+        //\r
+        // Only cache the first PXE10/WFM11a offer, and discard the others.\r
+        //\r
+        Private->OfferIndex[OfferType][0] = Private->OfferNum;\r
+        Private->OfferCount[OfferType]    = 1;\r
+      } else {\r
+        return ;\r
+      }\r
+    } else {\r
+      //\r
+      // It's a DHCPv4 offer with yiaddr, and cache them all.\r
+      //\r
+      Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
+      Private->OfferCount[OfferType]++;\r
+    }\r
+  }\r
+\r
+  Private->OfferNum++;\r
+}\r
+\r
+\r
+/**\r
+  Select an DHCPv4 offer, and record SelectIndex and SelectProxyType.\r
+\r
+  @param[in]  Private             Pointer to PxeBc private data.\r
+\r
+**/\r
+VOID\r
+PxeBcSelectDhcp4Offer (\r
+  IN PXEBC_PRIVATE_DATA       *Private\r
+  )\r
+{\r
+  UINT32                      Index;\r
+  UINT32                      OfferIndex;\r
+  EFI_DHCP4_PACKET            *Offer;\r
+\r
+  Private->SelectIndex = 0;\r
+\r
+  if (Private->IsOfferSorted) {\r
+    //\r
+    // Select offer by default policy.\r
+    //\r
+    if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {\r
+      //\r
+      // 1. DhcpPxe10 offer\r
+      //\r
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {\r
+      //\r
+      // 2. DhcpWfm11a offer\r
+      //\r
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+               Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {\r
+      //\r
+      // 3. DhcpOnly offer and ProxyPxe10 offer.\r
+      //\r
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+      Private->SelectProxyType = PxeOfferTypeProxyPxe10;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+               Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {\r
+      //\r
+      // 4. DhcpOnly offer and ProxyWfm11a offer.\r
+      //\r
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+      Private->SelectProxyType = PxeOfferTypeProxyWfm11a;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {\r
+      //\r
+      // 5. DhcpBinl offer.\r
+      //\r
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+               Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {\r
+      //\r
+      // 6. DhcpOnly offer and ProxyBinl offer.\r
+      //\r
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+      Private->SelectProxyType = PxeOfferTypeProxyBinl;\r
+\r
+    } else {\r
+      //\r
+      // 7. DhcpOnly offer with bootfilename.\r
+      //\r
+      for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {\r
+        OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];\r
+        if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {\r
+          Private->SelectIndex = OfferIndex + 1;\r
+          break;\r
+        }\r
+      }\r
+      //\r
+      // 8. Bootp offer with bootfilename.\r
+      //\r
+      OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0];\r
+      if (Private->SelectIndex == 0 &&\r
+          Private->OfferCount[PxeOfferTypeBootp] > 0 &&\r
+          Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {\r
+        Private->SelectIndex = OfferIndex + 1;\r
+      }\r
+    }\r
+  } else {\r
+    //\r
+    // Select offer by received order.\r
+    //\r
+    for (Index = 0; Index < Private->OfferNum; Index++) {\r
+\r
+      Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;\r
+\r
+      if (IS_PROXY_DHCP_OFFER (Offer)) {\r
+        //\r
+        // Skip proxy offers\r
+        //\r
+        continue;\r
+      }\r
+\r
+      if (!Private->IsProxyRecved &&\r
+          Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly &&\r
+          Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {\r
+        //\r
+        // Skip if DhcpOnly offer without any other proxy offers or bootfilename.\r
+        //\r
+        continue;\r
+      }\r
+\r
+      //\r
+      // Record the index of the select offer.\r
+      //\r
+      Private->SelectIndex = Index + 1;\r
+      break;\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Handle the DHCPv4 offer packet.\r
+\r
+  @param[in]  Private             Pointer to PxeBc private data.\r
+\r
+  @retval     EFI_SUCCESS         Handled the DHCPv4 offer packet successfully.\r
+  @retval     EFI_NO_RESPONSE     No response to the following request packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcHandleDhcp4Offer (\r
+  IN PXEBC_PRIVATE_DATA     *Private\r
+  )\r
+{\r
+  PXEBC_DHCP4_PACKET_CACHE  *Cache4;\r
+  EFI_DHCP4_PACKET_OPTION   **Options;\r
+  UINT32                    Index;\r
+  EFI_DHCP4_PACKET          *Offer;\r
+  PXEBC_OFFER_TYPE          OfferType;\r
+  UINT32                    ProxyIndex;\r
+  UINT32                    SelectIndex;\r
+  EFI_STATUS                Status;\r
+  EFI_PXE_BASE_CODE_MODE    *Mode;\r
+  EFI_DHCP4_PACKET          *Ack;\r
+\r
+  ASSERT (Private->SelectIndex > 0);\r
+  SelectIndex = (UINT32) (Private->SelectIndex - 1);\r
+  ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);\r
+  Cache4      = &Private->OfferBuffer[SelectIndex].Dhcp4;\r
+  Options     = Cache4->OptList;\r
+  Status      = EFI_SUCCESS;\r
+\r
+  if (Cache4->OfferType == PxeOfferTypeDhcpBinl) {\r
+    //\r
+    // DhcpBinl offer is selected, so need try to request bootfilename by this offer.\r
+    //\r
+    if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) {\r
+      Status = EFI_NO_RESPONSE;\r
+    }\r
+  } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) {\r
+\r
+    if (Private->IsProxyRecved) {\r
+      //\r
+      // DhcpOnly offer is selected, so need try to request bootfile name.\r
+      //\r
+      ProxyIndex = 0;\r
+      if (Private->IsOfferSorted) {\r
+        //\r
+        // The proxy offer should be determined if select by default policy.\r
+        // IsOfferSorted means all offers are labeled by OfferIndex.\r
+        //\r
+        ASSERT (Private->SelectProxyType < PxeOfferTypeMax);\r
+        ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);\r
+\r
+        if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {\r
+          //\r
+          // Try all the cached ProxyBinl offer one by one to request bootfile name.\r
+          //\r
+          for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {\r
+            ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
+            ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];\r
+            if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) {\r
+              break;\r
+            }\r
+          }\r
+          if (Index == Private->OfferCount[Private->SelectProxyType]) {\r
+            Status = EFI_NO_RESPONSE;\r
+          }\r
+        } else {\r
+          //\r
+          // For other proxy offers, only one is buffered.\r
+          //\r
+          ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
+        }\r
+      } else {\r
+        //\r
+        // The proxy offer should not be determined if select by received order.\r
+        //\r
+        Status = EFI_NO_RESPONSE;\r
+\r
+        for (Index = 0; Index < Private->OfferNum; Index++) {\r
+          ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
+          Offer     = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;\r
+          OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType;\r
+          if (!IS_PROXY_DHCP_OFFER (Offer)) {\r
+            //\r
+            // Skip non proxy DHCPv4 offers.\r
+            //\r
+            continue;\r
+          }\r
+\r
+          if (OfferType == PxeOfferTypeProxyBinl) {\r
+            //\r
+            // Try all the cached ProxyBinl offer one by one to request bootfile name.\r
+            //\r
+            if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) {\r
+              continue;\r
+            }\r
+          }\r
+\r
+          Private->SelectProxyType = OfferType;\r
+          ProxyIndex               = Index;\r
+          Status                   = EFI_SUCCESS;\r
+          break;\r
+        }\r
+      }\r
+\r
+      if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {\r
+        //\r
+        // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.\r
+        //\r
+        PxeBcCopyProxyOffer (Private, ProxyIndex);\r
+      }\r
+    } else {\r
+      //\r
+      //  Othewise, the bootfile name must be included in DhcpOnly offer.\r
+      //\r
+      ASSERT (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);\r
+    }\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // All PXE boot information is ready by now.\r
+    //\r
+    Mode  = Private->PxeBc.Mode;\r
+    Offer = &Cache4->Packet.Offer;\r
+    Ack   = &Private->DhcpAck.Dhcp4.Packet.Ack;\r
+    if (Cache4->OfferType == PxeOfferTypeBootp) {\r
+      //\r
+      // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply\r
+      // should be taken as ack.\r
+      //\r
+      Ack = Offer;\r
+    }\r
+\r
+    PxeBcCopyDhcp4Ack (Private, Ack, TRUE);\r
+    Mode->DhcpDiscoverValid = TRUE;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver\r
+  to intercept events that occurred in the configuration process.\r
+\r
+  @param[in]  This              Pointer to the EFI DHCPv4 Protocol.\r
+  @param[in]  Context           Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure().\r
+  @param[in]  CurrentState      The current operational state of the EFI DHCPv4 Protocol driver.\r
+  @param[in]  Dhcp4Event        The event that occurs in the current state, which usually means a\r
+                                state transition.\r
+  @param[in]  Packet            The DHCPv4 packet that is going to be sent or already received.\r
+  @param[out] NewPacket         The packet that is used to replace the above Packet.\r
+\r
+  @retval EFI_SUCCESS           Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.\r
+  @retval EFI_NOT_READY         Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol\r
+                                driver will continue to wait for more DHCPOFFER packets until the\r
+                                retry timeout expires.\r
+  @retval EFI_ABORTED           Tells the EFI DHCPv4 Protocol driver to abort the current process\r
+                                and return to the Dhcp4Init or Dhcp4InitReboot state.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDhcp4CallBack (\r
+  IN  EFI_DHCP4_PROTOCOL               *This,\r
+  IN  VOID                             *Context,\r
+  IN  EFI_DHCP4_STATE                  CurrentState,\r
+  IN  EFI_DHCP4_EVENT                  Dhcp4Event,\r
+  IN  EFI_DHCP4_PACKET                 *Packet            OPTIONAL,\r
+  OUT EFI_DHCP4_PACKET                 **NewPacket        OPTIONAL\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA                   *Private;\r
+  EFI_PXE_BASE_CODE_MODE               *Mode;\r
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  *Callback;\r
+  EFI_DHCP4_PACKET_OPTION              *MaxMsgSize;\r
+  UINT16                               Value;\r
+  EFI_STATUS                           Status;\r
+  BOOLEAN                              Received;\r
+\r
+  if ((Dhcp4Event != Dhcp4RcvdOffer) &&\r
+      (Dhcp4Event != Dhcp4SelectOffer) &&\r
+      (Dhcp4Event != Dhcp4SendDiscover) &&\r
+      (Dhcp4Event != Dhcp4RcvdAck)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Private   = (PXEBC_PRIVATE_DATA *) Context;\r
+  Mode      = Private->PxeBc.Mode;\r
+  Callback  = Private->PxeBcCallback;\r
+\r
+  //\r
+  // Override the Maximum DHCP Message Size.\r
+  //\r
+  MaxMsgSize = PxeBcParseDhcp4Options (\r
+                 Packet->Dhcp4.Option,\r
+                 GET_OPTION_BUFFER_LEN (Packet),\r
+                 PXEBC_DHCP4_TAG_MAXMSG\r
+                 );\r
+  if (MaxMsgSize != NULL) {\r
+    Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);\r
+    CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));\r
+  }\r
+\r
+  //\r
+  // Callback to user if any packets sent or received.\r
+  //\r
+  if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) {\r
+    Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck);\r
+    Status = Callback->Callback (\r
+                         Callback,\r
+                         Private->Function,\r
+                         Received,\r
+                         Packet->Length,\r
+                         (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4\r
+                         );\r
+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+      return EFI_ABORTED;\r
+    }\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  switch (Dhcp4Event) {\r
+\r
+  case Dhcp4SendDiscover:\r
+    //\r
+    // Cache the DHCPv4 discover packet to mode data directly.\r
+    // It need to check SendGuid as well as Dhcp4SendRequest.\r
+    //\r
+    CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length);\r
+\r
+  case Dhcp4SendRequest:\r
+    if (Mode->SendGUID) {\r
+      //\r
+      // Send the system Guid instead of the MAC address as the hardware address if required.\r
+      //\r
+      if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) {\r
+        //\r
+        // Zero the Guid to indicate NOT programable if failed to get system Guid.\r
+        //\r
+        ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));\r
+      }\r
+      Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID);\r
+    }\r
+    break;\r
+\r
+  case Dhcp4RcvdOffer:\r
+    Status = EFI_NOT_READY;\r
+    if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {\r
+      //\r
+      // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record\r
+      // the OfferIndex and OfferCount.\r
+      //\r
+      PxeBcCacheDhcp4Offer (Private, Packet);\r
+    }\r
+    break;\r
+\r
+  case Dhcp4SelectOffer:\r
+    //\r
+    // Select offer by the default policy or by order, and record the SelectIndex\r
+    // and SelectProxyType.\r
+    //\r
+    PxeBcSelectDhcp4Offer (Private);\r
+\r
+    if (Private->SelectIndex == 0) {\r
+      Status = EFI_ABORTED;\r
+    } else {\r
+      *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer;\r
+    }\r
+    break;\r
+\r
+  case Dhcp4RcvdAck:\r
+    //\r
+    // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data\r
+    // without verification.\r
+    //\r
+    ASSERT (Private->SelectIndex != 0);\r
+\r
+    PxeBcCopyDhcp4Ack (Private, Packet, FALSE);\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Build and send out the request packet for the bootfile, and parse the reply.\r
+\r
+  @param[in]  Private               Pointer to PxeBc private data.\r
+  @param[in]  Type                  PxeBc option boot item type.\r
+  @param[in]  Layer                 Pointer to option boot item layer.\r
+  @param[in]  UseBis                Use BIS or not.\r
+  @param[in]  DestIp                Pointer to the server address.\r
+  @param[in]  IpCount               The total count of the server address.\r
+  @param[in]  SrvList               Pointer to EFI_PXE_BASE_CODE_SRVLIST.\r
+\r
+  @retval     EFI_SUCCESS           Successfully discovered boot file.\r
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.\r
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.\r
+  @retval     Others                Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4Discover (\r
+  IN  PXEBC_PRIVATE_DATA              *Private,\r
+  IN  UINT16                          Type,\r
+  IN  UINT16                          *Layer,\r
+  IN  BOOLEAN                         UseBis,\r
+  IN  EFI_IP_ADDRESS                  *DestIp,\r
+  IN  UINT16                          IpCount,\r
+  IN  EFI_PXE_BASE_CODE_SRVLIST       *SrvList\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT          Sport;\r
+  EFI_PXE_BASE_CODE_MODE              *Mode;\r
+  EFI_DHCP4_PROTOCOL                  *Dhcp4;\r
+  EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN    Token;\r
+  BOOLEAN                             IsBCast;\r
+  EFI_STATUS                          Status;\r
+  UINT16                              RepIndex;\r
+  UINT16                              SrvIndex;\r
+  UINT16                              TryIndex;\r
+  EFI_DHCP4_LISTEN_POINT              ListenPoint;\r
+  EFI_DHCP4_PACKET                    *Response;\r
+  UINT8                               Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];\r
+  EFI_DHCP4_PACKET_OPTION             *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];\r
+  UINT32                              OptCount;\r
+  EFI_DHCP4_PACKET_OPTION             *PxeOpt;\r
+  PXEBC_OPTION_BOOT_ITEM              *PxeBootItem;\r
+  UINT8                               VendorOptLen;\r
+  UINT32                              Xid;\r
+\r
+  Mode      = Private->PxeBc.Mode;\r
+  Dhcp4     = Private->Dhcp4;\r
+  Status    = EFI_SUCCESS;\r
+\r
+  ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));\r
+\r
+  //\r
+  // Use broadcast if destination address not specified.\r
+  //\r
+  if (DestIp == NULL) {\r
+    Sport   = PXEBC_DHCP4_S_PORT;\r
+    IsBCast = TRUE;\r
+  } else {\r
+    Sport   = PXEBC_BS_DISCOVER_PORT;\r
+    IsBCast = FALSE;\r
+  }\r
+\r
+  if (!UseBis && Layer != NULL) {\r
+    *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;\r
+  }\r
+\r
+  //\r
+  // Build all the options for the request packet.\r
+  //\r
+  OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE);\r
+\r
+  if (Private->IsDoDiscover) {\r
+    //\r
+    // Add vendor option of PXE_BOOT_ITEM\r
+    //\r
+    VendorOptLen      = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1);\r
+    OptList[OptCount] = AllocateZeroPool (VendorOptLen);\r
+    if (OptList[OptCount] == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    OptList[OptCount]->OpCode     = PXEBC_DHCP4_TAG_VENDOR;\r
+    OptList[OptCount]->Length     = (UINT8) (VendorOptLen - 2);\r
+    PxeOpt                        = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data;\r
+    PxeOpt->OpCode                = PXEBC_VENDOR_TAG_BOOT_ITEM;\r
+    PxeOpt->Length                = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM);\r
+    PxeBootItem                   = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data;\r
+    PxeBootItem->Type             = HTONS (Type);\r
+    PxeOpt->Data[PxeOpt->Length]  = PXEBC_DHCP4_TAG_EOP;\r
+\r
+    if (Layer != NULL) {\r
+      PxeBootItem->Layer          = HTONS (*Layer);\r
+    }\r
+\r
+    OptCount++;\r
+  }\r
+\r
+  //\r
+  // Build the request packet with seed packet and option list.\r
+  //\r
+  Status = Dhcp4->Build (\r
+                    Dhcp4,\r
+                    &Private->SeedPacket,\r
+                    0,\r
+                    NULL,\r
+                    OptCount,\r
+                    OptList,\r
+                    &Token.Packet\r
+                    );\r
+  //\r
+  // Free the vendor option of PXE_BOOT_ITEM.\r
+  //\r
+  if (Private->IsDoDiscover) {\r
+    FreePool (OptList[OptCount - 1]);\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Mode->SendGUID) {\r
+    if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) {\r
+      //\r
+      // Zero the Guid to indicate NOT programable if failed to get system Guid.\r
+      //\r
+      ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));\r
+    }\r
+    Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8)  sizeof (EFI_GUID);\r
+  }\r
+\r
+  //\r
+  // Set fields of the token for the request packet.\r
+  //\r
+  Xid                                 = NET_RANDOM (NetRandomInitSeed ());\r
+  Token.Packet->Dhcp4.Header.Xid      = HTONL (Xid);\r
+  Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0));\r
+  CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Token.RemotePort = Sport;\r
+\r
+  if (IsBCast) {\r
+    SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);\r
+  } else {\r
+    CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));\r
+  }\r
+\r
+  CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  if (!IsBCast) {\r
+    Token.ListenPointCount            = 1;\r
+    Token.ListenPoints                = &ListenPoint;\r
+    Token.ListenPoints[0].ListenPort  = PXEBC_BS_DISCOVER_PORT;\r
+    CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS));\r
+    CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS));\r
+  }\r
+\r
+  //\r
+  // Send out the request packet to discover the bootfile.\r
+  //\r
+  for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) {\r
+\r
+    Token.TimeoutValue                  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex);\r
+    Token.Packet->Dhcp4.Header.Seconds  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1));\r
+\r
+    Status = Dhcp4->TransmitReceive (Dhcp4, &Token);\r
+    if (Token.Status != EFI_TIMEOUT) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) {\r
+    //\r
+    // No server response our PXE request\r
+    //\r
+    Status = EFI_TIMEOUT;\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+\r
+    RepIndex  = 0;\r
+    SrvIndex  = 0;\r
+    Response  = Token.ResponseList;\r
+    //\r
+    // Find the right PXE Reply according to server address.\r
+    //\r
+    while (RepIndex < Token.ResponseCount) {\r
+\r
+      while (SrvIndex < IpCount) {\r
+        if (SrvList[SrvIndex].AcceptAnyResponse) {\r
+          break;\r
+        }\r
+        if ((SrvList[SrvIndex].Type == Type) &&\r
+            EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &Private->ServerIp)) {\r
+          break;\r
+        }\r
+        SrvIndex++;\r
+      }\r
+\r
+      if ((IpCount != SrvIndex) || (IpCount == 0)) {\r
+        break;\r
+      }\r
+\r
+      SrvIndex = 0;\r
+      RepIndex++;\r
+\r
+      Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);\r
+    }\r
+\r
+    if (RepIndex < Token.ResponseCount) {\r
+      //\r
+      // Cache the right PXE reply packet here, set valid flag later.\r
+      // Especially for PXE discover packet, store it into mode data here.\r
+      //\r
+      if (Private->IsDoDiscover) {\r
+        PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response);\r
+        CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length);\r
+      } else {\r
+        PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response);\r
+      }\r
+    } else {\r
+      //\r
+      // Not found the right PXE reply packet.\r
+      //\r
+      Status = EFI_NOT_FOUND;\r
+    }\r
+    if (Token.ResponseList != NULL) {\r
+      FreePool (Token.ResponseList);\r
+    }\r
+  }\r
+\r
+  FreePool (Token.Packet);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.\r
+\r
+  @param[in]  Private           Pointer to PxeBc private data.\r
+  @param[in]  Dhcp4             Pointer to the EFI_DHCP4_PROTOCOL\r
+\r
+  @retval EFI_SUCCESS           The D.O.R.A process successfully finished.\r
+  @retval Others                Failed to finish the D.O.R.A process.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4Dora (\r
+  IN PXEBC_PRIVATE_DATA        *Private,\r
+  IN EFI_DHCP4_PROTOCOL        *Dhcp4\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE       *PxeMode;\r
+  EFI_DHCP4_CONFIG_DATA        Config;\r
+  EFI_DHCP4_MODE_DATA          Mode;\r
+  EFI_DHCP4_PACKET_OPTION      *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];\r
+  UINT8                        Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];\r
+  UINT32                       OptCount;\r
+  EFI_STATUS                   Status;\r
+\r
+  ASSERT (Dhcp4 != NULL);\r
+\r
+  Status   = EFI_SUCCESS;\r
+  PxeMode  = Private->PxeBc.Mode;\r
+\r
+  //\r
+  // Build option list for the request packet.\r
+  //\r
+  OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE);\r
+  ASSERT (OptCount> 0);\r
+\r
+  ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA));\r
+  ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+\r
+  Config.OptionCount      = OptCount;\r
+  Config.OptionList       = OptList;\r
+  Config.Dhcp4Callback    = PxeBcDhcp4CallBack;\r
+  Config.CallbackContext  = Private;\r
+  Config.DiscoverTryCount = PXEBC_DHCP_RETRIES;\r
+  Config.DiscoverTimeout  = mPxeDhcpTimeout;\r
+\r
+  //\r
+  // Configure the DHCPv4 instance for PXE boot.\r
+  //\r
+  Status = Dhcp4->Configure (Dhcp4, &Config);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Initialize the record fields for DHCPv4 offer in private data.\r
+  //\r
+  Private->IsProxyRecved = FALSE;\r
+  Private->OfferNum      = 0;\r
+  ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));\r
+  ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));\r
+\r
+  //\r
+  // Start DHCPv4 D.O.R.A. process to acquire IPv4 address.\r
+  //\r
+  Status = Dhcp4->Start (Dhcp4, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_ICMP_ERROR) {\r
+      PxeMode->IcmpErrorReceived = TRUE;\r
+    }\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Get the acquired IPv4 address and store them.\r
+  //\r
+  Status = Dhcp4->GetModeData (Dhcp4, &Mode);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  ASSERT (Mode.State == Dhcp4Bound);\r
+\r
+  CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+  CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+  CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Status = PxeBcFlushStaionIp (Private, &Private->StationIp, &Private->SubnetMask);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check the selected offer whether BINL retry is needed.\r
+  //\r
+  Status = PxeBcHandleDhcp4Offer (Private);\r
+\r
+  AsciiPrint ("\n  Station IP address is ");\r
+\r
+  PxeBcShowIp4Addr (&Private->StationIp.v4);\r
+\r
+ON_EXIT:\r
+  if (EFI_ERROR (Status)) {\r
+    Dhcp4->Stop (Dhcp4);\r
+    Dhcp4->Configure (Dhcp4, NULL);\r
+  } else {\r
+    ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+    Dhcp4->Configure (Dhcp4, &Config);\r
+    Private->IsAddressOk = TRUE;\r
+  }\r
+\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h
new file mode 100644 (file)
index 0000000..bc21b21
--- /dev/null
@@ -0,0 +1,389 @@
+/** @file\r
+  Functions declaration related with DHCPv4 for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_DHCP4_H__\r
+#define __EFI_PXEBC_DHCP4_H__\r
+\r
+#define PXEBC_DHCP4_OPTION_MAX_NUM         16\r
+#define PXEBC_DHCP4_OPTION_MAX_SIZE        312\r
+#define PXEBC_DHCP4_PACKET_MAX_SIZE        1472\r
+#define PXEBC_DHCP4_S_PORT                 67\r
+#define PXEBC_DHCP4_C_PORT                 68\r
+#define PXEBC_BS_DOWNLOAD_PORT             69\r
+#define PXEBC_BS_DISCOVER_PORT             4011\r
+#define PXEBC_DHCP4_OPCODE_REQUEST         1\r
+#define PXEBC_DHCP4_OPCODE_REPLY           2\r
+#define PXEBC_DHCP4_MSG_TYPE_REQUEST       3\r
+#define PXEBC_DHCP4_MAGIC                  0x63538263 // network byte order\r
+\r
+//\r
+// Dhcp Options\r
+//\r
+#define PXEBC_DHCP4_TAG_PAD                0    // Pad Option\r
+#define PXEBC_DHCP4_TAG_EOP                255  // End Option\r
+#define PXEBC_DHCP4_TAG_NETMASK            1    // Subnet Mask\r
+#define PXEBC_DHCP4_TAG_TIME_OFFSET        2    // Time Offset from UTC\r
+#define PXEBC_DHCP4_TAG_ROUTER             3    // Router option,\r
+#define PXEBC_DHCP4_TAG_TIME_SERVER        4    // Time Server\r
+#define PXEBC_DHCP4_TAG_NAME_SERVER        5    // Name Server\r
+#define PXEBC_DHCP4_TAG_DNS_SERVER         6    // Domain Name Server\r
+#define PXEBC_DHCP4_TAG_HOSTNAME           12   // Host Name\r
+#define PXEBC_DHCP4_TAG_BOOTFILE_LEN       13   // Boot File Size\r
+#define PXEBC_DHCP4_TAG_DUMP               14   // Merit Dump File\r
+#define PXEBC_DHCP4_TAG_DOMAINNAME         15   // Domain Name\r
+#define PXEBC_DHCP4_TAG_ROOTPATH           17   // Root path\r
+#define PXEBC_DHCP4_TAG_EXTEND_PATH        18   // Extensions Path\r
+#define PXEBC_DHCP4_TAG_EMTU               22   // Maximum Datagram Reassembly Size\r
+#define PXEBC_DHCP4_TAG_TTL                23   // Default IP Time-to-live\r
+#define PXEBC_DHCP4_TAG_BROADCAST          28   // Broadcast Address\r
+#define PXEBC_DHCP4_TAG_NIS_DOMAIN         40   // Network Information Service Domain\r
+#define PXEBC_DHCP4_TAG_NIS_SERVER         41   // Network Information Servers\r
+#define PXEBC_DHCP4_TAG_NTP_SERVER         42   // Network Time Protocol Servers\r
+#define PXEBC_DHCP4_TAG_VENDOR             43   // Vendor Specific Information\r
+#define PXEBC_DHCP4_TAG_REQUEST_IP         50   // Requested IP Address\r
+#define PXEBC_DHCP4_TAG_LEASE              51   // IP Address Lease Time\r
+#define PXEBC_DHCP4_TAG_OVERLOAD           52   // Option Overload\r
+#define PXEBC_DHCP4_TAG_MSG_TYPE           53   // DHCP Message Type\r
+#define PXEBC_DHCP4_TAG_SERVER_ID          54   // Server Identifier\r
+#define PXEBC_DHCP4_TAG_PARA_LIST          55   // Parameter Request List\r
+#define PXEBC_DHCP4_TAG_MAXMSG             57   // Maximum DHCP Message Size\r
+#define PXEBC_DHCP4_TAG_T1                 58   // Renewal (T1) Time Value\r
+#define PXEBC_DHCP4_TAG_T2                 59   // Rebinding (T2) Time Value\r
+#define PXEBC_DHCP4_TAG_CLASS_ID           60   // Vendor class identifier\r
+#define PXEBC_DHCP4_TAG_CLIENT_ID          61   // Client-identifier\r
+#define PXEBC_DHCP4_TAG_TFTP               66   // TFTP server name\r
+#define PXEBC_DHCP4_TAG_BOOTFILE           67   // Bootfile name\r
+#define PXEBC_PXE_DHCP4_TAG_ARCH           93\r
+#define PXEBC_PXE_DHCP4_TAG_UNDI           94\r
+#define PXEBC_PXE_DHCP4_TAG_UUID           97\r
+//\r
+// Sub-Options in Dhcp Vendor Option\r
+//\r
+#define PXEBC_VENDOR_TAG_MTFTP_IP          1\r
+#define PXEBC_VENDOR_TAG_MTFTP_CPORT       2\r
+#define PXEBC_VENDOR_TAG_MTFTP_SPORT       3\r
+#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT     4\r
+#define PXEBC_VENDOR_TAG_MTFTP_DELAY       5\r
+#define PXEBC_VENDOR_TAG_DISCOVER_CTRL     6\r
+#define PXEBC_VENDOR_TAG_DISCOVER_MCAST    7\r
+#define PXEBC_VENDOR_TAG_BOOT_SERVERS      8\r
+#define PXEBC_VENDOR_TAG_BOOT_MENU         9\r
+#define PXEBC_VENDOR_TAG_MENU_PROMPT       10\r
+#define PXEBC_VENDOR_TAG_MCAST_ALLOC       11\r
+#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES  12\r
+#define PXEBC_VENDOR_TAG_BOOT_ITEM         71\r
+\r
+#define PXEBC_BOOT_REQUEST_TIMEOUT         1\r
+#define PXEBC_BOOT_REQUEST_RETRIES         4\r
+\r
+#define PXEBC_DHCP4_OVERLOAD_FILE          1\r
+#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME   2\r
+\r
+\r
+//\r
+// The array index of the DHCP4 option tag interested\r
+//\r
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN 0\r
+#define PXEBC_DHCP4_TAG_INDEX_VENDOR       1\r
+#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD     2\r
+#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE     3\r
+#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID    4\r
+#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID     5\r
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE     6\r
+#define PXEBC_DHCP4_TAG_INDEX_MAX          7\r
+\r
+//\r
+// Dhcp4 and Dhcp6 share this definition, and corresponding\r
+// relatioinship is as follows:\r
+//\r
+//   Dhcp4Discover <> Dhcp6Solicit\r
+//   Dhcp4Offer    <> Dhcp6Advertise\r
+//   Dhcp4Request  <> Dhcp6Request\r
+//   Dhcp4Ack      <> DHcp6Reply\r
+//\r
+typedef enum {\r
+  PxeOfferTypeDhcpOnly,\r
+  PxeOfferTypeDhcpPxe10,\r
+  PxeOfferTypeDhcpWfm11a,\r
+  PxeOfferTypeDhcpBinl,\r
+  PxeOfferTypeProxyPxe10,\r
+  PxeOfferTypeProxyWfm11a,\r
+  PxeOfferTypeProxyBinl,\r
+  PxeOfferTypeBootp,\r
+  PxeOfferTypeMax\r
+} PXEBC_OFFER_TYPE;\r
+\r
+#define BIT(x)                (1 << x)\r
+#define CTRL(x)               (0x1F & (x))\r
+#define DEFAULT_CLASS_ID_DATA "PXEClient:Arch:?????:????:??????"\r
+#define DEFAULT_UNDI_TYPE     1\r
+#define DEFAULT_UNDI_MAJOR    3\r
+#define DEFAULT_UNDI_MINOR    0\r
+\r
+#define MTFTP_VENDOR_OPTION_BIT_MAP \\r
+  (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \\r
+   BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \\r
+   BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \\r
+   BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \\r
+   BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY))\r
+\r
+#define DISCOVER_VENDOR_OPTION_BIT_MAP \\r
+  (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \\r
+   BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \\r
+   BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \\r
+   BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \\r
+   BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))\r
+\r
+#define IS_VALID_BOOT_PROMPT(x) \\r
+  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) \\r
+   == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))\r
+\r
+#define IS_VALID_BOOT_MENU(x) \\r
+  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) \\r
+   == BIT (PXEBC_VENDOR_TAG_BOOT_MENU))\r
+\r
+#define IS_VALID_MTFTP_VENDOR_OPTION(x) \\r
+  (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) \\r
+   == MTFTP_VENDOR_OPTION_BIT_MAP)\r
+\r
+#define IS_VALID_DISCOVER_VENDOR_OPTION(x) \\r
+  (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0)\r
+\r
+#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \\r
+  (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) \\r
+   == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES))\r
+\r
+#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \\r
+  (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & \\r
+     BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \\r
+    == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32))\r
+\r
+#define SET_VENDOR_OPTION_BIT_MAP(x, y) \\r
+  (*(x + ((y) / 32)) = (UINT32) ((UINT32) ((x)[(y) / 32]) | BIT ((y) % 32)))\r
+\r
+#define GET_NEXT_DHCP_OPTION(Opt) \\r
+  (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \\r
+   sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1)\r
+\r
+#define GET_OPTION_BUFFER_LEN(Pkt) \\r
+  ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4)\r
+\r
+#define GET_NEXT_BOOT_SVR_ENTRY(Ent) \\r
+  (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + \\r
+   ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS))\r
+\r
+#define IS_PROXY_DHCP_OFFER(Offer) \\r
+  EFI_IP4_EQUAL (&(Offer)->Dhcp4.Header.YourAddr, &mZeroIp4Addr)\r
+\r
+#define IS_DISABLE_BCAST_DISCOVER(x) \\r
+  (((x) & BIT (0)) == BIT (0))\r
+\r
+#define IS_DISABLE_MCAST_DISCOVER(x) \\r
+  (((x) & BIT (1)) == BIT (1))\r
+\r
+#define IS_ENABLE_USE_SERVER_LIST(x) \\r
+  (((x) & BIT (2)) == BIT (2))\r
+\r
+#define IS_ENABLE_BOOT_FILE_NAME(x) \\r
+  (((x) & BIT (3)) == BIT (3))\r
+\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+  UINT8 ParaList[135];\r
+} PXEBC_DHCP4_OPTION_PARA;\r
+\r
+typedef struct {\r
+  UINT16  Size;\r
+} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE;\r
+\r
+typedef struct {\r
+  UINT8 Type;\r
+  UINT8 MajorVer;\r
+  UINT8 MinorVer;\r
+} PXEBC_DHCP4_OPTION_UNDI;\r
+\r
+typedef struct {\r
+  UINT8 Type;\r
+} PXEBC_DHCP4_OPTION_MESG;\r
+\r
+typedef struct {\r
+  UINT16 Type;\r
+} PXEBC_DHCP4_OPTION_ARCH;\r
+\r
+typedef struct {\r
+  UINT8 ClassIdentifier[10];\r
+  UINT8 ArchitecturePrefix[5];\r
+  UINT8 ArchitectureType[5];\r
+  UINT8 Lit3[1];\r
+  UINT8 InterfaceName[4];\r
+  UINT8 Lit4[1];\r
+  UINT8 UndiMajor[3];\r
+  UINT8 UndiMinor[3];\r
+} PXEBC_DHCP4_OPTION_CLID;\r
+\r
+typedef struct {\r
+  UINT8 Type;\r
+  UINT8 Guid[16];\r
+} PXEBC_DHCP4_OPTION_UUID;\r
+\r
+typedef struct {\r
+  UINT16 Type;\r
+  UINT16 Layer;\r
+} PXEBC_OPTION_BOOT_ITEM;\r
+\r
+#pragma pack()\r
+\r
+typedef union {\r
+  PXEBC_DHCP4_OPTION_PARA           *Para;\r
+  PXEBC_DHCP4_OPTION_UNDI           *Undi;\r
+  PXEBC_DHCP4_OPTION_ARCH           *Arch;\r
+  PXEBC_DHCP4_OPTION_CLID           *Clid;\r
+  PXEBC_DHCP4_OPTION_UUID           *Uuid;\r
+  PXEBC_DHCP4_OPTION_MESG           *Mesg;\r
+  PXEBC_DHCP4_OPTION_MAX_MESG_SIZE  *MaxMesgSize;\r
+} PXEBC_DHCP4_OPTION_ENTRY;\r
+\r
+typedef struct {\r
+  UINT16            Type;\r
+  UINT8             IpCnt;\r
+  EFI_IPv4_ADDRESS  IpAddr[1];\r
+} PXEBC_BOOT_SVR_ENTRY;\r
+\r
+typedef struct {\r
+  UINT16            Type;\r
+  UINT8             DescLen;\r
+  UINT8             DescStr[1];\r
+} PXEBC_BOOT_MENU_ENTRY;\r
+\r
+typedef struct {\r
+  UINT8             Timeout;\r
+  UINT8             Prompt[1];\r
+} PXEBC_MENU_PROMPT;\r
+\r
+typedef struct {\r
+  UINT32                BitMap[8];\r
+  EFI_IPv4_ADDRESS      MtftpIp;\r
+  UINT16                MtftpCPort;\r
+  UINT16                MtftpSPort;\r
+  UINT8                 MtftpTimeout;\r
+  UINT8                 MtftpDelay;\r
+  UINT8                 DiscoverCtrl;\r
+  EFI_IPv4_ADDRESS      DiscoverMcastIp;\r
+  EFI_IPv4_ADDRESS      McastIpBase;\r
+  UINT16                McastIpBlock;\r
+  UINT16                McastIpRange;\r
+  UINT16                BootSrvType;\r
+  UINT16                BootSrvLayer;\r
+  PXEBC_BOOT_SVR_ENTRY  *BootSvr;\r
+  UINT8                 BootSvrLen;\r
+  PXEBC_BOOT_MENU_ENTRY *BootMenu;\r
+  UINT8                 BootMenuLen;\r
+  PXEBC_MENU_PROMPT     *MenuPrompt;\r
+  UINT8                 MenuPromptLen;\r
+  UINT32                *CredType;\r
+  UINT8                 CredTypeLen;\r
+} PXEBC_VENDOR_OPTION;\r
+\r
+typedef union {\r
+  EFI_DHCP4_PACKET        Offer;\r
+  EFI_DHCP4_PACKET        Ack;\r
+  UINT8                   Buffer[PXEBC_DHCP4_PACKET_MAX_SIZE];\r
+} PXEBC_DHCP4_PACKET;\r
+\r
+typedef struct {\r
+  PXEBC_DHCP4_PACKET      Packet;\r
+  PXEBC_OFFER_TYPE        OfferType;\r
+  EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_TAG_INDEX_MAX];\r
+  PXEBC_VENDOR_OPTION     VendorOpt;\r
+} PXEBC_DHCP4_PACKET_CACHE;\r
+\r
+\r
+/**\r
+  Create a template DHCPv4 packet as a seed.\r
+\r
+  @param[out] Seed           Pointer to the seed packet.\r
+  @param[in]  Udp4           Pointer to EFI_UDP4_PROTOCOL.\r
+\r
+**/\r
+VOID\r
+PxeBcSeedDhcp4Packet (\r
+  OUT EFI_DHCP4_PACKET       *Seed,\r
+  IN  EFI_UDP4_PROTOCOL      *Udp4\r
+  );\r
+\r
+\r
+/**\r
+  Parse the cached DHCPv4 packet, including all the options.\r
+\r
+  @param[in]  Cache4             Pointer to cached DHCPv4 packet.\r
+\r
+  @retval     EFI_SUCCESS        Parsed the DHCPv4 packet successfully.\r
+  @retval     EFI_DEVICE_ERROR   Failed to parse and invalid packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcParseDhcp4Packet (\r
+  IN PXEBC_DHCP4_PACKET_CACHE    *Cache4\r
+  );\r
+\r
+\r
+/**\r
+  Build and send out the request packet for the bootfile, and parse the reply.\r
+\r
+  @param[in]  Private               Pointer to PxeBc private data.\r
+  @param[in]  Type                  PxeBc option boot item type.\r
+  @param[in]  Layer                 Pointer to option boot item layer.\r
+  @param[in]  UseBis                Use BIS or not.\r
+  @param[in]  DestIp                Pointer to the server address.\r
+  @param[in]  IpCount               The total count of the server address.\r
+  @param[in]  SrvList               Pointer to EFI_PXE_BASE_CODE_SRVLIST.\r
+\r
+  @retval     EFI_SUCCESS           Successfully discovered boot file.\r
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.\r
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.\r
+  @retval     Others                Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4Discover (\r
+  IN  PXEBC_PRIVATE_DATA              *Private,\r
+  IN  UINT16                          Type,\r
+  IN  UINT16                          *Layer,\r
+  IN  BOOLEAN                         UseBis,\r
+  IN  EFI_IP_ADDRESS                  *DestIp,\r
+  IN  UINT16                          IpCount,\r
+  IN  EFI_PXE_BASE_CODE_SRVLIST       *SrvList\r
+  );\r
+\r
+\r
+/**\r
+  Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.\r
+\r
+  @param[in]  Private           Pointer to PxeBc private data.\r
+  @param[in]  Dhcp4             Pointer to the EFI_DHCP4_PROTOCOL\r
+\r
+  @retval EFI_SUCCESS           The D.O.R.A process successfully finished.\r
+  @retval Others                Failed to finish the D.O.R.A process.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4Dora (\r
+  IN PXEBC_PRIVATE_DATA         *Private,\r
+  IN EFI_DHCP4_PROTOCOL         *Dhcp4\r
+  );\r
+\r
+#endif\r
+\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
new file mode 100644 (file)
index 0000000..0b7cf1f
--- /dev/null
@@ -0,0 +1,1531 @@
+/** @file\r
+  Functions implementation related with DHCPv6 for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+/**\r
+  Parse out a DHCPv6 option by OptTag, and find the position in buffer.\r
+\r
+  @param[in]  Buffer        The pointer to the option buffer.\r
+  @param[in]  Length        Length of the option buffer.\r
+  @param[in]  OptTag        The required option tag.\r
+\r
+  @retval     NULL          Failed to parse the required option.\r
+  @retval     Others        The postion of the required option in buffer.\r
+\r
+**/\r
+EFI_DHCP6_PACKET_OPTION *\r
+PxeBcParseDhcp6Options (\r
+  IN UINT8                       *Buffer,\r
+  IN UINT32                      Length,\r
+  IN UINT16                      OptTag\r
+  )\r
+{\r
+  EFI_DHCP6_PACKET_OPTION        *Option;\r
+  UINT32                         Offset;\r
+\r
+  Option  = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
+  Offset  = 0;\r
+\r
+  //\r
+  // OpLen and OpCode here are both stored in network order.\r
+  //\r
+  while (Offset < Length) {\r
+\r
+    if (NTOHS (Option->OpCode) == OptTag) {\r
+\r
+      return Option;\r
+    }\r
+\r
+    Offset += (NTOHS(Option->OpLen) + 4);\r
+    Option  = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Build the options buffer for the DHCPv6 request packet.\r
+\r
+  @param[in]  Private             The pointer to PxeBc private data.\r
+  @param[out] OptList             The pointer to the option pointer array.\r
+  @param[in]  Buffer              The pointer to the buffer to contain the option list.\r
+\r
+  @return     Index               The count of the built-in options.\r
+\r
+**/\r
+UINT32\r
+PxeBcBuildDhcp6Options (\r
+  IN  PXEBC_PRIVATE_DATA           *Private,\r
+  OUT EFI_DHCP6_PACKET_OPTION      **OptList,\r
+  IN  UINT8                        *Buffer\r
+  )\r
+{\r
+  PXEBC_DHCP6_OPTION_ENTRY         OptEnt;\r
+  UINT32                           Index;\r
+  UINT16                           Value;\r
+\r
+  Index       = 0;\r
+  OptList[0]  = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
+\r
+  //\r
+  // Append client option request option\r
+  //\r
+  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_ORO);\r
+  OptList[Index]->OpLen      = HTONS (4);\r
+  OptEnt.Oro                 = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;\r
+  OptEnt.Oro->OpCode[0]      = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL);\r
+  OptEnt.Oro->OpCode[1]      = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM);\r
+  Index++;\r
+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
+\r
+  //\r
+  // Append client network device interface option\r
+  //\r
+  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_UNDI);\r
+  OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_UNDI));\r
+  OptEnt.Undi                = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;\r
+\r
+  if (Private->Nii != NULL) {\r
+    OptEnt.Undi->Type        = Private->Nii->Type;\r
+    OptEnt.Undi->MajorVer    = Private->Nii->MajorVer;\r
+    OptEnt.Undi->MinorVer    = Private->Nii->MinorVer;\r
+  } else {\r
+    OptEnt.Undi->Type        = DEFAULT_UNDI_TYPE;\r
+    OptEnt.Undi->MajorVer    = DEFAULT_UNDI_MAJOR;\r
+    OptEnt.Undi->MinorVer    = DEFAULT_UNDI_MINOR;\r
+  }\r
+\r
+  OptEnt.Undi->Reserved      = 0;\r
+  Index++;\r
+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
+\r
+  //\r
+  // Append client system architecture option\r
+  //\r
+  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_ARCH);\r
+  OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));\r
+  OptEnt.Arch                = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;\r
+  Value                      = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);\r
+  CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));\r
+  Index++;\r
+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
+\r
+  //\r
+  // Append vendor class option to store the PXE class identifier.\r
+  //\r
+  OptList[Index]->OpCode       = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS);\r
+  OptList[Index]->OpLen        = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));\r
+  OptEnt.VendorClass           = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;\r
+  OptEnt.VendorClass->Vendor   = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);\r
+  OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID));\r
+  CopyMem (\r
+    &OptEnt.VendorClass->ClassId,\r
+    DEFAULT_CLASS_ID_DATA,\r
+    sizeof (PXEBC_CLASS_ID)\r
+    );\r
+  PxeBcUintnToAscDecWithFormat (\r
+    EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,\r
+    OptEnt.VendorClass->ClassId.ArchitectureType,\r
+    sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)\r
+    );\r
+\r
+  if (Private->Nii != NULL) {\r
+    CopyMem (\r
+      OptEnt.VendorClass->ClassId.InterfaceName,\r
+      Private->Nii->StringId,\r
+      sizeof (OptEnt.VendorClass->ClassId.InterfaceName)\r
+      );\r
+    PxeBcUintnToAscDecWithFormat (\r
+      Private->Nii->MajorVer,\r
+      OptEnt.VendorClass->ClassId.UndiMajor,\r
+      sizeof (OptEnt.VendorClass->ClassId.UndiMajor)\r
+      );\r
+    PxeBcUintnToAscDecWithFormat (\r
+      Private->Nii->MinorVer,\r
+      OptEnt.VendorClass->ClassId.UndiMinor,\r
+      sizeof (OptEnt.VendorClass->ClassId.UndiMinor)\r
+      );\r
+  }\r
+\r
+  Index++;\r
+\r
+  return Index;\r
+}\r
+\r
+\r
+/**\r
+  Cache the DHCPv6 packet.\r
+\r
+  @param[in]  Dst          The pointer to the cache buffer for DHCPv6 packet.\r
+  @param[in]  Src          The pointer to the DHCPv6 packet to be cached.\r
+\r
+**/\r
+VOID\r
+PxeBcCacheDhcp6Packet (\r
+  IN EFI_DHCP6_PACKET          *Dst,\r
+  IN EFI_DHCP6_PACKET          *Src\r
+  )\r
+{\r
+  ASSERT (Dst->Size >= Src->Length);\r
+\r
+  CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);\r
+  Dst->Length = Src->Length;\r
+}\r
+\r
+\r
+/**\r
+  Free all the nodes in the list for boot file.\r
+\r
+  @param[in]  Head            The pointer to the head of list.\r
+\r
+**/\r
+VOID\r
+PxeBcFreeBootFileOption (\r
+  IN LIST_ENTRY               *Head\r
+  )\r
+{\r
+  LIST_ENTRY                  *Entry;\r
+  LIST_ENTRY                  *NextEntry;\r
+  PXEBC_DHCP6_OPTION_NODE     *Node;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) {\r
+    Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link);\r
+    RemoveEntryList (Entry);\r
+    FreePool (Node);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Parse the Boot File URL option.\r
+\r
+  @param[out]     FileName     The pointer to the boot file name.\r
+  @param[in, out] SrvAddr      The pointer to the boot server address.\r
+  @param[in]      BootFile     The pointer to the boot file URL option data.\r
+  @param[in]      Length       The length of the boot file URL option data.\r
+\r
+  @retval EFI_ABORTED     User cancel operation.\r
+  @retval EFI_SUCCESS     Selected the boot menu successfully.\r
+  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractBootFileUrl (\r
+     OUT UINT8               **FileName,\r
+  IN OUT EFI_IPv6_ADDRESS    *SrvAddr,\r
+  IN     CHAR8               *BootFile,\r
+  IN     UINT16              Length\r
+  )\r
+{\r
+  UINT16                     PrefixLen;\r
+  UINT8                      *BootFileNamePtr;\r
+  UINT8                      *BootFileName;\r
+  UINT16                     BootFileNameLen;\r
+  CHAR8                      *TmpStr;\r
+  CHAR8                      *ServerAddressOption;\r
+  CHAR8                      *ServerAddress;\r
+  EFI_STATUS                 Status;\r
+\r
+  //\r
+  // The format of the Boot File URL option is:\r
+  //\r
+  //  0                   1                   2                   3\r
+  //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  // |       OPT_BOOTFILE_URL        |            option-len         |\r
+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  // |                                                               |\r
+  // .                  bootfile-url  (variable length)              .\r
+  // |                                                               |\r
+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+  //\r
+\r
+  //\r
+  // Based upon RFC 5970 and UEFI errata that will appear in chapter 21.3 of UEFI 2.3\r
+  // specification after 2.3 errata B and future UEFI Specifications after 2.3.\r
+  // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME\r
+  // As an example where the BOOTFILE_NAME is the EFI loader and\r
+  // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.\r
+  //\r
+  PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);\r
+\r
+  if (Length <= PrefixLen ||\r
+      CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  BootFile = BootFile + PrefixLen;\r
+  Length   = (UINT16) (Length - PrefixLen);\r
+\r
+  TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1);\r
+  if (TmpStr == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  CopyMem (TmpStr, BootFile, Length);\r
+  TmpStr[Length] = '\0';\r
+\r
+  //\r
+  // Get the part of SERVER_ADDRESS string.\r
+  //\r
+  ServerAddressOption = TmpStr;\r
+  if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) {\r
+    FreePool (TmpStr);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ServerAddressOption ++;\r
+  ServerAddress = ServerAddressOption;\r
+  while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
+    ServerAddress++;\r
+  }\r
+\r
+  if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
+    FreePool (TmpStr);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *ServerAddress = '\0';\r
+\r
+  //\r
+  // Convert the string of server address to Ipv6 address format and store it.\r
+  //\r
+  Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (TmpStr);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Get the part of BOOTFILE_NAME string.\r
+  //\r
+  BootFileNamePtr = (UINT8*)((UINTN)ServerAddress + 1);\r
+  if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {\r
+    FreePool (TmpStr);\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ++BootFileNamePtr;\r
+  BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);\r
+  if (BootFileNameLen != 0 || FileName != NULL) {\r
+    BootFileName = (UINT8 *) AllocateZeroPool (BootFileNameLen);\r
+    if (BootFileName == NULL) {\r
+      FreePool (TmpStr);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    CopyMem (BootFileName, BootFileNamePtr, BootFileNameLen);\r
+    BootFileName[BootFileNameLen - 1] = '\0';\r
+    *FileName = BootFileName;\r
+  }\r
+\r
+\r
+  FreePool (TmpStr);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse the Boot File Parameter option.\r
+\r
+  @param[in]  BootFilePara      The pointer to boot file parameter option data.\r
+  @param[out] BootFileSize      The pointer to the parsed boot file size.\r
+\r
+  @retval EFI_SUCCESS     Successfully obtained the boot file size from parameter option.\r
+  @retval EFI_NOT_FOUND   Failed to extract the boot file size from parameter option.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractBootFileParam (\r
+  IN  CHAR8                  *BootFilePara,\r
+  OUT UINT16                 *BootFileSize\r
+  )\r
+{\r
+  UINT16                     Length;\r
+  UINT8                      Index;\r
+  UINT8                      Digit;\r
+  UINT32                     Size;\r
+\r
+  CopyMem (&Length, BootFilePara, sizeof (UINT16));\r
+  Length = NTOHS (Length);\r
+\r
+  //\r
+  // The BootFile Size should be 1~5 byte ASCII strings\r
+  //\r
+  if (Length < 1 || Length > 5) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Extract the value of BootFile Size.\r
+  //\r
+  BootFilePara = BootFilePara + sizeof (UINT16);\r
+  Size         = 0;\r
+  for (Index = 0; Index < Length; Index++) {\r
+    if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+\r
+    Size = (Size + Digit) * 10;\r
+  }\r
+\r
+  Size = Size / 10;\r
+  if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  *BootFileSize = (UINT16) Size;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse the cached DHCPv6 packet, including all the options.\r
+\r
+  @param[in]  Cache6           The pointer to a cached DHCPv6 packet.\r
+\r
+  @retval     EFI_SUCCESS      Parsed the DHCPv6 packet successfully.\r
+  @retval     EFI_DEVICE_ERROR Failed to parse and invalid the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcParseDhcp6Packet (\r
+  IN PXEBC_DHCP6_PACKET_CACHE  *Cache6\r
+  )\r
+{\r
+  EFI_DHCP6_PACKET             *Offer;\r
+  EFI_DHCP6_PACKET_OPTION      **Options;\r
+  EFI_DHCP6_PACKET_OPTION      *Option;\r
+  PXEBC_OFFER_TYPE             OfferType;\r
+  BOOLEAN                      IsProxyOffer;\r
+  BOOLEAN                      IsPxeOffer;\r
+  UINT32                       Offset;\r
+  UINT32                       Length;\r
+  UINT32                       EnterpriseNum;\r
+\r
+  IsProxyOffer = TRUE;\r
+  IsPxeOffer   = FALSE;\r
+  Offer        = &Cache6->Packet.Offer;\r
+  Options      = Cache6->OptList;\r
+\r
+  ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));\r
+\r
+  Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);\r
+  Offset  = 0;\r
+  Length  = GET_DHCP6_OPTION_SIZE (Offer);\r
+\r
+  //\r
+  // OpLen and OpCode here are both stored in network order, since they are from original packet.\r
+  //\r
+  while (Offset < Length) {\r
+\r
+    if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) {\r
+      Options[PXEBC_DHCP6_IDX_IA_NA] = Option;\r
+    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) {\r
+      //\r
+      // The server sends this option to inform the client about an URL to a boot file.\r
+      //\r
+      Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;\r
+    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) {\r
+      Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;\r
+    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) {\r
+      Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;\r
+    }\r
+\r
+    Offset += (NTOHS (Option->OpLen) + 4);\r
+    Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);\r
+  }\r
+\r
+  //\r
+  // The offer with assigned client address is a proxy offer.\r
+  // An ia_na option, embeded with valid ia_addr option and a status_code of success.\r
+  //\r
+  Option = Options[PXEBC_DHCP6_IDX_IA_NA];\r
+  if (Option != NULL && NTOHS(Option->OpLen) >= 12) {\r
+    Option = PxeBcParseDhcp6Options (\r
+               Option->Data + 12,\r
+               NTOHS (Option->OpLen),\r
+               PXEBC_DHCP6_OPT_STATUS_CODE\r
+               );\r
+    if (Option != NULL && Option->Data[0] == 0) {\r
+      IsProxyOffer = FALSE;\r
+    }\r
+  }\r
+\r
+  //\r
+  // The offer with "PXEClient" is a pxe offer.\r
+  //\r
+  Option        = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];\r
+  EnterpriseNum = PXEBC_DHCP6_ENTERPRISE_NUM;\r
+  if (Option != NULL &&\r
+      NTOHS(Option->OpLen) >= 13 &&\r
+      CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&\r
+      CompareMem (&Option->Data[4], DEFAULT_CLASS_ID_DATA, 9) == 0) {\r
+    IsPxeOffer = TRUE;\r
+  }\r
+\r
+  //\r
+  // Determine offer type of the dhcp6 packet.\r
+  //\r
+  if (IsPxeOffer) {\r
+    //\r
+    // It's a binl offer only with PXEClient.\r
+    //\r
+    OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;\r
+  } else {\r
+    //\r
+    // It's a dhcp only offer, which is a pure dhcp6 offer packet.\r
+    //\r
+    OfferType = PxeOfferTypeDhcpOnly;\r
+  }\r
+\r
+  Cache6->OfferType = OfferType;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Cache the DHCPv6 ack packet, and parse it on demand.\r
+\r
+  @param[in]  Private             The pointer to PxeBc private data.\r
+  @param[in]  Ack                 The pointer to the DHCPv6 ack packet.\r
+  @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.\r
+\r
+**/\r
+VOID\r
+PxeBcCopyDhcp6Ack (\r
+  IN PXEBC_PRIVATE_DATA   *Private,\r
+  IN EFI_DHCP6_PACKET     *Ack,\r
+  IN BOOLEAN              Verified\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+\r
+  Mode = Private->PxeBc.Mode;\r
+\r
+  PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);\r
+\r
+  if (Verified) {\r
+    //\r
+    // Parse the ack packet and store it into mode data if needed.\r
+    //\r
+    PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);\r
+    CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);\r
+    Mode->DhcpAckReceived = TRUE;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Cache the DHCPv6 proxy offer packet according to the received order.\r
+\r
+  @param[in]  Private               The pointer to PxeBc private data.\r
+  @param[in]  OfferIndex            The received order of offer packets.\r
+\r
+**/\r
+VOID\r
+PxeBcCopyDhcp6Proxy (\r
+  IN PXEBC_PRIVATE_DATA     *Private,\r
+  IN UINT32                 OfferIndex\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE    *Mode;\r
+  EFI_DHCP6_PACKET          *Offer;\r
+\r
+  ASSERT (OfferIndex < Private->OfferNum);\r
+  ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);\r
+\r
+  Mode  = Private->PxeBc.Mode;\r
+  Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;\r
+\r
+  //\r
+  // Cache the proxy offer packet and parse it.\r
+  //\r
+  PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);\r
+  PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);\r
+\r
+  //\r
+  // Store this packet into mode data.\r
+  //\r
+  CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);\r
+  Mode->ProxyOfferReceived = TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Retry to request bootfile name by the BINL offer.\r
+\r
+  @param[in]  Private              The pointer to PxeBc private data.\r
+  @param[in]  Index                The received order of offer packets.\r
+\r
+  @retval     EFI_SUCCESS          Successfully retried a request for the bootfile name.\r
+  @retval     EFI_DEVICE_ERROR     Failed to retry the bootfile name.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcRetryDhcp6Binl (\r
+  IN PXEBC_PRIVATE_DATA  *Private,\r
+  IN UINT32              Index\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE    *Mode;\r
+  PXEBC_DHCP6_PACKET_CACHE  *Offer;\r
+  PXEBC_DHCP6_PACKET_CACHE  *Cache6;\r
+  EFI_IP_ADDRESS            ServerIp;\r
+  EFI_STATUS                Status;\r
+\r
+  ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
+  ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||\r
+          Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl);\r
+\r
+  Mode                  = Private->PxeBc.Mode;\r
+  Private->IsDoDiscover = FALSE;\r
+  Offer                 = &Private->OfferBuffer[Index].Dhcp6;\r
+\r
+  ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
+  //\r
+  // Parse out the next server address from the last offer, and store it\r
+  //\r
+  Status = PxeBcExtractBootFileUrl (\r
+             NULL,\r
+             &ServerIp.v6,\r
+             (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
+             NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.\r
+  //\r
+  Status = PxeBcDhcp6Discover (\r
+             Private,\r
+             0,\r
+             NULL,\r
+             FALSE,\r
+             &ServerIp\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Cache6 = &Private->ProxyOffer.Dhcp6;\r
+  Status = PxeBcParseDhcp6Packet (Cache6);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Cache6->OfferType != PxeOfferTypeProxyPxe10 &&\r
+      Cache6->OfferType != PxeOfferTypeProxyWfm11a &&\r
+      Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {\r
+    //\r
+    // This BINL ack doesn't have discovery option set or multicast option set\r
+    // or bootfile name specified.\r
+    //\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Mode->ProxyOfferReceived = TRUE;\r
+  CopyMem (\r
+    &Mode->ProxyOffer.Dhcpv6,\r
+    &Cache6->Packet.Offer.Dhcp6,\r
+    Cache6->Packet.Offer.Length\r
+    );\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.\r
+\r
+  @param[in]  Private               The pointer to PXEBC_PRIVATE_DATA.\r
+  @param[in]  RcvdOffer             The pointer to the received offer packet.\r
+\r
+**/\r
+VOID\r
+PxeBcCacheDhcp6Offer (\r
+  IN PXEBC_PRIVATE_DATA     *Private,\r
+  IN EFI_DHCP6_PACKET       *RcvdOffer\r
+  )\r
+{\r
+  PXEBC_DHCP6_PACKET_CACHE  *Cache6;\r
+  EFI_DHCP6_PACKET          *Offer;\r
+  PXEBC_OFFER_TYPE          OfferType;\r
+\r
+  Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;\r
+  Offer  = &Cache6->Packet.Offer;\r
+\r
+  //\r
+  // Cache the content of DHCPv6 packet firstly.\r
+  //\r
+  PxeBcCacheDhcp6Packet (Offer, RcvdOffer);\r
+\r
+  //\r
+  // Validate the DHCPv6 packet, and parse the options and offer type.\r
+  //\r
+  if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.\r
+  //\r
+  OfferType = Cache6->OfferType;\r
+  ASSERT (OfferType < PxeOfferTypeMax);\r
+  ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);\r
+\r
+  if (IS_PROXY_OFFER (OfferType)) {\r
+    //\r
+    // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.\r
+    //\r
+    Private->IsProxyRecved = TRUE;\r
+\r
+    if (OfferType == PxeOfferTypeProxyBinl) {\r
+      //\r
+      // Cache all proxy BINL offers.\r
+      //\r
+      Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
+      Private->OfferCount[OfferType]++;\r
+    } else if (Private->OfferCount[OfferType] > 0) {\r
+      //\r
+      // Only cache the first PXE10/WFM11a offer, and discard the others.\r
+      //\r
+      Private->OfferIndex[OfferType][0] = Private->OfferNum;\r
+      Private->OfferCount[OfferType]    = 1;\r
+    } else {\r
+      return;\r
+    }\r
+  } else {\r
+    //\r
+    // It's a DHCPv6 offer with yiaddr, and cache them all.\r
+    //\r
+    Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
+    Private->OfferCount[OfferType]++;\r
+  }\r
+\r
+  Private->OfferNum++;\r
+}\r
+\r
+\r
+/**\r
+  Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.\r
+\r
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+PxeBcSelectDhcp6Offer (\r
+  IN PXEBC_PRIVATE_DATA     *Private\r
+  )\r
+{\r
+  UINT32                Index;\r
+  UINT32                OfferIndex;\r
+  PXEBC_OFFER_TYPE      OfferType;\r
+\r
+  Private->SelectIndex = 0;\r
+\r
+  if (Private->IsOfferSorted) {\r
+    //\r
+    // Select offer by default policy.\r
+    //\r
+    if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {\r
+      //\r
+      // 1. DhcpPxe10 offer\r
+      //\r
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {\r
+      //\r
+      // 2. DhcpWfm11a offer\r
+      //\r
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+               Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {\r
+      //\r
+      // 3. DhcpOnly offer and ProxyPxe10 offer.\r
+      //\r
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+      Private->SelectProxyType = PxeOfferTypeProxyPxe10;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+               Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {\r
+      //\r
+      // 4. DhcpOnly offer and ProxyWfm11a offer.\r
+      //\r
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+      Private->SelectProxyType = PxeOfferTypeProxyWfm11a;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {\r
+      //\r
+      // 5. DhcpBinl offer.\r
+      //\r
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;\r
+\r
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+               Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {\r
+      //\r
+      // 6. DhcpOnly offer and ProxyBinl offer.\r
+      //\r
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+      Private->SelectProxyType = PxeOfferTypeProxyBinl;\r
+\r
+    } else {\r
+      //\r
+      // 7. DhcpOnly offer with bootfilename.\r
+      //\r
+      for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {\r
+        OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];\r
+        if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) {\r
+          Private->SelectIndex = OfferIndex + 1;\r
+          break;\r
+        }\r
+      }\r
+    }\r
+  } else {\r
+    //\r
+    // Select offer by received order.\r
+    //\r
+    for (Index = 0; Index < Private->OfferNum; Index++) {\r
+\r
+      OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;\r
+\r
+      if (IS_PROXY_OFFER (OfferType)) {\r
+        //\r
+        // Skip proxy offers\r
+        //\r
+        continue;\r
+      }\r
+\r
+      if (!Private->IsProxyRecved &&\r
+          OfferType == PxeOfferTypeDhcpOnly &&\r
+          Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {\r
+        //\r
+        // Skip if DhcpOnly offer without any other proxy offers or bootfilename.\r
+        //\r
+        continue;\r
+      }\r
+\r
+      Private->SelectIndex = Index + 1;\r
+      break;\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Handle the DHCPv6 offer packet.\r
+\r
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.\r
+\r
+  @retval     EFI_SUCCESS         Handled the DHCPv6 offer packet successfully.\r
+  @retval     EFI_NO_RESPONSE     No response to the following request packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcHandleDhcp6Offer (\r
+  IN PXEBC_PRIVATE_DATA            *Private\r
+  )\r
+{\r
+  PXEBC_DHCP6_PACKET_CACHE         *Cache6;\r
+  EFI_STATUS                       Status;\r
+  PXEBC_OFFER_TYPE                 OfferType;\r
+  UINT32                           ProxyIndex;\r
+  UINT32                           SelectIndex;\r
+  UINT32                           Index;\r
+\r
+  ASSERT (Private->SelectIndex > 0);\r
+  SelectIndex = (UINT32) (Private->SelectIndex - 1);\r
+  ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);\r
+  Cache6      = &Private->OfferBuffer[SelectIndex].Dhcp6;\r
+  Status      = EFI_SUCCESS;\r
+\r
+  if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {\r
+    //\r
+    // DhcpBinl offer is selected, so need try to request bootfilename by this offer.\r
+    //\r
+    if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {\r
+      Status = EFI_NO_RESPONSE;\r
+    }\r
+  } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {\r
+\r
+    if (Private->IsProxyRecved) {\r
+      //\r
+      // DhcpOnly offer is selected, so need try to request bootfilename.\r
+      //\r
+      ProxyIndex = 0;\r
+      if (Private->IsOfferSorted) {\r
+        //\r
+        // The proxy offer should be determined if select by default policy.\r
+        // IsOfferSorted means all offers are labeled by OfferIndex.\r
+        //\r
+        ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);\r
+\r
+        if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {\r
+          //\r
+          // Try all the cached ProxyBinl offer one by one to request bootfilename.\r
+          //\r
+          for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {\r
+\r
+            ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];\r
+            if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {\r
+              break;\r
+            }\r
+          }\r
+          if (Index == Private->OfferCount[Private->SelectProxyType]) {\r
+            Status = EFI_NO_RESPONSE;\r
+          }\r
+        } else {\r
+          //\r
+          // For other proxy offers (pxe10 or wfm11a), only one is buffered.\r
+          //\r
+          ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
+        }\r
+      } else {\r
+        //\r
+        // The proxy offer should not be determined if select by received order.\r
+        //\r
+        Status = EFI_NO_RESPONSE;\r
+\r
+        for (Index = 0; Index < Private->OfferNum; Index++) {\r
+\r
+          OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;\r
+\r
+          if (!IS_PROXY_OFFER (OfferType)) {\r
+            //\r
+            // Skip non proxy dhcp offers.\r
+            //\r
+            continue;\r
+          }\r
+\r
+          if (OfferType == PxeOfferTypeProxyBinl) {\r
+            //\r
+            // Try all the cached ProxyBinl offer one by one to request bootfilename.\r
+            //\r
+            if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {\r
+              continue;\r
+            }\r
+          }\r
+\r
+          Private->SelectProxyType = OfferType;\r
+          ProxyIndex               = Index;\r
+          Status                   = EFI_SUCCESS;\r
+          break;\r
+        }\r
+      }\r
+\r
+      if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {\r
+        //\r
+        // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.\r
+        //\r
+        PxeBcCopyDhcp6Proxy (Private, ProxyIndex);\r
+      }\r
+    } else {\r
+      //\r
+      //  Othewise, the bootfilename must be included in DhcpOnly offer.\r
+      //\r
+      ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
+    }\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // All PXE boot information is ready by now.\r
+    //\r
+    PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);\r
+    Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Unregister the address by Ip6Config protocol.\r
+\r
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+PxeBcUnregisterIp6Address (\r
+  IN PXEBC_PRIVATE_DATA           *Private\r
+  )\r
+{\r
+  if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {\r
+    //\r
+    // PXE driver change the policy of IP6 driver, it's a chance to recover.\r
+    // Keep the point and there is no enough requirements to do recovery.\r
+    //\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Register the ready address by Ip6Config protocol.\r
+\r
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.\r
+  @param[in]  Address             The pointer to the ready address.\r
+\r
+  @retval     EFI_SUCCESS         Registered the address succesfully.\r
+  @retval     Others              Failed to register the address.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcRegisterIp6Address (\r
+  IN PXEBC_PRIVATE_DATA            *Private,\r
+  IN EFI_IPv6_ADDRESS              *Address\r
+  )\r
+{\r
+  EFI_IP6_PROTOCOL                 *Ip6;\r
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;\r
+  EFI_IP6_CONFIG_POLICY            Policy;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS    CfgAddr;\r
+  UINTN                            DataSize;\r
+  EFI_EVENT                        TimeOutEvt;\r
+  EFI_EVENT                        MappedEvt;\r
+  EFI_STATUS                       Status;\r
+\r
+  Status     = EFI_SUCCESS;\r
+  TimeOutEvt = NULL;\r
+  MappedEvt  = NULL;\r
+  DataSize   = sizeof (EFI_IP6_CONFIG_POLICY);\r
+  Ip6Cfg     = Private->Ip6Cfg;\r
+  Ip6        = Private->Ip6;\r
+\r
+  ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
+  CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  //\r
+  // Get and store the current policy of IP6 driver.\r
+  //\r
+  Status = Ip6Cfg->GetData (\r
+                     Ip6Cfg,\r
+                     Ip6ConfigDataTypePolicy,\r
+                     &DataSize,\r
+                     &Private->Ip6Policy\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // There is no channel between IP6 and PXE driver about address setting,\r
+  // so it has to set the new address by Ip6ConfigProtocol manually.\r
+  //\r
+  Policy = Ip6ConfigPolicyManual;\r
+  Status = Ip6Cfg->SetData (\r
+                     Ip6Cfg,\r
+                     Ip6ConfigDataTypePolicy,\r
+                     sizeof(EFI_IP6_CONFIG_POLICY),\r
+                     &Policy\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // There is no need to recover later.\r
+    //\r
+    Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Create a timer as setting address timeout event since DAD in IP6 driver.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &TimeOutEvt\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Create a notify event to set address flag when DAD if IP6 driver succeeded.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  PxeBcCommonNotify,\r
+                  &Private->IsAddressOk,\r
+                  &MappedEvt\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Ip6Cfg->RegisterDataNotify (\r
+                     Ip6Cfg,\r
+                     Ip6ConfigDataTypeManualAddress,\r
+                     MappedEvt\r
+                     );\r
+  if (EFI_ERROR(Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Ip6Cfg->SetData (\r
+                     Ip6Cfg,\r
+                     Ip6ConfigDataTypeManualAddress,\r
+                     sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS),\r
+                     &CfgAddr\r
+                     );\r
+  if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Start the 5 secondes timer to wait for setting address.\r
+  //\r
+  Status = EFI_NO_MAPPING;\r
+  gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT);\r
+\r
+  while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
+    Ip6->Poll (Ip6);\r
+    if (Private->IsAddressOk) {\r
+      Status = EFI_SUCCESS;\r
+      break;\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+  if (MappedEvt != NULL) {\r
+    Ip6Cfg->UnregisterDataNotify (\r
+              Ip6Cfg,\r
+              Ip6ConfigDataTypeManualAddress,\r
+              MappedEvt\r
+              );\r
+    gBS->CloseEvent (MappedEvt);\r
+  }\r
+  if (TimeOutEvt != NULL) {\r
+    gBS->CloseEvent (TimeOutEvt);\r
+  }\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver\r
+  to intercept events that occurred in the configuration process.\r
+\r
+  @param[in]  This              The pointer to the EFI DHCPv6 Protocol.\r
+  @param[in]  Context           The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().\r
+  @param[in]  CurrentState      The current operational state of the EFI DHCPv Protocol driver.\r
+  @param[in]  Dhcp6Event        The event that occurs in the current state, which usually means a\r
+                                state transition.\r
+  @param[in]  Packet            The DHCPv6 packet that is going to be sent or was already received.\r
+  @param[out] NewPacket         The packet that is used to replace the Packet above.\r
+\r
+  @retval EFI_SUCCESS           Told the EFI DHCPv6 Protocol driver to continue the DHCP process.\r
+  @retval EFI_NOT_READY         Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol\r
+                                driver will continue to wait for more packets.\r
+  @retval EFI_ABORTED           Told the EFI DHCPv6 Protocol driver to abort the current process.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDhcp6CallBack (\r
+  IN  EFI_DHCP6_PROTOCOL           *This,\r
+  IN  VOID                         *Context,\r
+  IN  EFI_DHCP6_STATE              CurrentState,\r
+  IN  EFI_DHCP6_EVENT              Dhcp6Event,\r
+  IN  EFI_DHCP6_PACKET             *Packet,\r
+  OUT EFI_DHCP6_PACKET             **NewPacket     OPTIONAL\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA                  *Private;\r
+  EFI_PXE_BASE_CODE_MODE              *Mode;\r
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;\r
+  EFI_DHCP6_PACKET                    *SelectAd;\r
+  EFI_STATUS                          Status;\r
+  BOOLEAN                             Received;\r
+\r
+  if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&\r
+      (Dhcp6Event != Dhcp6SelectAdvertise) &&\r
+      (Dhcp6Event != Dhcp6SendSolicit) &&\r
+      (Dhcp6Event != Dhcp6SendRequest) &&\r
+      (Dhcp6Event != Dhcp6RcvdReply)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  ASSERT (Packet != NULL);\r
+\r
+  Private   = (PXEBC_PRIVATE_DATA *) Context;\r
+  Mode      = Private->PxeBc.Mode;\r
+  Callback  = Private->PxeBcCallback;\r
+\r
+  //\r
+  // Callback to user when any traffic ocurred if has.\r
+  //\r
+  if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) {\r
+    Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);\r
+    Status = Callback->Callback (\r
+                         Callback,\r
+                         Private->Function,\r
+                         Received,\r
+                         Packet->Length,\r
+                         (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6\r
+                         );\r
+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+      return EFI_ABORTED;\r
+    }\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  switch (Dhcp6Event) {\r
+\r
+  case Dhcp6SendSolicit:\r
+    //\r
+    // Cache the dhcp discover packet to mode data directly.\r
+    //\r
+    CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);\r
+    break;\r
+\r
+  case Dhcp6RcvdAdvertise:\r
+    Status = EFI_NOT_READY;\r
+    if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {\r
+      //\r
+      // Cache the dhcp offers to OfferBuffer[] for select later, and record\r
+      // the OfferIndex and OfferCount.\r
+      //\r
+      PxeBcCacheDhcp6Offer (Private, Packet);\r
+    }\r
+    break;\r
+\r
+  case Dhcp6SendRequest:\r
+    //\r
+    // Store the request packet as seed packet for discover.\r
+    //\r
+    if (Private->Dhcp6Request != NULL) {\r
+      FreePool (Private->Dhcp6Request);\r
+    }\r
+    Private->Dhcp6Request = AllocateZeroPool (Packet->Size);\r
+    if (Private->Dhcp6Request != NULL) {\r
+      CopyMem (Private->Dhcp6Request, Packet, Packet->Size);\r
+    }\r
+    break;\r
+\r
+  case Dhcp6SelectAdvertise:\r
+    //\r
+    // Select offer by the default policy or by order, and record the SelectIndex\r
+    // and SelectProxyType.\r
+    //\r
+    PxeBcSelectDhcp6Offer (Private);\r
+\r
+    if (Private->SelectIndex == 0) {\r
+      Status = EFI_ABORTED;\r
+    } else {\r
+      ASSERT (NewPacket != NULL);\r
+      SelectAd   = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;\r
+      *NewPacket = AllocateZeroPool (SelectAd->Size);\r
+      ASSERT (*NewPacket != NULL);\r
+      CopyMem (*NewPacket, SelectAd, SelectAd->Size);\r
+    }\r
+    break;\r
+\r
+  case Dhcp6RcvdReply:\r
+    //\r
+    // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data\r
+    // without verification.\r
+    //\r
+    ASSERT (Private->SelectIndex != 0);\r
+    PxeBcCopyDhcp6Ack (Private, Packet, FALSE);\r
+    break;\r
+\r
+  default:\r
+    ASSERT (0);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Build and send out the request packet for the bootfile, and parse the reply.\r
+\r
+  @param[in]  Private               The pointer to PxeBc private data.\r
+  @param[in]  Type                  PxeBc option boot item type.\r
+  @param[in]  Layer                 The pointer to option boot item layer.\r
+  @param[in]  UseBis                Use BIS or not.\r
+  @param[in]  DestIp                The pointer to the server address.\r
+\r
+  @retval     EFI_SUCCESS           Successfully discovered the boot file.\r
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.\r
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.\r
+  @retval     Others                Failed to discover the boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6Discover (\r
+  IN  PXEBC_PRIVATE_DATA              *Private,\r
+  IN  UINT16                          Type,\r
+  IN  UINT16                          *Layer,\r
+  IN  BOOLEAN                         UseBis,\r
+  IN  EFI_IP_ADDRESS                  *DestIp\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT          SrcPort;\r
+  EFI_PXE_BASE_CODE_UDP_PORT          DestPort;\r
+  EFI_PXE_BASE_CODE_MODE              *Mode;\r
+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;\r
+  EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;\r
+  UINTN                               DiscoverLen;\r
+  EFI_DHCP6_PACKET                    *Request;\r
+  UINTN                               RequestLen;\r
+  EFI_DHCP6_PACKET                    *Reply;\r
+  UINT8                               *RequestOpt;\r
+  UINT8                               *DiscoverOpt;\r
+  UINTN                               ReadSize;\r
+  UINT16                              OpFlags;\r
+  UINT16                              OpCode;\r
+  UINT16                              OpLen;\r
+  UINT32                              Xid;\r
+  EFI_STATUS                          Status;\r
+\r
+  PxeBc       = &Private->PxeBc;\r
+  Mode        = PxeBc->Mode;\r
+  Request     = Private->Dhcp6Request;\r
+  SrcPort     = PXEBC_BS_DISCOVER_PORT;\r
+  DestPort    = PXEBC_BS_DISCOVER_PORT;\r
+  OpFlags     = 0;\r
+\r
+  if (!UseBis && Layer != NULL) {\r
+    *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;\r
+  }\r
+\r
+  if (Request == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));\r
+  if (Discover == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Build the discover packet by the cached request packet before.\r
+  //\r
+  Xid                     = NET_RANDOM (NetRandomInitSeed ());\r
+  Discover->TransactionId = HTONL (Xid);\r
+  Discover->MessageType   = Request->Dhcp6.Header.MessageType;\r
+  RequestOpt              = Request->Dhcp6.Option;\r
+  DiscoverOpt             = Discover->DhcpOptions;\r
+  DiscoverLen             = sizeof (EFI_DHCP6_HEADER);\r
+  RequestLen              = DiscoverLen;\r
+\r
+  while (RequestLen < Request->Length) {\r
+    OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);\r
+    OpLen  = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);\r
+    if (OpCode != EFI_DHCP6_IA_TYPE_NA &&\r
+        OpCode != EFI_DHCP6_IA_TYPE_TA) {\r
+      //\r
+      // Copy all the options except IA option.\r
+      //\r
+      CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);\r
+      DiscoverOpt += (OpLen + 4);\r
+      DiscoverLen += (OpLen + 4);\r
+    }\r
+    RequestOpt += (OpLen + 4);\r
+    RequestLen += (OpLen + 4);\r
+  }\r
+\r
+  Status = PxeBc->UdpWrite (\r
+                    PxeBc,\r
+                    OpFlags,\r
+                    &Private->ServerIp,\r
+                    &DestPort,\r
+                    NULL,\r
+                    &Private->StationIp,\r
+                    &SrcPort,\r
+                    NULL,\r
+                    NULL,\r
+                    &DiscoverLen,\r
+                    (VOID *) Discover\r
+                    );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Cache the right PXE reply packet here, set valid flag later.\r
+  // Especially for PXE discover packet, store it into mode data here.\r
+  //\r
+  if (Private->IsDoDiscover) {\r
+    CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);\r
+    Reply = &Private->PxeReply.Dhcp6.Packet.Ack;\r
+  } else {\r
+    Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;\r
+  }\r
+  ReadSize = (UINTN) Reply->Size;\r
+\r
+  Status = PxeBc->UdpRead (\r
+                    PxeBc,\r
+                    OpFlags,\r
+                    &Private->StationIp,\r
+                    &SrcPort,\r
+                    &Private->ServerIp,\r
+                    &DestPort,\r
+                    NULL,\r
+                    NULL,\r
+                    &ReadSize,\r
+                    (VOID *) &Reply->Dhcp6\r
+                    );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.\r
+\r
+  @param[in]  Private           The pointer to PxeBc private data.\r
+  @param[in]  Dhcp6             The pointer to the EFI_DHCP6_PROTOCOL\r
+\r
+  @retval EFI_SUCCESS           The S.A.R.R. process successfully finished.\r
+  @retval Others                Failed to finish the S.A.R.R. process.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6Sarr (\r
+  IN PXEBC_PRIVATE_DATA            *Private,\r
+  IN EFI_DHCP6_PROTOCOL            *Dhcp6\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE           *PxeMode;\r
+  EFI_DHCP6_CONFIG_DATA            Config;\r
+  EFI_DHCP6_MODE_DATA              Mode;\r
+  EFI_DHCP6_RETRANSMISSION         *Retransmit;\r
+  EFI_DHCP6_PACKET_OPTION          *OptList[PXEBC_DHCP6_OPTION_MAX_NUM];\r
+  UINT8                            Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];\r
+  UINT32                           OptCount;\r
+  EFI_STATUS                       Status;\r
+\r
+  Status     = EFI_SUCCESS;\r
+  PxeMode    = Private->PxeBc.Mode;\r
+\r
+  //\r
+  // Build option list for the request packet.\r
+  //\r
+  OptCount   = PxeBcBuildDhcp6Options (Private, OptList, Buffer);\r
+  ASSERT (OptCount> 0);\r
+\r
+  Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));\r
+  if (Retransmit == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));\r
+  ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));\r
+\r
+  Config.OptionCount           = OptCount;\r
+  Config.OptionList            = OptList;\r
+  Config.Dhcp6Callback         = PxeBcDhcp6CallBack;\r
+  Config.CallbackContext       = Private;\r
+  Config.IaInfoEvent           = NULL;\r
+  Config.RapidCommit           = FALSE;\r
+  Config.ReconfigureAccept     = FALSE;\r
+  Config.IaDescriptor.IaId     = 1;\r
+  Config.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;\r
+  Config.SolicitRetransmission = Retransmit;\r
+  Retransmit->Irt              = 4;\r
+  Retransmit->Mrc              = 4;\r
+  Retransmit->Mrt              = 32;\r
+  Retransmit->Mrd              = 60;\r
+\r
+  //\r
+  // Configure the DHCPv6 instance for PXE boot.\r
+  //\r
+  Status = Dhcp6->Configure (Dhcp6, &Config);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Retransmit);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Initialize the record fields for DHCPv6 offer in private data.\r
+  //\r
+  Private->IsProxyRecved = FALSE;\r
+  Private->OfferNum      = 0;\r
+  Private->SelectIndex   = 0;\r
+  ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));\r
+  ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));\r
+\r
+\r
+  //\r
+  // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.\r
+  //\r
+  Status = Dhcp6->Start (Dhcp6);\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_ICMP_ERROR) {\r
+      PxeMode->IcmpErrorReceived = TRUE;\r
+    }\r
+    Dhcp6->Configure (Dhcp6, NULL);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Get the acquired IPv6 address and store them.\r
+  //\r
+  Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    Dhcp6->Stop (Dhcp6);\r
+    return Status;\r
+  }\r
+\r
+  ASSERT (Mode.Ia->State == Dhcp6Bound);\r
+  CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));\r
+  CopyMem (&PxeMode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);\r
+  if (EFI_ERROR (Status)) {\r
+    Dhcp6->Stop (Dhcp6);\r
+    return Status;\r
+  }\r
+\r
+  Status = PxeBcFlushStaionIp (Private, &Private->StationIp, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    PxeBcUnregisterIp6Address (Private);\r
+    Dhcp6->Stop (Dhcp6);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Check the selected offer whether BINL retry is needed.\r
+  //\r
+  Status = PxeBcHandleDhcp6Offer (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    PxeBcUnregisterIp6Address (Private);\r
+    Dhcp6->Stop (Dhcp6);\r
+    return Status;\r
+  }\r
+\r
+  AsciiPrint ("\n  Station IP address is ");\r
+\r
+  PxeBcShowIp6Addr (&Private->StationIp.v6);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h
new file mode 100644 (file)
index 0000000..0eccacb
--- /dev/null
@@ -0,0 +1,277 @@
+/** @file\r
+  Functions declaration related with DHCPv6 for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2009 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_DHCP6_H__\r
+#define __EFI_PXEBC_DHCP6_H__\r
+\r
+#define PXEBC_DHCP6_OPTION_MAX_NUM        16\r
+#define PXEBC_DHCP6_OPTION_MAX_SIZE       312\r
+#define PXEBC_DHCP6_PACKET_MAX_SIZE       1472\r
+#define PXEBC_DHCP6_MAPPING_TIMEOUT       50000000   // 5 seconds, unit is 10nanosecond.\r
+#define PXEBC_IP6_POLICY_MAX              0xff\r
+\r
+#define PXEBC_DHCP6_S_PORT                547\r
+#define PXEBC_DHCP6_C_PORT                546\r
+\r
+#define PXEBC_DHCP6_OPT_CLIENT_ID         1\r
+#define PXEBC_DHCP6_OPT_SERVER_ID         2\r
+#define PXEBC_DHCP6_OPT_IA_NA             3\r
+#define PXEBC_DHCP6_OPT_IA_TA             4\r
+#define PXEBC_DHCP6_OPT_IAADDR            5\r
+#define PXEBC_DHCP6_OPT_ORO               6\r
+#define PXEBC_DHCP6_OPT_PREFERENCE        7\r
+#define PXEBC_DHCP6_OPT_ELAPSED_TIME      8\r
+#define PXEBC_DHCP6_OPT_REPLAY_MSG        9\r
+#define PXEBC_DHCP6_OPT_AUTH              11\r
+#define PXEBC_DHCP6_OPT_UNICAST           12\r
+#define PXEBC_DHCP6_OPT_STATUS_CODE       13\r
+#define PXEBC_DHCP6_OPT_RAPID_COMMIT      14\r
+#define PXEBC_DHCP6_OPT_USER_CLASS        15\r
+#define PXEBC_DHCP6_OPT_VENDOR_CLASS      16\r
+#define PXEBC_DHCP6_OPT_VENDOR_OPTS       17\r
+#define PXEBC_DHCP6_OPT_INTERFACE_ID      18\r
+#define PXEBC_DHCP6_OPT_RECONFIG_MSG      19\r
+#define PXEBC_DHCP6_OPT_RECONFIG_ACCEPT   20\r
+#define PXEBC_DHCP6_OPT_BOOT_FILE_URL     59    // Assigned by IANA, RFC 5970\r
+#define PXEBC_DHCP6_OPT_BOOT_FILE_PARAM   60    // Assigned by IANA, RFC 5970\r
+#define PXEBC_DHCP6_OPT_ARCH              61    // Assigned by IANA, RFC 5970\r
+#define PXEBC_DHCP6_OPT_UNDI              62    // Assigned by IANA, RFC 5970\r
+#define PXEBC_DHCP6_ENTERPRISE_NUM        343   // TODO: IANA TBD: temporarily using Intel's\r
+#define PXEBC_DHCP6_MAX_BOOT_FILE_SIZE    65535 //   It's a limitation of bit length, 65535*512 bytes.\r
+\r
+\r
+#define PXEBC_DHCP6_IDX_IA_NA             0\r
+#define PXEBC_DHCP6_IDX_BOOT_FILE_URL     1\r
+#define PXEBC_DHCP6_IDX_BOOT_FILE_PARAM   2\r
+#define PXEBC_DHCP6_IDX_VENDOR_CLASS      3\r
+#define PXEBC_DHCP6_IDX_MAX               4\r
+\r
+#define PXEBC_DHCP6_BOOT_FILE_URL_PREFIX  "tftp://"\r
+#define PXEBC_TFTP_URL_SEPARATOR          '/'\r
+#define PXEBC_ADDR_START_DELIMITER        '['\r
+#define PXEBC_ADDR_END_DELIMITER          ']'\r
+\r
+#define GET_NEXT_DHCP6_OPTION(Opt) \\r
+  (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \\r
+  sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1)\r
+\r
+#define GET_DHCP6_OPTION_SIZE(Pkt)  \\r
+  ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER))\r
+\r
+#define IS_PROXY_OFFER(Type) \\r
+  ((Type) == PxeOfferTypeProxyBinl || \\r
+   (Type) == PxeOfferTypeProxyPxe10 || \\r
+   (Type) == PxeOfferTypeProxyWfm11a)\r
+\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+  UINT16 OpCode[256];\r
+} PXEBC_DHCP6_OPTION_ORO;\r
+\r
+typedef struct {\r
+  UINT8 Type;\r
+  UINT8 MajorVer;\r
+  UINT8 MinorVer;\r
+  UINT8 Reserved;\r
+} PXEBC_DHCP6_OPTION_UNDI;\r
+\r
+typedef struct {\r
+  UINT16 Type;\r
+} PXEBC_DHCP6_OPTION_ARCH;\r
+\r
+typedef struct {\r
+  UINT8 ClassIdentifier[10];\r
+  UINT8 ArchitecturePrefix[5];\r
+  UINT8 ArchitectureType[5];\r
+  UINT8 Lit3[1];\r
+  UINT8 InterfaceName[4];\r
+  UINT8 Lit4[1];\r
+  UINT8 UndiMajor[3];\r
+  UINT8 UndiMinor[3];\r
+} PXEBC_CLASS_ID;\r
+\r
+typedef struct {\r
+  UINT32         Vendor;\r
+  UINT16         ClassLen;\r
+  PXEBC_CLASS_ID ClassId;\r
+} PXEBC_DHCP6_OPTION_VENDOR_CLASS;\r
+\r
+#pragma pack()\r
+\r
+typedef union {\r
+  PXEBC_DHCP6_OPTION_ORO            *Oro;\r
+  PXEBC_DHCP6_OPTION_UNDI           *Undi;\r
+  PXEBC_DHCP6_OPTION_ARCH           *Arch;\r
+  PXEBC_DHCP6_OPTION_VENDOR_CLASS   *VendorClass;\r
+} PXEBC_DHCP6_OPTION_ENTRY;\r
+\r
+typedef struct {\r
+  LIST_ENTRY              Link;\r
+  EFI_DHCP6_PACKET_OPTION *Option;\r
+  UINT8                   Precedence;\r
+} PXEBC_DHCP6_OPTION_NODE;\r
+\r
+typedef union {\r
+  EFI_DHCP6_PACKET        Offer;\r
+  EFI_DHCP6_PACKET        Ack;\r
+  UINT8                   Buffer[PXEBC_DHCP6_PACKET_MAX_SIZE];\r
+} PXEBC_DHCP6_PACKET;\r
+\r
+typedef struct {\r
+  PXEBC_DHCP6_PACKET      Packet;\r
+  PXEBC_OFFER_TYPE        OfferType;\r
+  EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_IDX_MAX];\r
+} PXEBC_DHCP6_PACKET_CACHE;\r
+\r
+\r
+/**\r
+  Free all the nodes in the boot file list.\r
+\r
+  @param[in]  Head            The pointer to the head of the list.\r
+\r
+**/\r
+VOID\r
+PxeBcFreeBootFileOption (\r
+  IN LIST_ENTRY               *Head\r
+  );\r
+\r
+\r
+/**\r
+  Parse the Boot File URL option.\r
+\r
+  @param[out]     FileName     The pointer to the boot file name.\r
+  @param[in, out] SrvAddr      The pointer to the boot server address.\r
+  @param[in]      BootFile     The pointer to the boot file URL option data.\r
+  @param[in]      Length       Length of the boot file URL option data.\r
+\r
+  @retval EFI_ABORTED     User canceled the operation.\r
+  @retval EFI_SUCCESS     Selected the boot menu successfully.\r
+  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractBootFileUrl (\r
+     OUT UINT8               **FileName,\r
+  IN OUT EFI_IPv6_ADDRESS    *SrvAddr,\r
+  IN     CHAR8               *BootFile,\r
+  IN     UINT16              Length\r
+  );\r
+\r
+\r
+/**\r
+  Parse the Boot File Parameter option.\r
+\r
+  @param[in]  BootFilePara      The pointer to the boot file parameter option data.\r
+  @param[out] BootFileSize      The pointer to the parsed boot file size.\r
+\r
+  @retval EFI_SUCCESS     Successfully obtained the boot file size from parameter option.\r
+  @retval EFI_NOT_FOUND   Failed to extract the boot file size from parameter option.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractBootFileParam (\r
+  IN  CHAR8                  *BootFilePara,\r
+  OUT UINT16                 *BootFileSize\r
+  );\r
+\r
+\r
+/**\r
+  Parse the cached DHCPv6 packet, including all the options.\r
+\r
+  @param[in]  Cache6           The pointer to a cached DHCPv6 packet.\r
+\r
+  @retval     EFI_SUCCESS      Parsed the DHCPv6 packet successfully.\r
+  @retval     EFI_DEVICE_ERROR Failed to parse and invalid packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcParseDhcp6Packet (\r
+  IN PXEBC_DHCP6_PACKET_CACHE  *Cache6\r
+  );\r
+\r
+\r
+/**\r
+  Register the ready address by Ip6Config protocol.\r
+\r
+  @param[in]  Private             The pointer to the PxeBc private data.\r
+  @param[in]  Address             The pointer to the ready address.\r
+\r
+  @retval     EFI_SUCCESS         Registered the address succesfully.\r
+  @retval     Others              Failed to register the address.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcRegisterIp6Address (\r
+  IN PXEBC_PRIVATE_DATA            *Private,\r
+  IN EFI_IPv6_ADDRESS              *Address\r
+  );\r
+\r
+\r
+/**\r
+  Unregister the address by Ip6Config protocol.\r
+\r
+  @param[in]  Private             The pointer to the PxeBc private data.\r
+\r
+**/\r
+VOID\r
+PxeBcUnregisterIp6Address (\r
+  IN PXEBC_PRIVATE_DATA            *Private\r
+  );\r
+\r
+\r
+/**\r
+  Build and send out the request packet for the bootfile, and parse the reply.\r
+\r
+  @param[in]  Private               The pointer to the PxeBc private data.\r
+  @param[in]  Type                  PxeBc option boot item type.\r
+  @param[in]  Layer                 The pointer to the option boot item layer.\r
+  @param[in]  UseBis                Use BIS or not.\r
+  @param[in]  DestIp                The pointer to the server address.\r
+\r
+  @retval     EFI_SUCCESS           Successfully discovered theboot file.\r
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.\r
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.\r
+  @retval     Others                Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6Discover (\r
+  IN  PXEBC_PRIVATE_DATA            *Private,\r
+  IN  UINT16                        Type,\r
+  IN  UINT16                        *Layer,\r
+  IN  BOOLEAN                       UseBis,\r
+  IN  EFI_IP_ADDRESS                *DestIp\r
+  );\r
+\r
+\r
+/**\r
+  Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.\r
+\r
+  @param[in]  Private           The pointer to the PxeBc private data.\r
+  @param[in]  Dhcp6             The pointer to EFI_DHCP6_PROTOCOL.\r
+\r
+  @retval EFI_SUCCESS           The S.A.R.R. process successfully finished.\r
+  @retval Others                Failed to finish the S.A.R.R. process.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6Sarr (\r
+  IN PXEBC_PRIVATE_DATA            *Private,\r
+  IN EFI_DHCP6_PROTOCOL            *Dhcp6\r
+  );\r
+\r
+#endif\r
+\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c
new file mode 100644 (file)
index 0000000..f785255
--- /dev/null
@@ -0,0 +1,1339 @@
+/** @file\r
+  Driver Binding functions implementationfor for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2007 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gPxeBcDriverBinding = {\r
+  PxeBcDriverBindingSupported,\r
+  PxeBcDriverBindingStart,\r
+  PxeBcDriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+\r
+/**\r
+  Get the Nic handle using any child handle in the IPv4 stack.\r
+\r
+  @param[in]  ControllerHandle    Pointer to child handle over IPv4.\r
+\r
+  @return NicHandle               The pointer to the Nic handle.\r
+\r
+**/\r
+EFI_HANDLE\r
+PxeBcGetNicByIp4Children (\r
+  IN EFI_HANDLE                 ControllerHandle\r
+  )\r
+{\r
+  EFI_HANDLE                    NicHandle;\r
+\r
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);\r
+  if (NicHandle == NULL) {\r
+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);\r
+    if (NicHandle == NULL) {\r
+      NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);\r
+      if (NicHandle == NULL) {\r
+        NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);\r
+        if (NicHandle == NULL) {\r
+          NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid);\r
+          if (NicHandle == NULL) {\r
+            return NULL;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  return NicHandle;\r
+}\r
+\r
+\r
+/**\r
+  Get the Nic handle using any child handle in the IPv6 stack.\r
+\r
+  @param[in]  ControllerHandle    Pointer to child handle over IPv6.\r
+\r
+  @return NicHandle               The pointer to the Nic handle.\r
+\r
+**/\r
+EFI_HANDLE\r
+PxeBcGetNicByIp6Children (\r
+  IN EFI_HANDLE                  ControllerHandle\r
+  )\r
+{\r
+  EFI_HANDLE                     NicHandle;\r
+\r
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid);\r
+  if (NicHandle == NULL) {\r
+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);\r
+    if (NicHandle == NULL) {\r
+      NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);\r
+      if (NicHandle == NULL) {\r
+        NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp6ProtocolGuid);\r
+        if (NicHandle == NULL) {\r
+          return NULL;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  return NicHandle;\r
+}\r
+\r
+\r
+/**\r
+  Destroy the opened instances based on IPv4.\r
+\r
+  @param[in]  This              Pointer to the EFI_DRIVER_BINDING_PROTOCOL.\r
+  @param[in]  Private           Pointer to PXEBC_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+PxeBcDestroyIp4Children (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN PXEBC_PRIVATE_DATA           *Private\r
+  )\r
+{\r
+  ASSERT(Private != NULL);\r
+\r
+  if (Private->ArpChild != NULL) {\r
+    //\r
+    // Close Arp for PxeBc->Arp and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+           Private->ArpChild,\r
+           &gEfiArpProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Private->Controller\r
+           );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiArpServiceBindingProtocolGuid,\r
+      Private->ArpChild\r
+      );\r
+  }\r
+\r
+  if (Private->Ip4Child != NULL) {\r
+    //\r
+    // Close Ip4 for background ICMP error message and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+           Private->Ip4Child,\r
+           &gEfiIp4ProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Private->Controller\r
+           );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiIp4ServiceBindingProtocolGuid,\r
+      Private->Ip4Child\r
+      );\r
+  }\r
+\r
+  if (Private->Udp4WriteChild != NULL) {\r
+    //\r
+    // Close Udp4 for PxeBc->UdpWrite and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+           Private->Udp4WriteChild,\r
+           &gEfiUdp4ProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Private->Controller\r
+           );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiUdp4ServiceBindingProtocolGuid,\r
+      Private->Udp4WriteChild\r
+      );\r
+  }\r
+\r
+  if (Private->Udp4ReadChild != NULL) {\r
+    //\r
+    // Close Udp4 for PxeBc->UdpRead and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+          Private->Udp4ReadChild,\r
+          &gEfiUdp4ProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Private->Controller\r
+          );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiUdp4ServiceBindingProtocolGuid,\r
+      Private->Udp4ReadChild\r
+      );\r
+  }\r
+\r
+  if (Private->Mtftp4Child != NULL) {\r
+    //\r
+    // Close Mtftp4 for PxeBc->Mtftp4 and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+          Private->Mtftp4Child,\r
+          &gEfiMtftp4ProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Private->Controller\r
+          );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiMtftp4ServiceBindingProtocolGuid,\r
+      Private->Mtftp4Child\r
+      );\r
+  }\r
+\r
+  if (Private->Dhcp4Child != NULL) {\r
+    //\r
+    // Close Dhcp4 for PxeBc->Dhcp4 and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+          Private->Dhcp4Child,\r
+          &gEfiDhcp4ProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Private->Controller\r
+          );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiDhcp4ServiceBindingProtocolGuid,\r
+      Private->Dhcp4Child\r
+      );\r
+  }\r
+\r
+  if (Private->Ip4Nic != NULL) {\r
+    //\r
+    // Close PxeBc from the parent Nic handle and destroy the virtual handle.\r
+    //\r
+    gBS->CloseProtocol (\r
+           Private->Controller,\r
+           &gEfiPxeBaseCodeProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Private->Ip4Nic->Controller\r
+           );\r
+\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           Private->Ip4Nic->Controller,\r
+           &gEfiDevicePathProtocolGuid,\r
+           Private->Ip4Nic->DevicePath,\r
+           &gEfiLoadFileProtocolGuid,\r
+           &Private->Ip4Nic->LoadFile,\r
+           NULL\r
+           );\r
+    FreePool (Private->Ip4Nic);\r
+  }\r
+\r
+  Private->ArpChild         = NULL;\r
+  Private->Ip4Child         = NULL;\r
+  Private->Udp4WriteChild   = NULL;\r
+  Private->Udp4ReadChild    = NULL;\r
+  Private->Mtftp4Child      = NULL;\r
+  Private->Dhcp4Child       = NULL;\r
+  Private->Ip4Nic           = NULL;\r
+}\r
+\r
+\r
+/**\r
+  Destroy the opened instances based on IPv6.\r
+\r
+  @param[in]  This              Pointer to the EFI_DRIVER_BINDING_PROTOCOL.\r
+  @param[in]  Private           Pointer to PXEBC_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+PxeBcDestroyIp6Children (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN PXEBC_PRIVATE_DATA           *Private\r
+  )\r
+{\r
+  ASSERT(Private != NULL);\r
+\r
+  if (Private->Ip6Child != NULL) {\r
+    //\r
+    // Close Ip6 for Ip6->Ip6Config and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+          Private->Ip6Child,\r
+          &gEfiIp6ProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Private->Controller\r
+          );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiIp6ServiceBindingProtocolGuid,\r
+      Private->Ip6Child\r
+      );\r
+  }\r
+\r
+  if (Private->Udp6WriteChild != NULL) {\r
+    //\r
+    // Close Udp6 for PxeBc->UdpWrite and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+           Private->Udp6WriteChild,\r
+           &gEfiUdp6ProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Private->Controller\r
+           );\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiUdp6ServiceBindingProtocolGuid,\r
+      Private->Udp6WriteChild\r
+      );\r
+  }\r
+\r
+  if (Private->Udp6ReadChild != NULL) {\r
+    //\r
+    // Close Udp6 for PxeBc->UdpRead and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+          Private->Udp6ReadChild,\r
+          &gEfiUdp6ProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Private->Controller\r
+          );\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiUdp6ServiceBindingProtocolGuid,\r
+      Private->Udp6ReadChild\r
+      );\r
+  }\r
+\r
+  if (Private->Mtftp6Child != NULL) {\r
+    //\r
+    // Close Mtftp6 for PxeBc->Mtftp and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+          Private->Mtftp6Child,\r
+          &gEfiMtftp6ProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Private->Controller\r
+          );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiMtftp6ServiceBindingProtocolGuid,\r
+      Private->Mtftp6Child\r
+      );\r
+  }\r
+\r
+  if (Private->Dhcp6Child != NULL) {\r
+    //\r
+    // Close Dhcp6 for PxeBc->Dhcp and destroy the instance.\r
+    //\r
+    gBS->CloseProtocol (\r
+          Private->Dhcp6Child,\r
+          &gEfiDhcp6ProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Private->Controller\r
+          );\r
+\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      This->DriverBindingHandle,\r
+      &gEfiDhcp6ServiceBindingProtocolGuid,\r
+      Private->Dhcp6Child\r
+      );\r
+  }\r
+\r
+  if (Private->Ip6Nic != NULL) {\r
+    //\r
+    // Close PxeBc from the parent Nic handle and destroy the virtual handle.\r
+    //\r
+    gBS->CloseProtocol (\r
+           Private->Controller,\r
+           &gEfiPxeBaseCodeProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Private->Ip6Nic->Controller\r
+           );\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           Private->Ip6Nic->Controller,\r
+           &gEfiDevicePathProtocolGuid,\r
+           Private->Ip6Nic->DevicePath,\r
+           &gEfiLoadFileProtocolGuid,\r
+           &Private->Ip6Nic->LoadFile,\r
+           NULL\r
+           );\r
+    FreePool (Private->Ip6Nic);\r
+  }\r
+\r
+  Private->Ip6Child           = NULL;\r
+  Private->Udp6WriteChild     = NULL;\r
+  Private->Udp6ReadChild      = NULL;\r
+  Private->Mtftp6Child        = NULL;\r
+  Private->Dhcp6Child         = NULL;\r
+  Private->Ip6Nic             = NULL;\r
+  Private->Mode.Ipv6Available = FALSE;\r
+}\r
+\r
+\r
+/**\r
+  Create the opened instances based on IPv4.\r
+\r
+  @param[in]  This              Pointer to EFI_DRIVER_BINDING_PROTOCOL.\r
+  @param[in]  ControllerHandle  Handle of the child to destroy.\r
+  @param[in]  Private Handle    Pointer to PXEBC_PRIVATE_DATA.\r
+\r
+  @retval EFI_SUCCESS           The instances based on IPv4 were all created successfully.\r
+  @retval Others                An unexpected error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcCreateIp4Children (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN PXEBC_PRIVATE_DATA           *Private\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  IPv4_DEVICE_PATH                Ip4Node;\r
+  EFI_PXE_BASE_CODE_PROTOCOL      *PxeBc;\r
+  EFI_PXE_BASE_CODE_MODE          *Mode;\r
+  EFI_UDP4_CONFIG_DATA            *Udp4CfgData;\r
+  EFI_IP4_CONFIG_DATA             *Ip4CfgData;\r
+  EFI_IP4_MODE_DATA               Ip4ModeData;\r
+\r
+  if (Private->Ip4Nic != NULL) {\r
+    //\r
+    // Already created before.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Create Dhcp4 child and open Dhcp4 protocol for PxeBc->Dhcp.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiDhcp4ServiceBindingProtocolGuid,\r
+             &Private->Dhcp4Child\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Dhcp4Child,\r
+                  &gEfiDhcp4ProtocolGuid,\r
+                  (VOID **) &Private->Dhcp4,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create Mtftp4 child and open Mtftp4 protocol for PxeBc->Mtftp.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiMtftp4ServiceBindingProtocolGuid,\r
+             &Private->Mtftp4Child\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Mtftp4Child,\r
+                  &gEfiMtftp4ProtocolGuid,\r
+                  (VOID **) &Private->Mtftp4,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create Udp4 child and open Udp4 protocol for PxeBc->UdpRead.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiUdp4ServiceBindingProtocolGuid,\r
+             &Private->Udp4ReadChild\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Udp4ReadChild,\r
+                  &gEfiUdp4ProtocolGuid,\r
+                  (VOID **) &Private->Udp4Read,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create Udp4 child and open Udp4 protocol for PxeBc->UdpWrite.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiUdp4ServiceBindingProtocolGuid,\r
+             &Private->Udp4WriteChild\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Udp4WriteChild,\r
+                  &gEfiUdp4ProtocolGuid,\r
+                  (VOID **) &Private->Udp4Write,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create Arp child and open Arp protocol for PxeBc->Arp.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiArpServiceBindingProtocolGuid,\r
+             &Private->ArpChild\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->ArpChild,\r
+                  &gEfiArpProtocolGuid,\r
+                  (VOID **) &Private->Arp,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create Ip4 child and open Ip4 protocol for background ICMP packets.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiIp4ServiceBindingProtocolGuid,\r
+             &Private->Ip4Child\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Ip4Child,\r
+                  &gEfiIp4ProtocolGuid,\r
+                  (VOID **) &Private->Ip4,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Get max packet size from Ip4 to calculate block size for Tftp later.\r
+  //\r
+  Status = Private->Ip4->GetModeData (Private->Ip4, &Ip4ModeData, NULL, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Private->Ip4MaxPacketSize = Ip4ModeData.MaxPacketSize;\r
+\r
+  Private->Ip4Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));\r
+  if (Private->Ip4Nic == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Private->Ip4Nic->Private   = Private;\r
+  Private->Ip4Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;\r
+\r
+  //\r
+  // Create a device path node for Ipv4 virtual nic, and append it.\r
+  //\r
+  ZeroMem (&Ip4Node, sizeof (IPv4_DEVICE_PATH));\r
+  Ip4Node.Header.Type     = MESSAGING_DEVICE_PATH;\r
+  Ip4Node.Header.SubType  = MSG_IPv4_DP;\r
+  Ip4Node.StaticIpAddress = FALSE;\r
+\r
+  SetDevicePathNodeLength (&Ip4Node.Header, sizeof (Ip4Node));\r
+\r
+  Private->Ip4Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip4Node.Header);\r
+\r
+  if (Private->Ip4Nic->DevicePath == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  CopyMem (\r
+    &Private->Ip4Nic->LoadFile,\r
+    &gLoadFileProtocolTemplate,\r
+    sizeof (EFI_LOAD_FILE_PROTOCOL)\r
+    );\r
+\r
+  //\r
+  // Create a new handle for IPv4 virtual nic,\r
+  // and install PxeBaseCode, LoadFile and DevicePath protocols.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Private->Ip4Nic->Controller,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  Private->Ip4Nic->DevicePath,\r
+                  &gEfiLoadFileProtocolGuid,\r
+                  &Private->Ip4Nic->LoadFile,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Open PxeBaseCode protocol by child to setup a parent-child relationship between\r
+  // real NIC handle and the virtual IPv4 NIC handle.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiPxeBaseCodeProtocolGuid,\r
+                  (VOID **) &PxeBc,\r
+                  This->DriverBindingHandle,\r
+                  Private->Ip4Nic->Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Set default configure data for Udp4Read and Ip4 instance.\r
+  //\r
+  Mode                            = PxeBc->Mode;\r
+  Udp4CfgData                     = &Private->Udp4CfgData;\r
+  Ip4CfgData                      = &Private->Ip4CfgData;\r
+\r
+  Udp4CfgData->AcceptBroadcast    = TRUE;\r
+  Udp4CfgData->AcceptAnyPort      = TRUE;\r
+  Udp4CfgData->AllowDuplicatePort = TRUE;\r
+  Udp4CfgData->TypeOfService      = Mode->ToS;\r
+  Udp4CfgData->TimeToLive         = Mode->TTL;\r
+  Udp4CfgData->ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;\r
+  Udp4CfgData->TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;\r
+\r
+  Ip4CfgData->AcceptIcmpErrors    = TRUE;\r
+  Ip4CfgData->DefaultProtocol     = EFI_IP_PROTO_ICMP;\r
+  Ip4CfgData->TypeOfService       = Mode->ToS;\r
+  Ip4CfgData->TimeToLive          = Mode->TTL;\r
+  Ip4CfgData->ReceiveTimeout      = PXEBC_DEFAULT_LIFETIME;\r
+  Ip4CfgData->TransmitTimeout     = PXEBC_DEFAULT_LIFETIME;\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  PxeBcDestroyIp4Children (This, Private);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Create the opened instances based on IPv6.\r
+\r
+  @param[in]  This              Pointer to EFI_DRIVER_BINDING_PROTOCOL.\r
+  @param[in]  ControllerHandle  Handle of the child to destroy.\r
+  @param[in]  Private Handle    Pointer to PXEBC_PRIVATE_DATA.\r
+\r
+  @retval EFI_SUCCESS           The instances based on IPv6 were all created successfully.\r
+  @retval Others                An unexpected error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcCreateIp6Children (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN PXEBC_PRIVATE_DATA           *Private\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  IPv6_DEVICE_PATH                Ip6Node;\r
+  EFI_PXE_BASE_CODE_PROTOCOL      *PxeBc;\r
+  EFI_UDP6_CONFIG_DATA            *Udp6CfgData;\r
+  EFI_IP6_CONFIG_DATA             *Ip6CfgData;\r
+  EFI_IP6_MODE_DATA               Ip6ModeData;\r
+\r
+  if (Private->Ip6Nic != NULL) {\r
+    //\r
+    // Already created before.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Private->Ip6Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));\r
+\r
+  if (Private->Ip6Nic == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Private->Ip6Nic->Private   = Private;\r
+  Private->Ip6Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;\r
+\r
+  //\r
+  // Create Dhcp6 child and open Dhcp6 protocol for PxeBc->Dhcp.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiDhcp6ServiceBindingProtocolGuid,\r
+             &Private->Dhcp6Child\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Dhcp6Child,\r
+                  &gEfiDhcp6ProtocolGuid,\r
+                  (VOID **) &Private->Dhcp6,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create Mtftp6 child and open Mtftp6 protocol for PxeBc->Mtftp.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiMtftp6ServiceBindingProtocolGuid,\r
+             &Private->Mtftp6Child\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Mtftp6Child,\r
+                  &gEfiMtftp6ProtocolGuid,\r
+                  (VOID **) &Private->Mtftp6,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create Udp6 child and open Udp6 protocol for PxeBc->UdpRead.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiUdp6ServiceBindingProtocolGuid,\r
+             &Private->Udp6ReadChild\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Udp6ReadChild,\r
+                  &gEfiUdp6ProtocolGuid,\r
+                  (VOID **) &Private->Udp6Read,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create Udp6 child and open Udp6 protocol for PxeBc->UdpWrite.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiUdp6ServiceBindingProtocolGuid,\r
+             &Private->Udp6WriteChild\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Udp6WriteChild,\r
+                  &gEfiUdp6ProtocolGuid,\r
+                  (VOID **) &Private->Udp6Write,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create Ip6 child and open Ip6 protocol for background ICMP6 packets.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             This->DriverBindingHandle,\r
+             &gEfiIp6ServiceBindingProtocolGuid,\r
+             &Private->Ip6Child\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->Ip6Child,\r
+                  &gEfiIp6ProtocolGuid,\r
+                  (VOID **) &Private->Ip6,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Get max packet size from Ip6 to calculate block size for Tftp later.\r
+  //\r
+  Status = Private->Ip6->GetModeData (Private->Ip6, &Ip6ModeData, NULL, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Private->Ip6MaxPacketSize = Ip6ModeData.MaxPacketSize;\r
+\r
+  //\r
+  // Locate Ip6->Ip6Config and store it for set IPv6 address.\r
+  //\r
+  Status = gBS->HandleProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiIp6ConfigProtocolGuid,\r
+                  (VOID **) &Private->Ip6Cfg\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Create a device path node for Ipv6 virtual nic, and append it.\r
+  //\r
+  ZeroMem (&Ip6Node, sizeof (IPv6_DEVICE_PATH));\r
+  Ip6Node.Header.Type     = MESSAGING_DEVICE_PATH;\r
+  Ip6Node.Header.SubType  = MSG_IPv6_DP;\r
+  Ip6Node.StaticIpAddress = FALSE;\r
+\r
+  SetDevicePathNodeLength (&Ip6Node.Header, sizeof (Ip6Node));\r
+\r
+  Private->Ip6Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip6Node.Header);\r
+\r
+  if (Private->Ip6Nic->DevicePath == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  CopyMem (\r
+    &Private->Ip6Nic->LoadFile,\r
+    &gLoadFileProtocolTemplate,\r
+    sizeof (EFI_LOAD_FILE_PROTOCOL)\r
+    );\r
+\r
+  //\r
+  // Create a new handle for IPv6 virtual nic,\r
+  // and install PxeBaseCode, LoadFile and DevicePath protocols.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Private->Ip6Nic->Controller,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  Private->Ip6Nic->DevicePath,\r
+                  &gEfiLoadFileProtocolGuid,\r
+                  &Private->Ip6Nic->LoadFile,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Open PxeBaseCode protocol by child to setup a parent-child relationship between\r
+  // real NIC handle and the virtual IPv6 NIC handle.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiPxeBaseCodeProtocolGuid,\r
+                  (VOID **) &PxeBc,\r
+                  This->DriverBindingHandle,\r
+                  Private->Ip6Nic->Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Set IPv6 avaiable flag and set default configure data for\r
+  // Udp6Read and Ip6 instance.\r
+  //\r
+  Private->Mode.Ipv6Available     = TRUE;\r
+  Udp6CfgData                     = &Private->Udp6CfgData;\r
+  Ip6CfgData                      = &Private->Ip6CfgData;\r
+\r
+  Udp6CfgData->AcceptAnyPort      = TRUE;\r
+  Udp6CfgData->AllowDuplicatePort = TRUE;\r
+  Udp6CfgData->HopLimit           = PXEBC_DEFAULT_HOPLIMIT;\r
+  Udp6CfgData->ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;\r
+  Udp6CfgData->TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;\r
+\r
+  Ip6CfgData->AcceptIcmpErrors    = TRUE;\r
+  Ip6CfgData->DefaultProtocol     = IP6_ICMP;\r
+  Ip6CfgData->HopLimit            = PXEBC_DEFAULT_HOPLIMIT;\r
+  Ip6CfgData->ReceiveTimeout      = PXEBC_DEFAULT_LIFETIME;\r
+  Ip6CfgData->TransmitTimeout     = PXEBC_DEFAULT_LIFETIME;\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  PxeBcDestroyIp6Children (This, Private);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The entry point for UefiPxeBc driver that installs the driver\r
+  binding and component name protocol on its image.\r
+\r
+  @param[in]  ImageHandle          The Image handle of the driver.\r
+  @param[in]  SystemTable          The system table.\r
+\r
+  @return EFI_SUCCESS\r
+  @return Others\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverEntryPoint (\r
+  IN EFI_HANDLE             ImageHandle,\r
+  IN EFI_SYSTEM_TABLE       *SystemTable\r
+  )\r
+{\r
+  return EfiLibInstallDriverBindingComponentName2 (\r
+           ImageHandle,\r
+           SystemTable,\r
+           &gPxeBcDriverBinding,\r
+           ImageHandle,\r
+           &gPxeBcComponentName,\r
+           &gPxeBcComponentName2\r
+           );\r
+}\r
+\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle. This service\r
+  is called by the EFI boot service ConnectController(). In\r
+  order to make drivers as small as possible, there are a few calling\r
+  restrictions for this service. ConnectController() must\r
+  follow these calling restrictions. If any other agent wishes to call\r
+  Supported() it must also follow these calling restrictions.\r
+\r
+  @param[in]  This                The pointer to the driver binding protocol.\r
+  @param[in]  ControllerHandle    The handle of device to be tested.\r
+  @param[in]  RemainingDevicePath Optional parameter used to pick a specific child\r
+                                  device to be started.\r
+\r
+  @retval EFI_SUCCESS         This driver supports this device.\r
+  @retval EFI_UNSUPPORTED     This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                      Ip4Status;\r
+  EFI_STATUS                      Ip6Status;\r
+\r
+  //\r
+  // Try to open the Mtftp4 and Dhcp4 protocol to test whether IPv4 stack is ready.\r
+  //\r
+  Ip4Status = gBS->OpenProtocol (\r
+                     ControllerHandle,\r
+                     &gEfiDhcp4ServiceBindingProtocolGuid,\r
+                     NULL,\r
+                     This->DriverBindingHandle,\r
+                     ControllerHandle,\r
+                     EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                     );\r
+  if (!EFI_ERROR (Ip4Status)) {\r
+    Ip4Status = gBS->OpenProtocol (\r
+                       ControllerHandle,\r
+                       &gEfiMtftp4ServiceBindingProtocolGuid,\r
+                       NULL,\r
+                       This->DriverBindingHandle,\r
+                       ControllerHandle,\r
+                       EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                       );\r
+  }\r
+\r
+  //\r
+  // Try to open the Mtftp6 and Dhcp6 protocol to test whether IPv4 stack is ready.\r
+  //\r
+  Ip6Status = gBS->OpenProtocol (\r
+                     ControllerHandle,\r
+                     &gEfiDhcp6ServiceBindingProtocolGuid,\r
+                     NULL,\r
+                     This->DriverBindingHandle,\r
+                     ControllerHandle,\r
+                     EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                     );\r
+  if (!EFI_ERROR (Ip6Status)) {\r
+    Ip6Status = gBS->OpenProtocol (\r
+                       ControllerHandle,\r
+                       &gEfiMtftp6ServiceBindingProtocolGuid,\r
+                       NULL,\r
+                       This->DriverBindingHandle,\r
+                       ControllerHandle,\r
+                       EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                       );\r
+  }\r
+\r
+  //\r
+  // It's unsupported case if both stack are not ready.\r
+  //\r
+  if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Start this driver on ControllerHandle. This service is called by the\r
+  EFI boot service ConnectController(). In order to make\r
+  drivers as small as possible, there are a few calling restrictions for\r
+  this service. ConnectController() must follow these\r
+  calling restrictions. If any other agent wishes to call Start() it\r
+  must also follow these calling restrictions.\r
+\r
+  @param[in]  This                 The pointer to the driver binding protocol.\r
+  @param[in]  ControllerHandle     The handle of device to be started.\r
+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child\r
+                                   device to be started.\r
+\r
+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA              *Private;\r
+  EFI_PXE_BASE_CODE_PROTOCOL      *PxeBc;\r
+  EFI_STATUS                      Status;\r
+  EFI_STATUS                      Ip4Status;\r
+  EFI_STATUS                      Ip6Status;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiPxeBaseCodeProtocolGuid,\r
+                  (VOID **) &PxeBc,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Skip the initialization if the driver has been started already.\r
+    //\r
+    Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc);\r
+  } else {\r
+    //\r
+    // If the driver has not been started yet, it should do initialization.\r
+    //\r
+    Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA));\r
+    if (Private == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    CopyMem (\r
+      &Private->PxeBc,\r
+      &gPxeBcProtocolTemplate,\r
+      sizeof (EFI_PXE_BASE_CODE_PROTOCOL)\r
+      );\r
+\r
+    Private->Signature          = PXEBC_PRIVATE_DATA_SIGNATURE;\r
+    Private->Controller         = ControllerHandle;\r
+    Private->Image              = This->ImageHandle;\r
+    Private->PxeBc.Mode         = &Private->Mode;\r
+    Private->Mode.Ipv6Supported = TRUE;\r
+    Private->Mode.AutoArp       = TRUE;\r
+    Private->Mode.TTL           = DEFAULT_TTL;\r
+    Private->Mode.ToS           = DEFAULT_ToS;\r
+\r
+    //\r
+    // Open device path to prepare for appending virtual NIC node.\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    ControllerHandle,\r
+                    &gEfiDevicePathProtocolGuid,\r
+                    (VOID **) &Private->DevicePath,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    //\r
+    // Get the NII interface if it exists, it's not required.\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    ControllerHandle,\r
+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
+                    (VOID **) &Private->Nii,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      Private->Nii = NULL;\r
+    }\r
+\r
+    //\r
+    // Install PxeBaseCode protocol onto the real NIC handler.\r
+    //\r
+    Status = gBS->InstallProtocolInterface (\r
+                    &ControllerHandle,\r
+                    &gEfiPxeBaseCodeProtocolGuid,\r
+                    EFI_NATIVE_INTERFACE,\r
+                    &Private->PxeBc\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Try to create virtual NIC handle for IPv4.\r
+  //\r
+  Ip4Status = PxeBcCreateIp4Children (This, ControllerHandle, Private);\r
+\r
+  //\r
+  // Try to create virtual NIC handle for IPv6.\r
+  //\r
+  Ip6Status = PxeBcCreateIp6Children (This, ControllerHandle, Private);\r
+\r
+  if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) {\r
+    //\r
+    // Failed to start PXE driver if IPv4 and IPv6 stack are both not available.\r
+    //\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  gBS->UninstallProtocolInterface (\r
+         ControllerHandle,\r
+         &gEfiPxeBaseCodeProtocolGuid,\r
+         &Private->PxeBc\r
+         );\r
+  PxeBcDestroyIp4Children (This, Private);\r
+  PxeBcDestroyIp6Children (This, Private);\r
+  FreePool (Private);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stop this driver on ControllerHandle. This service is called by the\r
+  EFI boot service DisconnectController(). In order to\r
+  make drivers as small as possible, there are a few calling\r
+  restrictions for this service. DisconnectController()\r
+  must follow these calling restrictions. If any other agent wishes\r
+  to call Stop() it must also follow these calling restrictions.\r
+\r
+  @param[in]  This              Protocol instance pointer.\r
+  @param[in]  ControllerHandle  Handle of device to stop driver on.\r
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
+                                children is zero stop the entire bus driver.\r
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCESS           This driver was removed ControllerHandle.\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+  @retval Others                This driver was not removed from this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingStop (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN UINTN                        NumberOfChildren,\r
+  IN EFI_HANDLE                   *ChildHandleBuffer\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA              *Private;\r
+  PXEBC_VIRTUAL_NIC               *VirtualNic;\r
+  EFI_PXE_BASE_CODE_PROTOCOL      *PxeBc;\r
+  EFI_LOAD_FILE_PROTOCOL          *LoadFile;\r
+  EFI_STATUS                      Status;\r
+  EFI_HANDLE                      NicHandle;\r
+  BOOLEAN                         IsIpv6;\r
+\r
+  Private    = NULL;\r
+  NicHandle  = NULL;\r
+  VirtualNic = NULL;\r
+  LoadFile   = NULL;\r
+  PxeBc      = NULL;\r
+  IsIpv6     = FALSE;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &gEfiLoadFileProtocolGuid,\r
+                  (VOID **) &LoadFile,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // Get the Nic handle by any pass-over service child handle.\r
+    //\r
+    NicHandle = PxeBcGetNicByIp4Children (ControllerHandle);\r
+    if (NicHandle == NULL) {\r
+      NicHandle = PxeBcGetNicByIp6Children (ControllerHandle);\r
+      if (NicHandle == NULL) {\r
+        return EFI_DEVICE_ERROR;\r
+      } else {\r
+        IsIpv6 = TRUE;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Try to retrieve the private data by PxeBc protocol.\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    NicHandle,\r
+                    &gEfiPxeBaseCodeProtocolGuid,\r
+                    (VOID **) &PxeBc,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc);\r
+\r
+  } else {\r
+    //\r
+    // It's a virtual handle with LoadFileProtocol.\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    ControllerHandle,\r
+                    &gEfiLoadFileProtocolGuid,\r
+                    (VOID **) &LoadFile,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (LoadFile);\r
+    Private    = VirtualNic->Private;\r
+    NicHandle  = Private->Controller;\r
+\r
+    if (Private->Ip6Nic == VirtualNic) {\r
+      IsIpv6   = TRUE;\r
+    }\r
+  }\r
+\r
+  if (Private->Ip4Nic != NULL && !IsIpv6) {\r
+    PxeBcDestroyIp4Children (This, Private);\r
+  }\r
+\r
+  if (Private->Ip6Nic != NULL && IsIpv6) {\r
+    PxeBcDestroyIp6Children (This, Private);\r
+  }\r
+\r
+  if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) {\r
+    gBS->UninstallProtocolInterface (\r
+           NicHandle,\r
+           &gEfiPxeBaseCodeProtocolGuid,\r
+           &Private->PxeBc\r
+           );\r
+    FreePool (Private);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h
new file mode 100644 (file)
index 0000000..8df4bae
--- /dev/null
@@ -0,0 +1,104 @@
+/** @file\r
+  Driver Binding functions declaration for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2007 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_DRIVER_H__\r
+#define __EFI_PXEBC_DRIVER_H__\r
+\r
+extern EFI_COMPONENT_NAME_PROTOCOL  gPxeBcComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2;\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle. This service\r
+  is called by the EFI boot service ConnectController(). In\r
+  order to make drivers as small as possible, there are a few calling\r
+  restrictions for this service. ConnectController() must\r
+  follow these calling restrictions. If any other agent wishes to call\r
+  Supported() it must also follow these calling restrictions.\r
+\r
+  @param[in]  This                The pointer to the driver binding protocol.\r
+  @param[in]  ControllerHandle    The handle of device to be tested.\r
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child\r
+                                  device to be started.\r
+\r
+  @retval EFI_SUCCESS         This driver supports this device.\r
+  @retval EFI_UNSUPPORTED     This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+\r
+/**\r
+  Start this driver on ControllerHandle. This service is called by the\r
+  EFI boot service ConnectController(). In order to make\r
+  drivers as small as possible, there are a few calling restrictions for\r
+  this service. ConnectController() must follow these\r
+  calling restrictions. If any other agent wishes to call Start() it\r
+  must also follow these calling restrictions.\r
+\r
+  @param[in]  This                 The pointer to the driver binding protocol.\r
+  @param[in]  ControllerHandle     The handle of device to be started.\r
+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child\r
+                                   device to be started.\r
+\r
+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.\r
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+\r
+/**\r
+  Stop this driver on ControllerHandle. This service is called by the\r
+  EFI boot service DisconnectController(). In order to\r
+  make drivers as small as possible, there are a few calling\r
+  restrictions for this service. DisconnectController()\r
+  must follow these calling restrictions. If any other agent wishes\r
+  to call Stop() it must also follow these calling restrictions.\r
+\r
+  @param[in]  This              Protocol instance pointer.\r
+  @param[in]  ControllerHandle  Handle of device to stop driver on\r
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
+                                children is zero stop the entire bus driver.\r
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+  @retval EFI_SUCCESS           This driver is removed ControllerHandle\r
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.\r
+  @retval Others                This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingStop (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN UINTN                        NumberOfChildren,\r
+  IN EFI_HANDLE                   *ChildHandleBuffer\r
+  );\r
+\r
+#endif\r
+\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c
new file mode 100644 (file)
index 0000000..7e5b4d6
--- /dev/null
@@ -0,0 +1,2200 @@
+/** @file\r
+  This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.\r
+\r
+  Copyright (c) 2007 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+/**\r
+  Enables the use of the PXE Base Code Protocol functions.\r
+\r
+  This function enables the use of the PXE Base Code Protocol functions. If the\r
+  Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then\r
+  EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted\r
+  addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted\r
+  addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported\r
+  field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will\r
+  be returned. If there is not enough memory or other resources to start the PXE\r
+  Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the\r
+  PXE Base Code Protocol will be started.\r
+\r
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]  UseIpv6           Specifies the type of IP addresses that are to be\r
+                                used during the session that is being started.\r
+                                Set to TRUE for IPv6, and FALSE for IPv4.\r
+\r
+  @retval EFI_SUCCESS           The PXE Base Code Protocol was started.\r
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.\r
+  @retval EFI_UNSUPPORTED       UseIpv6 is TRUE, but the Ipv6Supported field of the\r
+                                EFI_PXE_BASE_CODE_MODE structure is FALSE.\r
+  @retval EFI_ALREADY_STARTED   The PXE Base Code Protocol is already in the started state.\r
+  @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid\r
+                                EFI_PXE_BASE_CODE_PROTOCOL structure.\r
+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory or other resources to start the\r
+                                PXE Base Code Protocol.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcStart (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN BOOLEAN                          UseIpv6\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA      *Private;\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+  UINTN                   Index;\r
+  EFI_STATUS              Status;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode    = Private->PxeBc.Mode;\r
+\r
+  if (Mode->Started) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  //\r
+  // Detect whether using IPv6 or not, and set it into mode data.\r
+  //\r
+  if (UseIpv6 && Mode->Ipv6Available && Mode->Ipv6Supported && Private->Ip6Nic != NULL) {\r
+    Mode->UsingIpv6 = TRUE;\r
+  } else if (!UseIpv6 && Private->Ip4Nic != NULL) {\r
+    Mode->UsingIpv6 = FALSE;\r
+  } else {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  if (Mode->UsingIpv6) {\r
+    AsciiPrint ("\n>>Start PXE over IPv6");\r
+    //\r
+    // Configure block size for TFTP as a default value to handle all link layers.\r
+    //\r
+    Private->BlockSize = (UINTN) (Private->Ip6MaxPacketSize -\r
+                           PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);\r
+\r
+    //\r
+    // PXE over IPv6 starts here, initialize the fields and list header.\r
+    //\r
+    Private->Ip6Policy                          = PXEBC_IP6_POLICY_MAX;\r
+    Private->ProxyOffer.Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;\r
+    Private->DhcpAck.Dhcp6.Packet.Ack.Size      = PXEBC_DHCP6_PACKET_MAX_SIZE;\r
+    Private->PxeReply.Dhcp6.Packet.Ack.Size     = PXEBC_DHCP6_PACKET_MAX_SIZE;\r
+\r
+    for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {\r
+      Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;\r
+    }\r
+\r
+    //\r
+    // Create event and set status for token to capture ICMP6 error message.\r
+    //\r
+    Private->Icmp6Token.Status = EFI_NOT_READY;\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_NOTIFY,\r
+                    PxeBcIcmp6ErrorUpdate,\r
+                    Private,\r
+                    &Private->Icmp6Token.Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+  } else {\r
+    AsciiPrint ("\n>>Start PXE over IPv4");\r
+    //\r
+    // Configure block size for TFTP as a default value to handle all link layers.\r
+    //\r
+    Private->BlockSize = (UINTN) (Private->Ip4MaxPacketSize -\r
+                           PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);\r
+\r
+    //\r
+    // PXE over IPv4 starts here, initialize the fields.\r
+    //\r
+    Private->ProxyOffer.Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;\r
+    Private->DhcpAck.Dhcp4.Packet.Ack.Size      = PXEBC_DHCP4_PACKET_MAX_SIZE;\r
+    Private->PxeReply.Dhcp4.Packet.Ack.Size     = PXEBC_DHCP4_PACKET_MAX_SIZE;\r
+\r
+    for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {\r
+      Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;\r
+    }\r
+\r
+    PxeBcSeedDhcp4Packet (&Private->SeedPacket, Private->Udp4Read);\r
+\r
+    //\r
+    // Create the event for Arp cache update.\r
+    //\r
+    Status = gBS->CreateEvent (\r
+                    EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    PxeBcArpCacheUpdate,\r
+                    Private,\r
+                    &Private->ArpUpdateEvent\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    //\r
+    // Start a periodic timer by second to update Arp cache.\r
+    //\r
+    Status = gBS->SetTimer (\r
+                    Private->ArpUpdateEvent,\r
+                    TimerPeriodic,\r
+                    TICKS_PER_SECOND\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    //\r
+    // Create event and set status for token to capture ICMP error message.\r
+    //\r
+    Private->Icmp6Token.Status = EFI_NOT_READY;\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_NOTIFY,\r
+                    PxeBcIcmpErrorUpdate,\r
+                    Private,\r
+                    &Private->IcmpToken.Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If PcdTftpBlockSize is set to non-zero, override the default value.\r
+  //\r
+  if (PcdGet64 (PcdTftpBlockSize) != 0) {\r
+    Private->BlockSize   = (UINTN) PcdGet64 (PcdTftpBlockSize);\r
+  }\r
+\r
+  //\r
+  // Create event for UdpRead/UdpWrite timeout since they are both blocking API.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &Private->UdpTimeOutEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Private->IsAddressOk = FALSE;\r
+  Mode->Started        = TRUE;\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  if (Mode->UsingIpv6) {\r
+    if (Private->Icmp6Token.Event != NULL) {\r
+      gBS->CloseEvent (Private->Icmp6Token.Event);\r
+      Private->Icmp6Token.Event = NULL;\r
+    }\r
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+    Private->Ip6->Configure (Private->Ip6, NULL);\r
+  } else {\r
+    if (Private->ArpUpdateEvent != NULL) {\r
+      gBS->CloseEvent (Private->ArpUpdateEvent);\r
+      Private->ArpUpdateEvent = NULL;\r
+    }\r
+    if (Private->IcmpToken.Event != NULL) {\r
+      gBS->CloseEvent (Private->IcmpToken.Event);\r
+      Private->IcmpToken.Event = NULL;\r
+    }\r
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+    Private->Ip4->Configure (Private->Ip4, NULL);\r
+  }\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Disable the use of the PXE Base Code Protocol functions.\r
+\r
+  This function stops all activity on the network device. All the resources allocated\r
+  in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is\r
+  set to FALSE, and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE\r
+  structure is already FALSE, then EFI_NOT_STARTED will be returned.\r
+\r
+  @param[in]  This               Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+\r
+  @retval EFI_SUCCESS           The PXE Base Code Protocol was stopped.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is already in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid\r
+                                EFI_PXE_BASE_CODE_PROTOCOL structure.\r
+  @retval Others\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcStop (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA      *Private;\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+  BOOLEAN                 Ipv6Supported;\r
+  BOOLEAN                 Ipv6Available;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private       = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode          = Private->PxeBc.Mode;\r
+  Ipv6Supported = Mode->Ipv6Supported;\r
+  Ipv6Available = Mode->Ipv6Available;\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Mode->UsingIpv6) {\r
+    //\r
+    // Configure all the instances for IPv6 as NULL.\r
+    //\r
+    ZeroMem (&Private->Udp6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));\r
+    ZeroMem (&Private->Ip6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));\r
+    Private->Dhcp6->Stop (Private->Dhcp6);\r
+    Private->Dhcp6->Configure (Private->Dhcp6, NULL);\r
+    Private->Udp6Write->Configure (Private->Udp6Write, NULL);\r
+    Private->Udp6Read->Groups (Private->Udp6Read, FALSE, NULL);\r
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+    Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);\r
+    Private->Ip6->Configure (Private->Ip6, NULL);\r
+    PxeBcUnregisterIp6Address (Private);\r
+    if (Private->Icmp6Token.Event != NULL) {\r
+      gBS->CloseEvent (Private->Icmp6Token.Event);\r
+      Private->Icmp6Token.Event = NULL;\r
+    }\r
+    if (Private->Dhcp6Request != NULL) {\r
+      FreePool (Private->Dhcp6Request);\r
+      Private->Dhcp6Request = NULL;\r
+    }\r
+    if (Private->BootFileName != NULL) {\r
+      FreePool (Private->BootFileName);\r
+      Private->BootFileName = NULL;\r
+    }\r
+  } else {\r
+    //\r
+    // Configure all the instances for IPv4 as NULL.\r
+    //\r
+    ZeroMem (&Private->Udp4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    ZeroMem (&Private->Udp4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    ZeroMem (&Private->Ip4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    ZeroMem (&Private->Ip4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    Private->Dhcp4->Stop (Private->Dhcp4);\r
+    Private->Dhcp4->Configure (Private->Dhcp4, NULL);\r
+    Private->Udp4Write->Configure (Private->Udp4Write, NULL);\r
+    Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL);\r
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+    Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);\r
+    Private->Ip4->Configure (Private->Ip4, NULL);\r
+    if (Private->ArpUpdateEvent != NULL) {\r
+      gBS->CloseEvent (Private->ArpUpdateEvent);\r
+      Private->ArpUpdateEvent = NULL;\r
+    }\r
+    if (Private->IcmpToken.Event != NULL) {\r
+      gBS->CloseEvent (Private->IcmpToken.Event);\r
+      Private->IcmpToken.Event = NULL;\r
+    }\r
+  }\r
+\r
+  gBS->CloseEvent (Private->UdpTimeOutEvent);\r
+  Private->CurSrcPort   = 0;\r
+  Private->BootFileSize = 0;\r
+\r
+  //\r
+  // Reset the mode data.\r
+  //\r
+  ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE));\r
+  Mode->Ipv6Available = Ipv6Available;\r
+  Mode->Ipv6Supported = Ipv6Supported;\r
+  Mode->AutoArp       = TRUE;\r
+  Mode->TTL           = DEFAULT_TTL;\r
+  Mode->ToS           = DEFAULT_ToS;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6\r
+  S.A.R.R (solicit / advertise / request / reply) sequence.\r
+\r
+  If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before\r
+  they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will\r
+  be tried in the order in which they are received. Please see the Preboot Execution\r
+  Environment (PXE) Specification and Unified Extensible Firmware Interface (UEFI)\r
+  Specification for additional details on the implementation of DHCP.\r
+  If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,\r
+  then the DHCP sequence will be stopped and EFI_ABORTED will be returned.\r
+\r
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]  SortOffers        TRUE if the offers received should be sorted. Set to FALSE to\r
+                                try the offers in the order that they are received.\r
+\r
+  @retval EFI_SUCCESS           Valid DHCP has completed.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid\r
+                                EFI_PXE_BASE_CODE_PROTOCOL structure.\r
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.\r
+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory to complete the DHCP Protocol.\r
+  @retval EFI_ABORTED           The callback function aborted the DHCP Protocol.\r
+  @retval EFI_TIMEOUT           The DHCP Protocol timed out.\r
+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the DHCP session.\r
+  @retval EFI_NO_RESPONSE       Valid PXE offer was not received.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcDhcp (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN BOOLEAN                          SortOffers\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA           *Private;\r
+  EFI_PXE_BASE_CODE_MODE       *Mode;\r
+  EFI_STATUS                   Status;\r
+  EFI_PXE_BASE_CODE_IP_FILTER  IpFilter;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status                  = EFI_SUCCESS;\r
+  Private                 = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode                    = Private->PxeBc.Mode;\r
+  Mode->IcmpErrorReceived = FALSE;\r
+  Private->Function       = EFI_PXE_BASE_CODE_FUNCTION_DHCP;\r
+  Private->IsOfferSorted  = SortOffers;\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Mode->UsingIpv6) {\r
+\r
+    //\r
+    // Stop Udp6Read instance\r
+    //\r
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+\r
+    //\r
+    // Start S.A.R.R. process to get a IPv6 address and other boot information.\r
+    //\r
+    Status = PxeBcDhcp6Sarr (Private, Private->Dhcp6);\r
+  } else {\r
+\r
+    //\r
+    // Stop Udp4Read instance\r
+    //\r
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+\r
+    //\r
+    // Start D.O.R.A. process to get a IPv4 address and other boot information.\r
+    //\r
+    Status = PxeBcDhcp4Dora (Private, Private->Dhcp4);\r
+  }\r
+\r
+  //\r
+  // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP\r
+  // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.\r
+  //\r
+  ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));\r
+  IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
+  This->SetIpFilter (This, &IpFilter);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Attempts to complete the PXE Boot Server and/or boot image discovery sequence.\r
+\r
+  This function attempts to complete the PXE Boot Server and/or boot image discovery\r
+  sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the\r
+  PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the\r
+  EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the\r
+  PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure\r
+  will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE.\r
+  In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[],\r
+  has two uses: It is the Boot Server IP address list used for unicast discovery\r
+  (if the UseUCast field is TRUE), and it is the list used for Boot Server verification\r
+  (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure\r
+  is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot\r
+  Server reply of that type will be accepted. If the AcceptAnyResponse field is\r
+  FALSE, only responses from Boot Servers with matching IP addresses will be accepted.\r
+  This function can take at least 10 seconds to timeout and return control to the\r
+  caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be\r
+  returned. Please see the Preboot Execution Environment (PXE) Specification for\r
+  additional details on the implementation of the Discovery sequence.\r
+  If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,\r
+  then the Discovery sequence is stopped and EFI_ABORTED will be returned.\r
+\r
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]  Type              The type of bootstrap to perform.\r
+  @param[in]  Layer             Pointer to the boot server layer number to discover, which must be\r
+                                PXE_BOOT_LAYER_INITIAL when a new server type is being\r
+                                discovered.\r
+  @param[in]  UseBis            TRUE if Boot Integrity Services are to be used. FALSE otherwise.\r
+  @param[in]  Info              Pointer to a data structure that contains additional information\r
+                                on the type of discovery operation that is to be performed.\r
+                                It is optional.\r
+\r
+  @retval EFI_SUCCESS           The Discovery sequence has been completed.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.\r
+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory to complete Discovery.\r
+  @retval EFI_ABORTED           The callback function aborted the Discovery sequence.\r
+  @retval EFI_TIMEOUT           The Discovery sequence timed out.\r
+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the PXE discovery\r
+                                session.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcDiscover (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN UINT16                           Type,\r
+  IN UINT16                           *Layer,\r
+  IN BOOLEAN                          UseBis,\r
+  IN EFI_PXE_BASE_CODE_DISCOVER_INFO  *Info   OPTIONAL\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA              *Private;\r
+  EFI_PXE_BASE_CODE_MODE          *Mode;\r
+  EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;\r
+  EFI_PXE_BASE_CODE_SRVLIST       *SrvList;\r
+  PXEBC_BOOT_SVR_ENTRY            *BootSvrEntry;\r
+  UINT16                          Index;\r
+  EFI_STATUS                      Status;\r
+  EFI_PXE_BASE_CODE_IP_FILTER     IpFilter;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private                 = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode                    = Private->PxeBc.Mode;\r
+  Mode->IcmpErrorReceived = FALSE;\r
+  BootSvrEntry            = NULL;\r
+  SrvList                 = NULL;\r
+  Status                  = EFI_DEVICE_ERROR;\r
+  Private->Function       = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  //\r
+  // Station address should be ready before do discover.\r
+  //\r
+  if (!Private->IsAddressOk) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Mode->UsingIpv6) {\r
+\r
+    //\r
+    // Stop Udp6Read instance\r
+    //\r
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+  } else {\r
+\r
+    //\r
+    // Stop Udp4Read instance\r
+    //\r
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+  }\r
+\r
+  //\r
+  // There are 3 methods to get the information for discover.\r
+  //\r
+  if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) {\r
+    //\r
+    // 1. Take the previous setting as the discover info.\r
+    //\r
+    if (!Mode->PxeDiscoverValid ||\r
+        !Mode->PxeReplyReceived ||\r
+        (!Mode->PxeBisReplyReceived && UseBis)) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Info                         = &DefaultInfo;\r
+    Info->IpCnt                  = 1;\r
+    Info->UseUCast               = TRUE;\r
+    SrvList                      = Info->SrvList;\r
+    SrvList[0].Type              = Type;\r
+    SrvList[0].AcceptAnyResponse = FALSE;\r
+\r
+    CopyMem (&SrvList->IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));\r
+\r
+  } else if (Info == NULL) {\r
+    //\r
+    // 2. Extract the discover information from the cached packets if unspecified.\r
+    //\r
+    Info   = &DefaultInfo;\r
+    Status = PxeBcExtractDiscoverInfo (Private, Type, Info, &BootSvrEntry, &SrvList);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+  } else {\r
+    //\r
+    // 3. Take the pass-in information as the discover info, and validate the server list.\r
+    //\r
+    SrvList = Info->SrvList;\r
+\r
+    if (!SrvList[0].AcceptAnyResponse) {\r
+      for (Index = 1; Index < Info->IpCnt; Index++) {\r
+        if (SrvList[Index].AcceptAnyResponse) {\r
+          break;\r
+        }\r
+      }\r
+      if (Index != Info->IpCnt) {\r
+        //\r
+        // It's invalid if the first server doesn't accecpt any response\r
+        // and meanwhile any of the rest servers accept any reponse.\r
+        //\r
+        Status = EFI_INVALID_PARAMETER;\r
+        goto ON_EXIT;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Info and BootSvrEntry/SrvList are all ready by now, so execute discover by UniCast/BroadCast/MultiCast.\r
+  //\r
+  if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) ||\r
+      (Info->MustUseList && Info->IpCnt == 0)) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Private->IsDoDiscover = TRUE;\r
+\r
+  if (Info->UseUCast) {\r
+    //\r
+    // Do discover by unicast.\r
+    //\r
+    for (Index = 0; Index < Info->IpCnt; Index++) {\r
+      if (BootSvrEntry == NULL) {\r
+        CopyMem (&Private->ServerIp, &SrvList[Index].IpAddr, sizeof (EFI_IP_ADDRESS));\r
+      } else {\r
+        ASSERT (!Mode->UsingIpv6);\r
+        ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));\r
+        CopyMem (&Private->ServerIp, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));\r
+      }\r
+\r
+      Status = PxeBcDiscoverBootServer (\r
+                 Private,\r
+                 Type,\r
+                 Layer,\r
+                 UseBis,\r
+                 &SrvList[Index].IpAddr,\r
+                 0,\r
+                 NULL\r
+                 );\r
+    }\r
+  } else if (Info->UseMCast) {\r
+    //\r
+    // Do discover by multicast.\r
+    //\r
+    Status = PxeBcDiscoverBootServer (\r
+               Private,\r
+               Type,\r
+               Layer,\r
+               UseBis,\r
+               &Info->ServerMCastIp,\r
+               0,\r
+               NULL\r
+               );\r
+\r
+  } else if (Info->UseBCast) {\r
+    //\r
+    // Do discover by broadcast, but only valid for IPv4.\r
+    //\r
+    ASSERT (!Mode->UsingIpv6);\r
+    Status = PxeBcDiscoverBootServer (\r
+               Private,\r
+               Type,\r
+               Layer,\r
+               UseBis,\r
+               NULL,\r
+               Info->IpCnt,\r
+               SrvList\r
+               );\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Parse the cached PXE reply packet, and store it into mode data if valid.\r
+    //\r
+    if (Mode->UsingIpv6) {\r
+      Status = PxeBcParseDhcp6Packet (&Private->PxeReply.Dhcp6);\r
+      if (!EFI_ERROR (Status)) {\r
+        CopyMem (\r
+          &Mode->PxeReply.Dhcpv6,\r
+          &Private->PxeReply.Dhcp6.Packet.Offer,\r
+          Private->PxeReply.Dhcp6.Packet.Offer.Length\r
+          );\r
+        Mode->PxeReplyReceived = TRUE;\r
+        Mode->PxeDiscoverValid = TRUE;\r
+      }\r
+    } else {\r
+      Status = PxeBcParseDhcp4Packet (&Private->PxeReply.Dhcp4);\r
+      if (!EFI_ERROR (Status)) {\r
+        CopyMem (\r
+          &Mode->PxeReply.Dhcpv4,\r
+          &Private->PxeReply.Dhcp4.Packet.Offer,\r
+          Private->PxeReply.Dhcp4.Packet.Offer.Length\r
+          );\r
+        Mode->PxeReplyReceived = TRUE;\r
+        Mode->PxeDiscoverValid = TRUE;\r
+      }\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  //\r
+  // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP\r
+  // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.\r
+  //\r
+  ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));\r
+  IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
+  This->SetIpFilter (This, &IpFilter);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Used to perform TFTP and MTFTP services.\r
+\r
+  This function is used to perform TFTP and MTFTP services. This includes the\r
+  TFTP operations to get the size of a file, read a directory, read a file, and\r
+  write a file. It also includes the MTFTP operations to get the size of a file,\r
+  read a directory, and read a file. The type of operation is specified by Operation.\r
+  If the callback function that is invoked during the TFTP/MTFTP operation does\r
+  not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will\r
+  be returned.\r
+  For read operations, the return data will be placed in the buffer specified by\r
+  BufferPtr. If BufferSize is too small to contain the entire downloaded file,\r
+  then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero,\r
+  or the size of the requested file. (NOTE: the size of the requested file is only returned\r
+  if the TFTP server supports TFTP options). If BufferSize is large enough for the\r
+  read operation, then BufferSize will be set to the size of the downloaded file,\r
+  and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services\r
+  should use the get-file-size operations to determine the size of the downloaded\r
+  file prior to using the read-file operations-especially when downloading large\r
+  (greater than 64 MB) files-instead of making two calls to the read-file operation.\r
+  Following this recommendation will save time if the file is larger than expected\r
+  and the TFTP server does not support TFTP option extensions. Without TFTP option\r
+  extension support, the client must download the entire file, counting and discarding\r
+  the received packets, to determine the file size.\r
+  For write operations, the data to be sent is in the buffer specified by BufferPtr.\r
+  BufferSize specifies the number of bytes to send. If the write operation completes\r
+  successfully, then EFI_SUCCESS will be returned.\r
+  For TFTP "get file size" operations, the size of the requested file or directory\r
+  is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server\r
+  does not support options, the file will be downloaded into a bit bucket and the\r
+  length of the downloaded file will be returned. For MTFTP "get file size" operations,\r
+  if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED\r
+  will be returned.\r
+  This function can take up to 10 seconds to timeout and return control to the caller.\r
+  If the TFTP sequence does not complete, EFI_TIMEOUT will be returned.\r
+  If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,\r
+  then the TFTP sequence is stopped and EFI_ABORTED will be returned.\r
+\r
+  @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]      Operation     The type of operation to perform.\r
+  @param[in, out] BufferPtr     A pointer to the data buffer.\r
+  @param[in]      Overwrite     Only used on write file operations. TRUE if a file on a remote\r
+                                server can be overwritten.\r
+  @param[in, out] BufferSize    For get-file-size operations, *BufferSize returns the size of the\r
+                                requested file.\r
+  @param[in]      BlockSize     The requested block size to be used during a TFTP transfer.\r
+  @param[in]      ServerIp      The TFTP / MTFTP server IP address.\r
+  @param[in]      Filename      A Null-terminated ASCII string that specifies a directory name\r
+                                or a file name.\r
+  @param[in]      Info          Pointer to the MTFTP information.\r
+  @param[in]      DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation.\r
+\r
+  @retval EFI_SUCCESS           The TFTP/MTFTP operation was completed.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.\r
+  @retval EFI_BUFFER_TOO_SMALL  The buffer is not large enough to complete the read operation.\r
+  @retval EFI_ABORTED           The callback function aborted the TFTP/MTFTP operation.\r
+  @retval EFI_TIMEOUT           The TFTP/MTFTP operation timed out.\r
+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the MTFTP session.\r
+  @retval EFI_TFTP_ERROR        A TFTP error packet was received during the MTFTP session.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcMtftp (\r
+  IN     EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN     EFI_PXE_BASE_CODE_TFTP_OPCODE    Operation,\r
+  IN OUT VOID                             *BufferPtr    OPTIONAL,\r
+  IN     BOOLEAN                          Overwrite,\r
+  IN OUT UINT64                           *BufferSize,\r
+  IN     UINTN                            *BlockSize    OPTIONAL,\r
+  IN     EFI_IP_ADDRESS                   *ServerIp,\r
+  IN     UINT8                            *Filename,\r
+  IN     EFI_PXE_BASE_CODE_MTFTP_INFO     *Info         OPTIONAL,\r
+  IN     BOOLEAN                          DontUseBuffer\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA              *Private;\r
+  EFI_PXE_BASE_CODE_MODE          *Mode;\r
+  EFI_MTFTP4_CONFIG_DATA          Mtftp4Config;\r
+  EFI_MTFTP6_CONFIG_DATA          Mtftp6Config;\r
+  VOID                            *Config;\r
+  EFI_STATUS                      Status;\r
+  EFI_PXE_BASE_CODE_IP_FILTER     IpFilter;\r
+\r
+\r
+  if ((This == NULL) ||\r
+      (Filename == NULL) ||\r
+      (BufferSize == NULL) ||\r
+      (ServerIp == NULL) ||\r
+      ((BufferPtr == NULL) && DontUseBuffer) ||\r
+      ((BlockSize != NULL) && (*BlockSize < PXE_MTFTP_DEFAULT_BLOCK_SIZE)) ||\r
+      (!NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 0) && !NetIp6IsValidUnicast (&ServerIp->v6))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Config    = NULL;\r
+  Status    = EFI_DEVICE_ERROR;\r
+  Private   = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode      = Private->PxeBc.Mode;\r
+\r
+  if (Mode->UsingIpv6) {\r
+    //\r
+    // Set configuration data for Mtftp6 instance.\r
+    //\r
+    ZeroMem (&Mtftp6Config, sizeof (EFI_MTFTP6_CONFIG_DATA));\r
+    Config                         = &Mtftp6Config;\r
+    Mtftp6Config.TimeoutValue      = PXEBC_MTFTP_TIMEOUT;\r
+    Mtftp6Config.TryCount          = PXEBC_MTFTP_RETRIES;\r
+    CopyMem (&Mtftp6Config.StationIp, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+    CopyMem (&Mtftp6Config.ServerIp, &ServerIp->v6, sizeof (EFI_IPv6_ADDRESS));\r
+    //\r
+    // Stop Udp6Read instance\r
+    //\r
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+  } else {\r
+    //\r
+    // Set configuration data for Mtftp4 instance.\r
+    //\r
+    ZeroMem (&Mtftp4Config, sizeof (EFI_MTFTP4_CONFIG_DATA));\r
+    Config                         = &Mtftp4Config;\r
+    Mtftp4Config.UseDefaultSetting = FALSE;\r
+    Mtftp4Config.TimeoutValue      = PXEBC_MTFTP_TIMEOUT;\r
+    Mtftp4Config.TryCount          = PXEBC_MTFTP_RETRIES;\r
+    CopyMem (&Mtftp4Config.StationIp, &Private->StationIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4Config.SubnetMask, &Private->SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4Config.GatewayIp, &Private->GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4Config.ServerIp, &ServerIp->v4, sizeof (EFI_IPv4_ADDRESS));\r
+    //\r
+    // Stop Udp4Read instance\r
+    //\r
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+  }\r
+\r
+  Mode->TftpErrorReceived = FALSE;\r
+  Mode->IcmpErrorReceived = FALSE;\r
+\r
+  switch (Operation) {\r
+\r
+  case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE:\r
+    //\r
+    // Send TFTP request to get file size.\r
+    //\r
+    Status = PxeBcTftpGetFileSize (\r
+               Private,\r
+               Config,\r
+               Filename,\r
+               BlockSize,\r
+               BufferSize\r
+               );\r
+\r
+    break;\r
+\r
+  case EFI_PXE_BASE_CODE_TFTP_READ_FILE:\r
+    //\r
+    // Send TFTP request to read file.\r
+    //\r
+    Status = PxeBcTftpReadFile (\r
+               Private,\r
+               Config,\r
+               Filename,\r
+               BlockSize,\r
+               BufferPtr,\r
+               BufferSize,\r
+               DontUseBuffer\r
+               );\r
+\r
+    break;\r
+\r
+  case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:\r
+    //\r
+    // Send TFTP request to write file.\r
+    //\r
+    Status = PxeBcTftpWriteFile (\r
+               Private,\r
+               Config,\r
+               Filename,\r
+               Overwrite,\r
+               BlockSize,\r
+               BufferPtr,\r
+               BufferSize\r
+               );\r
+\r
+    break;\r
+\r
+  case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:\r
+    //\r
+    // Send TFTP request to read directory.\r
+    //\r
+    Status = PxeBcTftpReadDirectory (\r
+               Private,\r
+               Config,\r
+               Filename,\r
+               BlockSize,\r
+               BufferPtr,\r
+               BufferSize,\r
+               DontUseBuffer\r
+               );\r
+\r
+    break;\r
+\r
+  case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE:\r
+  case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:\r
+  case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:\r
+    Status = EFI_UNSUPPORTED;\r
+\r
+    break;\r
+\r
+  default:\r
+    Status = EFI_INVALID_PARAMETER;\r
+\r
+    break;\r
+  }\r
+\r
+  if (Status == EFI_ICMP_ERROR) {\r
+    Mode->IcmpErrorReceived = TRUE;\r
+  }\r
+\r
+  //\r
+  // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP\r
+  // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.\r
+  //\r
+  ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));\r
+  IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
+  This->SetIpFilter (This, &IpFilter);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Writes a UDP packet to the network interface.\r
+\r
+  This function writes a UDP packet specified by the (optional HeaderPtr and)\r
+  BufferPtr parameters to the network interface. The UDP header is automatically\r
+  built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp,\r
+  SrcIp, and SrcPort to build this header. If the packet is successfully built and\r
+  transmitted through the network interface, then EFI_SUCCESS will be returned.\r
+  If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will\r
+  be returned. If an ICMP error occurs during the transmission of the packet, then\r
+  the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and\r
+  EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return\r
+  EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned.\r
+\r
+  @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]      OpFlags       The UDP operation flags.\r
+  @param[in]      DestIp        The destination IP address.\r
+  @param[in]      DestPort      The destination UDP port number.\r
+  @param[in]      GatewayIp     The gateway IP address.\r
+  @param[in]      SrcIp         The source IP address.\r
+  @param[in, out] SrcPort       The source UDP port number.\r
+  @param[in]      HeaderSize    An optional field which may be set to the length of a header\r
+                                at HeaderPtr to be prefixed to the data at BufferPtr.\r
+  @param[in]  HeaderPtr         If HeaderSize is not NULL, a pointer to a header to be\r
+                                prefixed to the data at BufferPtr.\r
+  @param[in]  BufferSize        A pointer to the size of the data at BufferPtr.\r
+  @param[in]  BufferPtr         A pointer to the data to be written.\r
+\r
+  @retval EFI_SUCCESS           The UDP Write operation completed.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_BAD_BUFFER_SIZE   The buffer is too long to be transmitted.\r
+  @retval EFI_ABORTED           The callback function aborted the UDP Write operation.\r
+  @retval EFI_TIMEOUT           The UDP Write operation timed out.\r
+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the UDP write session.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcUdpWrite (\r
+  IN     EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN     UINT16                           OpFlags,\r
+  IN     EFI_IP_ADDRESS                   *DestIp,\r
+  IN     EFI_PXE_BASE_CODE_UDP_PORT       *DestPort,\r
+  IN     EFI_IP_ADDRESS                   *GatewayIp  OPTIONAL,\r
+  IN     EFI_IP_ADDRESS                   *SrcIp      OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT       *SrcPort    OPTIONAL,\r
+  IN     UINTN                            *HeaderSize OPTIONAL,\r
+  IN     VOID                             *HeaderPtr  OPTIONAL,\r
+  IN     UINTN                            *BufferSize,\r
+  IN     VOID                             *BufferPtr\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA        *Private;\r
+  EFI_PXE_BASE_CODE_MODE    *Mode;\r
+  EFI_UDP4_SESSION_DATA     Udp4Session;\r
+  EFI_UDP6_SESSION_DATA     Udp6Session;\r
+  EFI_STATUS                Status;\r
+  BOOLEAN                   DoNotFragment;\r
+\r
+  if (This == NULL || DestIp == NULL || DestPort == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode    = Private->PxeBc.Mode;\r
+\r
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT) != 0) {\r
+    DoNotFragment = FALSE;\r
+  } else {\r
+    DoNotFragment = TRUE;\r
+  }\r
+\r
+  if (!Mode->UsingIpv6 && GatewayIp != NULL && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 0)) {\r
+    //\r
+    // Gateway is provided but it's not a unicast IPv4 address, while it will be ignored for IPv6.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (HeaderSize != NULL && (*HeaderSize == 0 || HeaderPtr == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (BufferSize == NULL || (*BufferSize != 0 && BufferPtr == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (!Private->IsAddressOk && SrcIp == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Private->CurSrcPort == 0 ||\r
+      (SrcPort != NULL && *SrcPort != Private->CurSrcPort)) {\r
+    //\r
+    // Reconfigure UDPv4/UDPv6 for UdpWrite if the source port changed.\r
+    //\r
+    if (SrcPort != NULL) {\r
+      Private->CurSrcPort = *SrcPort;\r
+    }\r
+  }\r
+\r
+  if (Mode->UsingIpv6) {\r
+    Status = PxeBcConfigUdp6Write (\r
+               Private->Udp6Write,\r
+               &Private->StationIp.v6,\r
+               &Private->CurSrcPort\r
+               );\r
+  } else {\r
+    //\r
+    // Configure the UDPv4 instance with gateway information from DHCP server as default.\r
+    //\r
+    Status = PxeBcConfigUdp4Write (\r
+               Private->Udp4Write,\r
+               &Private->StationIp.v4,\r
+               &Private->SubnetMask.v4,\r
+               &Private->GatewayIp.v4,\r
+               &Private->CurSrcPort,\r
+               DoNotFragment\r
+               );\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Private->CurSrcPort = 0;\r
+    return EFI_INVALID_PARAMETER;\r
+  } else if (SrcPort != NULL) {\r
+    *SrcPort = Private->CurSrcPort;\r
+  }\r
+\r
+  //\r
+  // Start a timer as timeout event for this blocking API.\r
+  //\r
+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);\r
+\r
+  if (Mode->UsingIpv6) {\r
+    //\r
+    // Construct UDPv6 session data.\r
+    //\r
+    ZeroMem (&Udp6Session, sizeof (EFI_UDP6_SESSION_DATA));\r
+    CopyMem (&Udp6Session.DestinationAddress, DestIp, sizeof (EFI_IPv6_ADDRESS));\r
+    Udp6Session.DestinationPort = *DestPort;\r
+    if (SrcIp != NULL) {\r
+      CopyMem (&Udp6Session.SourceAddress, SrcIp, sizeof (EFI_IPv6_ADDRESS));\r
+    }\r
+    if (SrcPort != NULL) {\r
+      Udp6Session.SourcePort = *SrcPort;\r
+    }\r
+\r
+    Status = PxeBcUdp6Write (\r
+               Private->Udp6Write,\r
+               &Udp6Session,\r
+               Private->UdpTimeOutEvent,\r
+               HeaderSize,\r
+               HeaderPtr,\r
+               BufferSize,\r
+               BufferPtr\r
+               );\r
+  } else {\r
+    //\r
+    // Construct UDPv4 session data.\r
+    //\r
+    ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA));\r
+    CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));\r
+    Udp4Session.DestinationPort = *DestPort;\r
+    if (SrcIp != NULL) {\r
+      CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS));\r
+    }\r
+    if (SrcPort != NULL) {\r
+      Udp4Session.SourcePort = *SrcPort;\r
+    }\r
+    //\r
+    // Override the gateway information if user specified.\r
+    //\r
+    Status = PxeBcUdp4Write (\r
+               Private->Udp4Write,\r
+               &Udp4Session,\r
+               Private->UdpTimeOutEvent,\r
+               (EFI_IPv4_ADDRESS *) GatewayIp,\r
+               HeaderSize,\r
+               HeaderPtr,\r
+               BufferSize,\r
+               BufferPtr\r
+               );\r
+  }\r
+\r
+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);\r
+\r
+\r
+  //\r
+  // Reset the UdpWrite instance.\r
+  //\r
+  if (Mode->UsingIpv6) {\r
+    Private->Udp6Write->Configure (Private->Udp6Write, NULL);\r
+  } else {\r
+    Private->Udp4Write->Configure (Private->Udp4Write, NULL);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Reads a UDP packet from the network interface.\r
++\r
+  This function reads a UDP packet from a network interface. The data contents\r
+  are returned in (the optional HeaderPtr and) BufferPtr, and the size of the\r
+  buffer received is returned in BufferSize . If the input BufferSize is smaller\r
+  than the UDP packet received (less optional HeaderSize), it will be set to the\r
+  required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the\r
+  contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is\r
+  successfully received, then EFI_SUCCESS will be returned, and the information\r
+  from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if\r
+  they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort,\r
+  SrcIp, and SrcPort input values, different types of UDP packet receive filtering\r
+  will be performed. The following tables summarize these receive filter operations.\r
+\r
+  @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]      OpFlags       The UDP operation flags.\r
+  @param[in, out] DestIp        The destination IP address.\r
+  @param[in, out] DestPort      The destination UDP port number.\r
+  @param[in, out] SrcIp         The source IP address.\r
+  @param[in, out] SrcPort       The source UDP port number.\r
+  @param[in]      HeaderSize    An optional field which may be set to the length of a\r
+                                header at HeaderPtr to be prefixed to the data at BufferPtr.\r
+  @param[in]      HeaderPtr     If HeaderSize is not NULL, a pointer to a header to be\r
+                                prefixed to the data at BufferPtr.\r
+  @param[in, out] BufferSize    A pointer to the size of the data at BufferPtr.\r
+  @param[in]      BufferPtr     A pointer to the data to be read.\r
+\r
+  @retval EFI_SUCCESS           The UDP Read operation was completed.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.\r
+  @retval EFI_BUFFER_TOO_SMALL  The packet is larger than Buffer can hold.\r
+  @retval EFI_ABORTED           The callback function aborted the UDP Read operation.\r
+  @retval EFI_TIMEOUT           The UDP Read operation timed out.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcUdpRead (\r
+  IN     EFI_PXE_BASE_CODE_PROTOCOL   *This,\r
+  IN     UINT16                       OpFlags,\r
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,\r
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL,\r
+  IN     UINTN                        *HeaderSize  OPTIONAL,\r
+  IN     VOID                         *HeaderPtr   OPTIONAL,\r
+  IN OUT UINTN                        *BufferSize,\r
+  IN     VOID                         *BufferPtr\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA          *Private;\r
+  EFI_PXE_BASE_CODE_MODE      *Mode;\r
+  EFI_UDP4_COMPLETION_TOKEN   Udp4Token;\r
+  EFI_UDP6_COMPLETION_TOKEN   Udp6Token;\r
+  EFI_UDP4_RECEIVE_DATA       *Udp4Rx;\r
+  EFI_UDP6_RECEIVE_DATA       *Udp6Rx;\r
+  EFI_STATUS                  Status;\r
+  BOOLEAN                     IsDone;\r
+  BOOLEAN                     IsMatched;\r
+  UINTN                       CopiedLen;\r
+\r
+  if (This == NULL || DestIp == NULL || DestPort == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private   = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode      = Private->PxeBc.Mode;\r
+  IsDone    = FALSE;\r
+  IsMatched = FALSE;\r
+  Udp4Rx    = NULL;\r
+  Udp6Rx    = NULL;\r
+\r
+  if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0 && DestPort == NULL) ||\r
+      ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0 && SrcIp == NULL) ||\r
+      ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0 && SrcPort == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((HeaderSize != NULL && *HeaderSize == 0) || (HeaderSize != NULL && HeaderPtr == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((BufferSize == NULL) || (BufferPtr == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  ZeroMem (&Udp6Token, sizeof (EFI_UDP6_COMPLETION_TOKEN));\r
+  ZeroMem (&Udp4Token, sizeof (EFI_UDP4_COMPLETION_TOKEN));\r
+\r
+  if (Mode->UsingIpv6) {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_NOTIFY,\r
+                    PxeBcCommonNotify,\r
+                    &IsDone,\r
+                    &Udp6Token.Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  } else {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_NOTIFY,\r
+                    PxeBcCommonNotify,\r
+                    &IsDone,\r
+                    &Udp4Token.Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Start a timer as timeout event for this blocking API.\r
+  //\r
+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);\r
+  Mode->IcmpErrorReceived = FALSE;\r
+\r
+  //\r
+  // Read packet by Udp4Read/Udp6Read until matched or timeout.\r
+  //\r
+  while (!IsMatched && !EFI_ERROR (Status)) {\r
+    if (Mode->UsingIpv6) {\r
+      Status = PxeBcUdp6Read (\r
+                 Private->Udp6Read,\r
+                 &Udp6Token,\r
+                 Mode,\r
+                 Private->UdpTimeOutEvent,\r
+                 OpFlags,\r
+                 &IsDone,\r
+                 &IsMatched,\r
+                 DestIp,\r
+                 DestPort,\r
+                 SrcIp,\r
+                 SrcPort\r
+                 );\r
+    } else {\r
+      Status = PxeBcUdp4Read (\r
+                 Private->Udp4Read,\r
+                 &Udp4Token,\r
+                 Mode,\r
+                 Private->UdpTimeOutEvent,\r
+                 OpFlags,\r
+                 &IsDone,\r
+                 &IsMatched,\r
+                 DestIp,\r
+                 DestPort,\r
+                 SrcIp,\r
+                 SrcPort\r
+                 );\r
+    }\r
+  }\r
+\r
+  if (Status == EFI_ICMP_ERROR ||\r
+      Status == EFI_NETWORK_UNREACHABLE ||\r
+      Status == EFI_HOST_UNREACHABLE ||\r
+      Status == EFI_PROTOCOL_UNREACHABLE ||\r
+      Status == EFI_PORT_UNREACHABLE) {\r
+    //\r
+    // Get different return status for icmp error from Udp, refers to UEFI spec.\r
+    //\r
+    Mode->IcmpErrorReceived = TRUE;\r
+  }\r
+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);\r
+\r
+  if (IsMatched) {\r
+    //\r
+    // Copy the rececived packet to user if matched by filter.\r
+    //\r
+    CopiedLen = 0;\r
+    if (Mode->UsingIpv6) {\r
+      Udp6Rx = Udp6Token.Packet.RxData;\r
+      ASSERT (Udp6Rx != NULL);\r
+      //\r
+      // Copy the header part of received data.\r
+      //\r
+      if (HeaderSize != NULL) {\r
+        CopiedLen   = MIN (*HeaderSize, Udp6Rx->DataLength);\r
+        *HeaderSize = CopiedLen;\r
+        CopyMem (HeaderPtr, Udp6Rx->FragmentTable[0].FragmentBuffer, *HeaderSize);\r
+      }\r
+      //\r
+      // Copy the other part of received data.\r
+      //\r
+      if (Udp6Rx->DataLength - CopiedLen > *BufferSize) {\r
+        Status = EFI_BUFFER_TOO_SMALL;\r
+      } else {\r
+        *BufferSize = Udp6Rx->DataLength - CopiedLen;\r
+        CopyMem (BufferPtr, (UINT8 *) Udp6Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize);\r
+      }\r
+      //\r
+      // Recycle the receiving buffer after copy to user.\r
+      //\r
+      gBS->SignalEvent (Udp6Rx->RecycleSignal);\r
+    } else {\r
+      Udp4Rx = Udp4Token.Packet.RxData;\r
+      ASSERT (Udp4Rx != NULL);\r
+      //\r
+      // Copy the header part of received data.\r
+      //\r
+      if (HeaderSize != NULL) {\r
+        CopiedLen   = MIN (*HeaderSize, Udp4Rx->DataLength);\r
+        *HeaderSize = CopiedLen;\r
+        CopyMem (HeaderPtr, Udp4Rx->FragmentTable[0].FragmentBuffer, *HeaderSize);\r
+      }\r
+      //\r
+      // Copy the other part of received data.\r
+      //\r
+      if (Udp4Rx->DataLength - CopiedLen > *BufferSize) {\r
+        Status = EFI_BUFFER_TOO_SMALL;\r
+      } else {\r
+        *BufferSize = Udp4Rx->DataLength - CopiedLen;\r
+        CopyMem (BufferPtr, (UINT8 *) Udp4Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize);\r
+      }\r
+      //\r
+      // Recycle the receiving buffer after copy to user.\r
+      //\r
+      gBS->SignalEvent (Udp4Rx->RecycleSignal);\r
+    }\r
+  }\r
+\r
+  if (Mode->UsingIpv6) {\r
+    Private->Udp6Read->Cancel (Private->Udp6Read, &Udp6Token);\r
+    gBS->CloseEvent (Udp6Token.Event);\r
+  } else {\r
+    Private->Udp4Read->Cancel (Private->Udp4Read, &Udp4Token);\r
+    gBS->CloseEvent (Udp4Token.Event);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Updates the IP receive filters of a network device and enables software filtering.\r
+\r
+  The NewFilter field is used to modify the network device's current IP receive\r
+  filter settings and to enable a software filter. This function updates the IpFilter\r
+  field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter.\r
+  The software filter is used when the USE_FILTER in OpFlags is set to UdpRead().\r
+  The current hardware filter remains in effect no matter what the settings of OpFlags.\r
+  This is so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those\r
+  packets whose reception is enabled in hardware-physical NIC address (unicast),\r
+  broadcast address, logical address or addresses (multicast), or all (promiscuous).\r
+  UdpRead() does not modify the IP filter settings.\r
+  Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive\r
+  filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.\r
+  If an application or driver wishes to preserve the IP receive filter settings,\r
+  it will have to preserve the IP receive filter settings before these calls, and\r
+  use SetIpFilter() to restore them after the calls. If incompatible filtering is\r
+  requested (for example, PROMISCUOUS with anything else), or if the device does not\r
+  support a requested filter setting and it cannot be accommodated in software\r
+  (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned.\r
+  The IPlist field is used to enable IPs other than the StationIP. They may be\r
+  multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP,\r
+  then both the StationIP and the IPs from the IPlist will be used.\r
+\r
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]  NewFilter         Pointer to the new set of IP receive filters.\r
+\r
+  @retval EFI_SUCCESS           The IP receive filter settings were updated.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcSetIpFilter (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN EFI_PXE_BASE_CODE_IP_FILTER      *NewFilter\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  PXEBC_PRIVATE_DATA        *Private;\r
+  EFI_PXE_BASE_CODE_MODE    *Mode;\r
+  EFI_UDP4_CONFIG_DATA      Udp4Cfg;\r
+  EFI_UDP6_CONFIG_DATA      Udp6Cfg;\r
+  UINTN                     Index;\r
+  BOOLEAN                   NeedPromiscuous;\r
+\r
+  if (This == NULL || NewFilter == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private         = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode            = Private->PxeBc.Mode;\r
+  Status          = EFI_SUCCESS;\r
+  NeedPromiscuous = FALSE;\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  for (Index = 0; Index < NewFilter->IpCnt; Index++) {\r
+    ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);\r
+    if (!Mode->UsingIpv6 &&\r
+        IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) {\r
+      //\r
+      // IPv4 broadcast address should not be in IP filter.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&\r
+        (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) ||\r
+         NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6))) {\r
+      //\r
+      // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IPv4/IPv6 address\r
+      // is in IpList, promiscuous mode is needed.\r
+      //\r
+      NeedPromiscuous = TRUE;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Clear configuration for UdpRead and leave the original group joined before.\r
+  //\r
+  if (Mode->UsingIpv6) {\r
+    CopyMem(&Udp6Cfg, &Private->Udp6CfgData, sizeof (EFI_UDP6_CONFIG_DATA));\r
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+    Udp6Cfg.AcceptPromiscuous  = FALSE;\r
+  } else {\r
+    CopyMem(&Udp4Cfg, &Private->Udp4CfgData, sizeof (EFI_UDP4_CONFIG_DATA));\r
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+    Udp4Cfg.AcceptPromiscuous = FALSE;\r
+    Udp4Cfg.AcceptBroadcast   = FALSE;\r
+  }\r
+\r
+  if (NeedPromiscuous ||\r
+      (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0 ||\r
+      (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) {\r
+    //\r
+    // Configure UDPv4/UDPv6 as promiscuous mode to receive all packets.\r
+    //\r
+    Udp4Cfg.AcceptPromiscuous = TRUE;\r
+    Udp6Cfg.AcceptPromiscuous = TRUE;\r
+\r
+  } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) {\r
+    //\r
+    // Configure UDPv4 to receive all broadcast packets.\r
+    //\r
+    Udp4Cfg.AcceptBroadcast  = TRUE;\r
+  }\r
+\r
+  //\r
+  // Configure UDPv4/UDPv6 instance with the new configuration.\r
+  //\r
+  if (Mode->UsingIpv6) {\r
+    Status = Private->Udp6Read->Configure (Private->Udp6Read, &Udp6Cfg);\r
+  } else {\r
+    Status = Private->Udp4Read->Configure (Private->Udp4Read, &Udp4Cfg);\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) {\r
+\r
+    for (Index = 0; Index < NewFilter->IpCnt; Index++) {\r
+      //\r
+      // Join the multicast group if needed.\r
+      //\r
+      if (Mode->UsingIpv6) {\r
+        if (IP6_IS_MULTICAST (&NewFilter->IpList[Index].v6)) {\r
+          Status = Private->Udp6Read->Groups (\r
+                                        Private->Udp6Read,\r
+                                        TRUE,\r
+                                        &NewFilter->IpList[Index].v6\r
+                                        );\r
+          if (EFI_ERROR (Status)) {\r
+            goto ON_EXIT;\r
+          }\r
+        }\r
+      } else {\r
+        if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) {\r
+          Status = Private->Udp4Read->Groups (\r
+                                        Private->Udp4Read,\r
+                                        TRUE,\r
+                                        &NewFilter->IpList[Index].v4\r
+                                        );\r
+          if (EFI_ERROR (Status)) {\r
+            goto ON_EXIT;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Save the new IP filter into mode data.\r
+  //\r
+  CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter));\r
+\r
+ON_EXIT:\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Uses the ARP protocol to resolve a MAC address. It is not supported for IPv6.\r
+\r
+  This function uses the ARP protocol to resolve a MAC address. The IP address specified\r
+  by IpAddr is used to resolve a MAC address. If the ARP protocol succeeds in resolving\r
+  the specified address, then the ArpCacheEntries and ArpCache fields of the mode data\r
+  are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved\r
+  MAC address is placed there as well.  If the PXE Base Code protocol is in the\r
+  stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters\r
+  a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is\r
+  returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,\r
+  then EFI_ABORTED is returned.\r
+\r
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]  IpAddr            Pointer to the IP address that is used to resolve a MAC address.\r
+  @param[in]  MacAddr           If not NULL, a pointer to the MAC address that was resolved with the\r
+                                ARP protocol.\r
+\r
+  @retval EFI_SUCCESS           The IP or MAC address was resolved.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.\r
+  @retval EFI_ICMP_ERROR        An error occur with the ICMP packet message.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcArp (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN EFI_IP_ADDRESS                   *IpAddr,\r
+  IN EFI_MAC_ADDRESS                  *MacAddr OPTIONAL\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA      *Private;\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+  EFI_EVENT               ResolvedEvent;\r
+  EFI_STATUS              Status;\r
+  EFI_MAC_ADDRESS         TempMac;\r
+  EFI_MAC_ADDRESS         ZeroMac;\r
+  BOOLEAN                 IsResolved;\r
+\r
+  if (This == NULL || IpAddr == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private       = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode          = Private->PxeBc.Mode;\r
+  ResolvedEvent = NULL;\r
+  Status        = EFI_SUCCESS;\r
+  IsResolved    = FALSE;\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Mode->UsingIpv6) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Station address should be ready before do arp.\r
+  //\r
+  if (!Private->IsAddressOk) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Mode->IcmpErrorReceived = FALSE;\r
+  ZeroMem (&TempMac, sizeof (EFI_MAC_ADDRESS));\r
+  ZeroMem (&ZeroMac, sizeof (EFI_MAC_ADDRESS));\r
+\r
+  if (!Mode->AutoArp) {\r
+    //\r
+    // If AutoArp is FALSE, only search in the current Arp cache.\r
+    //\r
+    PxeBcArpCacheUpdate (NULL, Private);\r
+    if (!PxeBcCheckArpCache (Mode, &IpAddr->v4, &TempMac)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto ON_EXIT;\r
+    }\r
+  } else {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_NOTIFY,\r
+                    PxeBcCommonNotify,\r
+                    &IsResolved,\r
+                    &ResolvedEvent\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    //\r
+    // If AutoArp is TRUE, try to send Arp request on initiative.\r
+    //\r
+    Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, ResolvedEvent, &TempMac);\r
+    if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    while (!IsResolved) {\r
+      if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {\r
+        break;\r
+      }\r
+    }\r
+    if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {\r
+      Status = EFI_SUCCESS;\r
+    } else {\r
+      Status = EFI_TIMEOUT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Copy the Mac address to user if needed.\r
+  //\r
+  if (MacAddr != NULL && !EFI_ERROR (Status)) {\r
+    CopyMem (MacAddr, &TempMac, sizeof (EFI_MAC_ADDRESS));\r
+  }\r
+\r
+ON_EXIT:\r
+  if (ResolvedEvent != NULL) {\r
+    gBS->CloseEvent (ResolvedEvent);\r
+  }\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Updates the parameters that affect the operation of the PXE Base Code Protocol.\r
+\r
+  This function sets parameters that affect the operation of the PXE Base Code Protocol.\r
+  The parameter specified by NewAutoArp is used to control the generation of ARP\r
+  protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated\r
+  as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP\r
+  Protocol packets will be generated. In this case, the only mappings that are\r
+  available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure.\r
+  If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol\r
+  service, then the service will fail. This function updates the AutoArp field of\r
+  the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp.\r
+  The SetParameters() call must be invoked after a Callback Protocol is installed\r
+  to enable the use of callbacks.\r
+\r
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]  NewAutoArp        If not NULL, a pointer to a value that specifies whether to replace the\r
+                                current value of AutoARP.\r
+  @param[in]  NewSendGUID       If not NULL, a pointer to a value that specifies whether to replace the\r
+                                current value of SendGUID.\r
+  @param[in]  NewTTL            If not NULL, a pointer to be used in place of the current value of TTL,\r
+                                the "time to live" field of the IP header.\r
+  @param[in]  NewToS            If not NULL, a pointer to be used in place of the current value of ToS,\r
+                                the "type of service" field of the IP header.\r
+  @param[in]  NewMakeCallback   If not NULL, a pointer to a value that specifies whether to replace the\r
+                                current value of the MakeCallback field of the Mode structure.\r
+\r
+  @retval EFI_SUCCESS           The new parameters values were updated.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcSetParameters (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN BOOLEAN                          *NewAutoArp         OPTIONAL,\r
+  IN BOOLEAN                          *NewSendGUID        OPTIONAL,\r
+  IN UINT8                            *NewTTL             OPTIONAL,\r
+  IN UINT8                            *NewToS             OPTIONAL,\r
+  IN BOOLEAN                          *NewMakeCallback    OPTIONAL\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA      *Private;\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+  EFI_GUID                SystemGuid;\r
+  EFI_STATUS              Status;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode    = Private->PxeBc.Mode;\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (NewMakeCallback != NULL) {\r
+    if (*NewMakeCallback) {\r
+      //\r
+      // Update the previous PxeBcCallback protocol.\r
+      //\r
+      Status = gBS->HandleProtocol (\r
+                      Private->Controller,\r
+                      &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+                      (VOID **) &Private->PxeBcCallback\r
+                      );\r
+\r
+      if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    } else {\r
+      Private->PxeBcCallback = NULL;\r
+    }\r
+    Mode->MakeCallbacks = *NewMakeCallback;\r
+  }\r
+\r
+  if (NewSendGUID != NULL) {\r
+    if (*NewSendGUID && EFI_ERROR (PxeBcGetSystemGuid (&SystemGuid))) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    Mode->SendGUID = *NewSendGUID;\r
+  }\r
+\r
+  if (NewAutoArp != NULL) {\r
+    Mode->AutoArp = *NewAutoArp;\r
+  }\r
+\r
+  if (NewTTL != NULL) {\r
+    Mode->TTL = *NewTTL;\r
+  }\r
+\r
+  if (NewToS != NULL) {\r
+    Mode->ToS = *NewToS;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Updates the station IP address and/or subnet mask values of a network device.\r
+\r
+  This function updates the station IP address and/or subnet mask values of a network\r
+  device. The NewStationIp field is used to modify the network device's current IP address.\r
+  If NewStationIP is NULL, then the current IP address will not be modified. Otherwise,\r
+  this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure\r
+  with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet\r
+  mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified.\r
+  Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE\r
+  structure with NewSubnetMask.\r
+\r
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]  NewStationIp      Pointer to the new IP address to be used by the network device.\r
+  @param[in]  NewSubnetMask     Pointer to the new subnet mask to be used by the network device.\r
+\r
+  @retval EFI_SUCCESS           The new station IP address and/or subnet mask were updated.\r
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcSetStationIP (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN EFI_IP_ADDRESS                   *NewStationIp    OPTIONAL,\r
+  IN EFI_IP_ADDRESS                   *NewSubnetMask   OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  PXEBC_PRIVATE_DATA      *Private;\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+  EFI_ARP_CONFIG_DATA     ArpConfigData;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (NewStationIp != NULL &&\r
+      (!NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0) &&\r
+       !NetIp6IsValidUnicast (&NewStationIp->v6))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode    = Private->PxeBc.Mode;\r
+  Status  = EFI_SUCCESS;\r
+\r
+  if (!Mode->UsingIpv6 &&\r
+      NewSubnetMask != NULL &&\r
+      !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Mode->UsingIpv6 && NewStationIp != NULL) {\r
+    //\r
+    // Set the IPv6 address by Ip6Config protocol.\r
+    //\r
+    Status = PxeBcRegisterIp6Address (Private, &NewStationIp->v6);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  } else if (!Mode->UsingIpv6 && NewStationIp != NULL) {\r
+    //\r
+    // Configure the corresponding ARP with the IPv4 address.\r
+    //\r
+    ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA));\r
+\r
+    ArpConfigData.SwAddressType   = 0x0800;\r
+    ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS);\r
+    ArpConfigData.StationAddress  = &NewStationIp->v4;\r
+\r
+    Private->Arp->Configure (Private->Arp, NULL);\r
+    Private->Arp->Configure (Private->Arp, &ArpConfigData);\r
+\r
+    if (NewSubnetMask != NULL) {\r
+      Mode->RouteTableEntries                = 1;\r
+      Mode->RouteTable[0].IpAddr.Addr[0]     = NewStationIp->Addr[0] & NewSubnetMask->Addr[0];\r
+      Mode->RouteTable[0].SubnetMask.Addr[0] = NewSubnetMask->Addr[0];\r
+      Mode->RouteTable[0].GwAddr.Addr[0]     = 0;\r
+    }\r
+\r
+    Private->IsAddressOk = TRUE;\r
+  }\r
+\r
+  if (NewStationIp != NULL) {\r
+    CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));\r
+    CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));\r
+  }\r
+\r
+  if (!Mode->UsingIpv6 && NewSubnetMask != NULL) {\r
+    CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS));\r
+    CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS));\r
+  }\r
+\r
+  Status = PxeBcFlushStaionIp (Private, NewStationIp, NewSubnetMask);\r
+ON_EXIT:\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Updates the contents of the cached DHCP and Discover packets.\r
+\r
+  The pointers to the new packets are used to update the contents of the cached\r
+  packets in the EFI_PXE_BASE_CODE_MODE structure.\r
+\r
+  @param[in]  This                   Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+  @param[in]  NewDhcpDiscoverValid   Pointer to a value that will replace the current\r
+                                     DhcpDiscoverValid field.\r
+  @param[in]  NewDhcpAckReceived     Pointer to a value that will replace the current\r
+                                     DhcpAckReceived field.\r
+  @param[in]  NewProxyOfferReceived  Pointer to a value that will replace the current\r
+                                     ProxyOfferReceived field.\r
+  @param[in]  NewPxeDiscoverValid    Pointer to a value that will replace the current\r
+                                     ProxyOfferReceived field.\r
+  @param[in]  NewPxeReplyReceived    Pointer to a value that will replace the current\r
+                                     PxeReplyReceived field.\r
+  @param[in]  NewPxeBisReplyReceived Pointer to a value that will replace the current\r
+                                     PxeBisReplyReceived field.\r
+  @param[in]  NewDhcpDiscover        Pointer to the new cached DHCP Discover packet contents.\r
+  @param[in]  NewDhcpAck             Pointer to the new cached DHCP Ack packet contents.\r
+  @param[in]  NewProxyOffer          Pointer to the new cached Proxy Offer packet contents.\r
+  @param[in]  NewPxeDiscover         Pointer to the new cached PXE Discover packet contents.\r
+  @param[in]  NewPxeReply            Pointer to the new cached PXE Reply packet contents.\r
+  @param[in]  NewPxeBisReply         Pointer to the new cached PXE BIS Reply packet contents.\r
+\r
+  @retval EFI_SUCCESS            The cached packet contents were updated.\r
+  @retval EFI_NOT_STARTED        The PXE Base Code Protocol is in the stopped state.\r
+  @retval EFI_INVALID_PARAMETER  This is NULL or does not point to a valid\r
+                                 EFI_PXE_BASE_CODE_PROTOCOL structure.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcSetPackets (\r
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,\r
+  IN BOOLEAN                          *NewDhcpDiscoverValid      OPTIONAL,\r
+  IN BOOLEAN                          *NewDhcpAckReceived        OPTIONAL,\r
+  IN BOOLEAN                          *NewProxyOfferReceived     OPTIONAL,\r
+  IN BOOLEAN                          *NewPxeDiscoverValid       OPTIONAL,\r
+  IN BOOLEAN                          *NewPxeReplyReceived       OPTIONAL,\r
+  IN BOOLEAN                          *NewPxeBisReplyReceived    OPTIONAL,\r
+  IN EFI_PXE_BASE_CODE_PACKET         *NewDhcpDiscover           OPTIONAL,\r
+  IN EFI_PXE_BASE_CODE_PACKET         *NewDhcpAck                OPTIONAL,\r
+  IN EFI_PXE_BASE_CODE_PACKET         *NewProxyOffer             OPTIONAL,\r
+  IN EFI_PXE_BASE_CODE_PACKET         *NewPxeDiscover            OPTIONAL,\r
+  IN EFI_PXE_BASE_CODE_PACKET         *NewPxeReply               OPTIONAL,\r
+  IN EFI_PXE_BASE_CODE_PACKET         *NewPxeBisReply            OPTIONAL\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA      *Private;\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+  Mode    = Private->PxeBc.Mode;\r
+\r
+  if (!Mode->Started) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (NewDhcpDiscoverValid != NULL) {\r
+    Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid;\r
+  }\r
+\r
+  if (NewDhcpAckReceived != NULL) {\r
+    Mode->DhcpAckReceived = *NewDhcpAckReceived;\r
+  }\r
+\r
+  if (NewProxyOfferReceived != NULL) {\r
+    Mode->ProxyOfferReceived = *NewProxyOfferReceived;\r
+  }\r
+\r
+  if (NewPxeDiscoverValid != NULL) {\r
+    Mode->PxeDiscoverValid = *NewPxeDiscoverValid;\r
+  }\r
+\r
+  if (NewPxeReplyReceived != NULL) {\r
+    Mode->PxeReplyReceived = *NewPxeReplyReceived;\r
+  }\r
+\r
+  if (NewPxeBisReplyReceived != NULL) {\r
+    Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived;\r
+  }\r
+\r
+  if (NewDhcpDiscover != NULL) {\r
+    CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+  }\r
+\r
+  if (NewDhcpAck != NULL) {\r
+    CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+  }\r
+\r
+  if (NewProxyOffer != NULL) {\r
+    CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+  }\r
+\r
+  if (NewPxeDiscover != NULL) {\r
+    CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+  }\r
+\r
+  if (NewPxeReply != NULL) {\r
+    CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+  }\r
+\r
+  if (NewPxeBisReply != NULL) {\r
+    CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_PXE_BASE_CODE_PROTOCOL  gPxeBcProtocolTemplate = {\r
+  EFI_PXE_BASE_CODE_PROTOCOL_REVISION,\r
+  EfiPxeBcStart,\r
+  EfiPxeBcStop,\r
+  EfiPxeBcDhcp,\r
+  EfiPxeBcDiscover,\r
+  EfiPxeBcMtftp,\r
+  EfiPxeBcUdpWrite,\r
+  EfiPxeBcUdpRead,\r
+  EfiPxeBcSetIpFilter,\r
+  EfiPxeBcArp,\r
+  EfiPxeBcSetParameters,\r
+  EfiPxeBcSetStationIP,\r
+  EfiPxeBcSetPackets,\r
+  NULL\r
+};\r
+\r
+\r
+/**\r
+  Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has\r
+  received, or is waiting to receive a packet.\r
+\r
+  This function is invoked when the PXE Base Code Protocol is about to transmit, has received,\r
+  or is waiting to receive a packet. Parameters Function and Received specify the type of event.\r
+  Parameters PacketLen and Packet specify the packet that generated the event. If these fields\r
+  are zero and NULL respectively, then this is a status update callback. If the operation specified\r
+  by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation\r
+  specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to\r
+  the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms.\r
+  The SetParameters() function must be called after a Callback Protocol is installed to enable the\r
+  use of callbacks.\r
+\r
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance.\r
+  @param[in]  Function          The PXE Base Code Protocol function that is waiting for an event.\r
+  @param[in]  Received          TRUE if the callback is being invoked due to a receive event. FALSE if\r
+                                the callback is being invoked due to a transmit event.\r
+  @param[in]  PacketLength      The length, in bytes, of Packet. This field will have a value of zero if\r
+                                this is a wait for receive event.\r
+  @param[in]  PacketPtr         If Received is TRUE, a pointer to the packet that was just received;\r
+                                otherwise a pointer to the packet that is about to be transmitted.\r
+\r
+  @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE If Function specifies a continue operation.\r
+  @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT    If Function specifies an abort operation.\r
+\r
+**/\r
+EFI_PXE_BASE_CODE_CALLBACK_STATUS\r
+EFIAPI\r
+EfiPxeLoadFileCallback (\r
+  IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  *This,\r
+  IN EFI_PXE_BASE_CODE_FUNCTION           Function,\r
+  IN BOOLEAN                              Received,\r
+  IN UINT32                               PacketLength,\r
+  IN EFI_PXE_BASE_CODE_PACKET             *PacketPtr     OPTIONAL\r
+  )\r
+{\r
+  EFI_INPUT_KEY       Key;\r
+  EFI_STATUS          Status;\r
+\r
+  //\r
+  // Catch Ctrl-C or ESC to abort.\r
+  //\r
+  Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+\r
+    if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) {\r
+\r
+      return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;\r
+    }\r
+  }\r
+  //\r
+  // No print if receive packet\r
+  //\r
+  if (Received) {\r
+    return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+  }\r
+  //\r
+  // Print only for three functions\r
+  //\r
+  switch (Function) {\r
+\r
+  case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:\r
+    //\r
+    // Print only for open MTFTP packets, not every MTFTP packets\r
+    //\r
+    if (PacketLength != 0 && PacketPtr != NULL) {\r
+      if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {\r
+        return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+      }\r
+    }\r
+    break;\r
+\r
+  case EFI_PXE_BASE_CODE_FUNCTION_DHCP:\r
+  case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:\r
+    break;\r
+\r
+  default:\r
+    return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+  }\r
+\r
+  if (PacketLength != 0 && PacketPtr != NULL) {\r
+    //\r
+    // Print '.' when transmit a packet\r
+    //\r
+    AsciiPrint (".");\r
+  }\r
+\r
+  return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+}\r
+\r
+EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate = {\r
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,\r
+  EfiPxeLoadFileCallback\r
+};\r
+\r
+\r
+/**\r
+  Causes the driver to load a specified file.\r
+\r
+  @param[in]      This                Protocol instance pointer.\r
+  @param[in]      FilePath            The device specific path of the file to load.\r
+  @param[in]      BootPolicy          If TRUE, indicates that the request originates from the\r
+                                      boot manager is attempting to load FilePath as a boot\r
+                                      selection. If FALSE, then FilePath must match an exact file\r
+                                      to be loaded.\r
+  @param[in, out] BufferSize          On input the size of Buffer in bytes. On output with a return\r
+                                      code of EFI_SUCCESS, the amount of data transferred to\r
+                                      Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
+                                      the size of Buffer required to retrieve the requested file.\r
+  @param[in]      Buffer              The memory buffer to transfer the file to. IF Buffer is NULL,\r
+                                      then no the size of the requested file is returned in\r
+                                      BufferSize.\r
+\r
+  @retval EFI_SUCCESS                 The file was loaded.\r
+  @retval EFI_UNSUPPORTED             The device does not support the provided BootPolicy.\r
+  @retval EFI_INVALID_PARAMETER       FilePath is not a valid device path, or\r
+                                      BufferSize is NULL.\r
+  @retval EFI_NO_MEDIA                No medium was present to load the file.\r
+  @retval EFI_DEVICE_ERROR            The file was not loaded due to a device error.\r
+  @retval EFI_NO_RESPONSE             The remote system did not respond.\r
+  @retval EFI_NOT_FOUND               The file was not found.\r
+  @retval EFI_ABORTED                 The file load process was manually cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeLoadFile (\r
+  IN     EFI_LOAD_FILE_PROTOCOL           *This,\r
+  IN     EFI_DEVICE_PATH_PROTOCOL         *FilePath,\r
+  IN     BOOLEAN                          BootPolicy,\r
+  IN OUT UINTN                            *BufferSize,\r
+  IN     VOID                             *Buffer       OPTIONAL\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA          *Private;\r
+  PXEBC_VIRTUAL_NIC           *VirtualNic;\r
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;\r
+  BOOLEAN                     UsingIpv6;\r
+  EFI_STATUS                  Status;\r
+  BOOLEAN                     MediaPresent;\r
+\r
+  VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (This);\r
+  Private    = VirtualNic->Private;\r
+  PxeBc      = &Private->PxeBc;\r
+  UsingIpv6  = FALSE;\r
+  Status     = EFI_DEVICE_ERROR;\r
+\r
+  if (This == NULL || BufferSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Only support BootPolicy\r
+  //\r
+  if (!BootPolicy) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Check media status before PXE start\r
+  //\r
+  MediaPresent = TRUE;\r
+  NetLibDetectMedia (Private->Controller, &MediaPresent);\r
+  if (!MediaPresent) {\r
+    return EFI_NO_MEDIA;\r
+  }\r
+\r
+  //\r
+  // Check whether the virtual nic is using IPv6 or not.\r
+  //\r
+  if (VirtualNic == Private->Ip6Nic) {\r
+    UsingIpv6 = TRUE;\r
+  }\r
+\r
+  //\r
+  // Start Pxe Base Code to initialize PXE boot.\r
+  //\r
+  Status = PxeBc->Start (PxeBc, UsingIpv6);\r
+  if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {\r
+    Status = PxeBcLoadBootFile (Private, BufferSize, Buffer);\r
+  }\r
+\r
+  if (Status != EFI_SUCCESS &&\r
+      Status != EFI_UNSUPPORTED &&\r
+      Status != EFI_BUFFER_TOO_SMALL) {\r
+    //\r
+    // There are three cases, which needn't stop pxebc here.\r
+    //   1. success to download file.\r
+    //   2. success to get file size.\r
+    //   3. unsupported.\r
+    //\r
+    PxeBc->Stop (PxeBc);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+EFI_LOAD_FILE_PROTOCOL  gLoadFileProtocolTemplate = { EfiPxeLoadFile };\r
+\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h
new file mode 100644 (file)
index 0000000..7491cf8
--- /dev/null
@@ -0,0 +1,207 @@
+/** @file\r
+  This EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.\r
+  interfaces declaration.\r
+\r
+  Copyright (c) 2007 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_IMPL_H__\r
+#define __EFI_PXEBC_IMPL_H__\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Guid/SmBios.h>\r
+#include <IndustryStandard/SmBios.h>\r
+#include <Protocol/NetworkInterfaceIdentifier.h>\r
+#include <Protocol/Arp.h>\r
+#include <Protocol/Ip4.h>\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Ip6Config.h>\r
+#include <Protocol/Udp4.h>\r
+#include <Protocol/Udp6.h>\r
+#include <Protocol/Dhcp4.h>\r
+#include <Protocol/Dhcp6.h>\r
+#include <Protocol/Mtftp4.h>\r
+#include <Protocol/Mtftp6.h>\r
+#include <Protocol/PxeBaseCode.h>\r
+#include <Protocol/LoadFile.h>\r
+#include <Protocol/PxeBaseCodeCallBack.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DriverBinding.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/NetLib.h>\r
+#include <Library/DpcLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/PcdLib.h>\r
+\r
+typedef struct _PXEBC_PRIVATE_DATA  PXEBC_PRIVATE_DATA;\r
+typedef struct _PXEBC_VIRTUAL_NIC   PXEBC_VIRTUAL_NIC;\r
+\r
+#include "PxeBcDriver.h"\r
+#include "PxeBcDhcp4.h"\r
+#include "PxeBcDhcp6.h"\r
+#include "PxeBcMtftp.h"\r
+#include "PxeBcBoot.h"\r
+#include "PxeBcSupport.h"\r
+\r
+#define PXEBC_DEFAULT_HOPLIMIT        64\r
+#define PXEBC_DEFAULT_LIFETIME        50000    // 50 ms, unit is microsecond\r
+#define PXEBC_UDP_TIMEOUT             30000000 // 3 seconds, unit is 100nanosecond\r
+#define PXEBC_MTFTP_TIMEOUT           4\r
+#define PXEBC_MTFTP_RETRIES           6\r
+#define PXEBC_DHCP_RETRIES            4        // refers to mPxeDhcpTimeout, also by PXE2.1 spec.\r
+#define PXEBC_MENU_MAX_NUM            24\r
+#define PXEBC_OFFER_MAX_NUM           16\r
+\r
+#define PXEBC_PRIVATE_DATA_SIGNATURE          SIGNATURE_32 ('P', 'X', 'E', 'P')\r
+#define PXEBC_VIRTUAL_NIC_SIGNATURE           SIGNATURE_32 ('P', 'X', 'E', 'V')\r
+#define PXEBC_PRIVATE_DATA_FROM_PXEBC(a)      CR (a, PXEBC_PRIVATE_DATA, PxeBc, PXEBC_PRIVATE_DATA_SIGNATURE)\r
+#define PXEBC_VIRTUAL_NIC_FROM_LOADFILE(a)    CR (a, PXEBC_VIRTUAL_NIC, LoadFile, PXEBC_VIRTUAL_NIC_SIGNATURE)\r
+\r
+typedef union {\r
+  PXEBC_DHCP4_PACKET_CACHE            Dhcp4;\r
+  PXEBC_DHCP6_PACKET_CACHE            Dhcp6;\r
+} PXEBC_DHCP_PACKET_CACHE;\r
+\r
+struct _PXEBC_VIRTUAL_NIC {\r
+  UINT32                                    Signature;\r
+  EFI_HANDLE                                Controller;\r
+  EFI_LOAD_FILE_PROTOCOL                    LoadFile;\r
+  EFI_DEVICE_PATH_PROTOCOL                  *DevicePath;\r
+  PXEBC_PRIVATE_DATA                        *Private;\r
+};\r
+\r
+struct _PXEBC_PRIVATE_DATA {\r
+  UINT32                                    Signature;\r
+  EFI_HANDLE                                Controller;\r
+  EFI_HANDLE                                Image;\r
+\r
+  PXEBC_VIRTUAL_NIC                         *Ip4Nic;\r
+  PXEBC_VIRTUAL_NIC                         *Ip6Nic;\r
+\r
+  EFI_HANDLE                                ArpChild;\r
+  EFI_HANDLE                                Ip4Child;\r
+  EFI_HANDLE                                Dhcp4Child;\r
+  EFI_HANDLE                                Mtftp4Child;\r
+  EFI_HANDLE                                Udp4ReadChild;\r
+  EFI_HANDLE                                Udp4WriteChild;\r
+\r
+  EFI_ARP_PROTOCOL                          *Arp;\r
+  EFI_IP4_PROTOCOL                          *Ip4;\r
+  EFI_DHCP4_PROTOCOL                        *Dhcp4;\r
+  EFI_MTFTP4_PROTOCOL                       *Mtftp4;\r
+  EFI_UDP4_PROTOCOL                         *Udp4Read;\r
+  EFI_UDP4_PROTOCOL                         *Udp4Write;\r
+\r
+  EFI_HANDLE                                Ip6Child;\r
+  EFI_HANDLE                                Dhcp6Child;\r
+  EFI_HANDLE                                Mtftp6Child;\r
+  EFI_HANDLE                                Udp6ReadChild;\r
+  EFI_HANDLE                                Udp6WriteChild;\r
+\r
+  EFI_IP6_PROTOCOL                          *Ip6;\r
+  EFI_IP6_CONFIG_PROTOCOL                   *Ip6Cfg;\r
+  EFI_DHCP6_PROTOCOL                        *Dhcp6;\r
+  EFI_MTFTP6_PROTOCOL                       *Mtftp6;\r
+  EFI_UDP6_PROTOCOL                         *Udp6Read;\r
+  EFI_UDP6_PROTOCOL                         *Udp6Write;\r
+\r
+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;\r
+  EFI_PXE_BASE_CODE_PROTOCOL                PxeBc;\r
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL       LoadFileCallback;\r
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL       *PxeBcCallback;\r
+  EFI_DEVICE_PATH_PROTOCOL                  *DevicePath;\r
+\r
+  EFI_PXE_BASE_CODE_MODE                    Mode;\r
+  EFI_PXE_BASE_CODE_FUNCTION                Function;\r
+  UINT32                                    Ip6Policy;\r
+\r
+  EFI_UDP4_CONFIG_DATA                      Udp4CfgData;\r
+  EFI_UDP6_CONFIG_DATA                      Udp6CfgData;\r
+  EFI_IP4_CONFIG_DATA                       Ip4CfgData;\r
+  EFI_IP6_CONFIG_DATA                       Ip6CfgData;\r
+\r
+  EFI_EVENT                                 UdpTimeOutEvent;\r
+  EFI_EVENT                                 ArpUpdateEvent;\r
+  EFI_IP4_COMPLETION_TOKEN                  IcmpToken;\r
+  EFI_IP6_COMPLETION_TOKEN                  Icmp6Token;\r
+\r
+  BOOLEAN                                   IsAddressOk;\r
+  BOOLEAN                                   IsOfferSorted;\r
+  BOOLEAN                                   IsProxyRecved;\r
+  BOOLEAN                                   IsDoDiscover;\r
+\r
+  EFI_IP_ADDRESS                            StationIp;\r
+  EFI_IP_ADDRESS                            SubnetMask;\r
+  EFI_IP_ADDRESS                            GatewayIp;\r
+  EFI_IP_ADDRESS                            ServerIp;\r
+  UINT16                                    CurSrcPort;\r
+\r
+  UINT32                                    Ip4MaxPacketSize;\r
+  UINT32                                    Ip6MaxPacketSize;\r
+  UINT8                                     *BootFileName;\r
+  UINTN                                     BootFileSize;\r
+  UINTN                                     BlockSize;\r
+\r
+  PXEBC_DHCP_PACKET_CACHE                   ProxyOffer;\r
+  PXEBC_DHCP_PACKET_CACHE                   DhcpAck;\r
+  PXEBC_DHCP_PACKET_CACHE                   PxeReply;\r
+  EFI_DHCP6_PACKET                          *Dhcp6Request;\r
+  EFI_DHCP4_PACKET                          SeedPacket;\r
+\r
+  //\r
+  // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer.\r
+  //\r
+  // It supposed that\r
+  //\r
+  //   OfferNum:    8\r
+  //   OfferBuffer: [ProxyBinl, ProxyBinl, DhcpOnly, ProxyPxe10, DhcpOnly, DhcpPxe10, DhcpBinl, ProxyBinl]\r
+  //   (OfferBuffer is 0-based.)\r
+  //\r
+  // And assume that (DhcpPxe10 is the first priority actually.)\r
+  //\r
+  //   SelectIndex:     2\r
+  //   SelectProxyType: PXEBC_OFFER_TYPE_PROXY_BINL\r
+  //   (SelectIndex is 1-based, and 0 means no one is selected.)\r
+  //\r
+  // So it should be\r
+  //\r
+  //                 DhcpOnly  DhcpPxe10  DhcpWfm11a  DhcpBinl  ProxyPxe10  ProxyWfm11a  ProxyBinl  Bootp\r
+  //   OfferCount:  [    2(n),      1(n),       0(n),     1(n),       1(1),        0(1),      3(n),  1(1)]\r
+  //\r
+  //   OfferIndex: {[       2,         5,          0,        6,          3,           0,        *0,     0]\r
+  //                [       4,         0,          0,        0,          0,           0,         1,     0]\r
+  //                [       0,         0,          0,        0,          0,           0,         7,     0]\r
+  //                ...                                                                                  ]}\r
+  //   (OfferIndex is 0-based.)\r
+  //\r
+  //\r
+  UINT32                                    SelectIndex;\r
+  UINT32                                    SelectProxyType;\r
+  PXEBC_DHCP_PACKET_CACHE                   OfferBuffer[PXEBC_OFFER_MAX_NUM];\r
+  UINT32                                    OfferNum;\r
+  UINT32                                    OfferCount[PxeOfferTypeMax];\r
+  UINT32                                    OfferIndex[PxeOfferTypeMax][PXEBC_OFFER_MAX_NUM];\r
+};\r
+\r
+extern EFI_PXE_BASE_CODE_PROTOCOL           gPxeBcProtocolTemplate;\r
+extern EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  gPxeBcCallBackTemplate;\r
+extern EFI_LOAD_FILE_PROTOCOL               gLoadFileProtocolTemplate;\r
+\r
+#endif\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c
new file mode 100644 (file)
index 0000000..8adba66
--- /dev/null
@@ -0,0 +1,1105 @@
+/** @file\r
+  Functions implementation related with Mtftp for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2007 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+CHAR8 *mMtftpOptions[PXE_MTFTP_OPTION_MAXIMUM_INDEX] = {\r
+  "blksize",\r
+  "timeout",\r
+  "tsize",\r
+  "multicast"\r
+};\r
+\r
+\r
+/**\r
+  This is a callback function when packets are received or transmitted in Mtftp driver.\r
+\r
+  A callback function that is provided by the caller to intercept\r
+  the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP6_OPCODE_DATA8 packets processed in the\r
+  EFI_MTFTP6_PROTOCOL.ReadFile() function, and alternatively to intercept\r
+  EFI_MTFTP6_OPCODE_OACK or EFI_MTFTP6_OPCODE_ERROR packets during a call to\r
+  EFI_MTFTP6_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().\r
+\r
+  @param[in]  This           Pointer to EFI_MTFTP6_PROTOCOL.\r
+  @param[in]  Token          Pointer to EFI_MTFTP6_TOKEN.\r
+  @param[in]  PacketLen      Length of EFI_MTFTP6_PACKET.\r
+  @param[in]  Packet         Pointer to EFI_MTFTP6_PACKET to be checked.\r
+\r
+  @retval EFI_SUCCESS    The current operation succeeded.\r
+  @retval EFI_ABORTED    Abort the current transfer process.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcMtftp6CheckPacket (\r
+  IN EFI_MTFTP6_PROTOCOL              *This,\r
+  IN EFI_MTFTP6_TOKEN                 *Token,\r
+  IN UINT16                           PacketLen,\r
+  IN EFI_MTFTP6_PACKET                *Packet\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA                  *Private;\r
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;\r
+  EFI_STATUS                          Status;\r
+\r
+  Private   = (PXEBC_PRIVATE_DATA *) Token->Context;\r
+  Callback  = Private->PxeBcCallback;\r
+  Status    = EFI_SUCCESS;\r
+\r
+  if (Packet->OpCode == EFI_MTFTP6_OPCODE_ERROR) {\r
+    //\r
+    // Store the tftp error message into mode data and set the received flag.\r
+    //\r
+    Private->Mode.TftpErrorReceived   = TRUE;\r
+    Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;\r
+    AsciiStrnCpy (\r
+      Private->Mode.TftpError.ErrorString,\r
+      (CHAR8 *) Packet->Error.ErrorMessage,\r
+      PXE_MTFTP_ERROR_STRING_LENGTH\r
+      );\r
+  }\r
+\r
+  if (Callback != NULL) {\r
+    //\r
+    // Callback to user if has when received any tftp packet.\r
+    //\r
+    Status = Callback->Callback (\r
+                        Callback,\r
+                        Private->Function,\r
+                        TRUE,\r
+                        PacketLen,\r
+                        (EFI_PXE_BASE_CODE_PACKET *) Packet\r
+                        );\r
+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+      //\r
+      // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.\r
+      //\r
+      Status = EFI_ABORTED;\r
+    } else {\r
+      //\r
+      // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.\r
+      //\r
+      Status = EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to get the size of a file using Tftp.\r
+\r
+  @param[in]      Private        Pointer to PxeBc private data.\r
+  @param[in]      Config         Pointer to EFI_MTFTP6_CONFIG_DATA.\r
+  @param[in]      Filename       Pointer to boot file name.\r
+  @param[in]      BlockSize      Pointer to required block size.\r
+  @param[in, out] BufferSize     Pointer to buffer size.\r
+\r
+  @retval EFI_SUCCESS        Sucessfully obtained the size of file.\r
+  @retval EFI_NOT_FOUND      Parse the tftp ptions failed.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Has not obtained the size of the file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp6GetFileSize (\r
+  IN     PXEBC_PRIVATE_DATA           *Private,\r
+  IN     EFI_MTFTP6_CONFIG_DATA       *Config,\r
+  IN     UINT8                        *Filename,\r
+  IN     UINTN                        *BlockSize,\r
+  IN OUT UINT64                       *BufferSize\r
+  )\r
+{\r
+  EFI_MTFTP6_PROTOCOL                 *Mtftp6;\r
+  EFI_MTFTP6_OPTION                   ReqOpt[2];\r
+  EFI_MTFTP6_PACKET                   *Packet;\r
+  EFI_MTFTP6_OPTION                   *Option;\r
+  UINT32                              PktLen;\r
+  UINT8                               OptBuf[128];\r
+  UINT32                              OptCnt;\r
+  EFI_STATUS                          Status;\r
+\r
+  *BufferSize               = 0;\r
+  Status                    = EFI_DEVICE_ERROR;\r
+  Mtftp6                    = Private->Mtftp6;\r
+  Packet                    = NULL;\r
+  Option                    = NULL;\r
+  PktLen                    = 0;\r
+  OptCnt                    = 1;\r
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+  Status = Mtftp6->Configure (Mtftp6, Config);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Build the required options for get info.\r
+  //\r
+  ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];\r
+  PxeBcUintnToAscDec (0, OptBuf);\r
+  ReqOpt[0].ValueStr  = OptBuf;\r
+\r
+  if (BlockSize != NULL) {\r
+    ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+    ReqOpt[1].ValueStr  = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);\r
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr);\r
+    OptCnt++;\r
+  }\r
+\r
+  Status = Mtftp6->GetInfo (\r
+                     Mtftp6,\r
+                     FALSE,\r
+                     Filename,\r
+                     NULL,\r
+                     (UINT8) OptCnt,\r
+                     ReqOpt,\r
+                     &PktLen,\r
+                     &Packet\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_TFTP_ERROR) {\r
+      //\r
+      // Store the tftp error message into mode data and set the received flag.\r
+      //\r
+      Private->Mode.TftpErrorReceived   = TRUE;\r
+      Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;\r
+      AsciiStrnCpy (\r
+        Private->Mode.TftpError.ErrorString,\r
+        (CHAR8 *) Packet->Error.ErrorMessage,\r
+        PXE_MTFTP_ERROR_STRING_LENGTH\r
+        );\r
+    }\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Parse the options in the reply packet.\r
+  //\r
+  OptCnt = 0;\r
+  Status = Mtftp6->ParseOptions (\r
+                     Mtftp6,\r
+                     PktLen,\r
+                     Packet,\r
+                     (UINT32 *) &OptCnt,\r
+                     &Option\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Parse out the value of "tsize" option.\r
+  //\r
+  Status = EFI_NOT_FOUND;\r
+  while (OptCnt != 0) {\r
+    if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {\r
+      *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));\r
+      Status      = EFI_SUCCESS;\r
+    }\r
+    OptCnt--;\r
+  }\r
+  FreePool (Option);\r
+\r
+ON_ERROR:\r
+  if (Packet != NULL) {\r
+    FreePool (Packet);\r
+  }\r
+  Mtftp6->Configure (Mtftp6, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to get data of a file using Tftp.\r
+\r
+  @param[in]      Private        Pointer to PxeBc private data.\r
+  @param[in]      Config         Pointer to EFI_MTFTP6_CONFIG_DATA.\r
+  @param[in]      Filename       Pointer to boot file name.\r
+  @param[in]      BlockSize      Pointer to required block size.\r
+  @param[in]      BufferPtr      Pointer to buffer.\r
+  @param[in, out] BufferSize     Pointer to buffer size.\r
+  @param[in]      DontUseBuffer  Indicates whether with a receive buffer.\r
+\r
+  @retval EFI_SUCCESS        Successfully read the data from the special file.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Read data from file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp6ReadFile (\r
+  IN    PXEBC_PRIVATE_DATA            *Private,\r
+  IN     EFI_MTFTP6_CONFIG_DATA       *Config,\r
+  IN     UINT8                        *Filename,\r
+  IN     UINTN                        *BlockSize,\r
+  IN     UINT8                        *BufferPtr,\r
+  IN OUT UINT64                       *BufferSize,\r
+  IN     BOOLEAN                      DontUseBuffer\r
+  )\r
+{\r
+  EFI_MTFTP6_PROTOCOL                 *Mtftp6;\r
+  EFI_MTFTP6_TOKEN                    Token;\r
+  EFI_MTFTP6_OPTION                   ReqOpt[1];\r
+  UINT32                              OptCnt;\r
+  UINT8                               OptBuf[128];\r
+  EFI_STATUS                          Status;\r
+\r
+  Status                    = EFI_DEVICE_ERROR;\r
+  Mtftp6                    = Private->Mtftp6;\r
+  OptCnt                    = 0;\r
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+  Status = Mtftp6->Configure (Mtftp6, Config);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (BlockSize != NULL) {\r
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+    ReqOpt[0].ValueStr  = OptBuf;\r
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+    OptCnt++;\r
+  }\r
+\r
+  Token.Event         = NULL;\r
+  Token.OverrideData  = NULL;\r
+  Token.Filename      = Filename;\r
+  Token.ModeStr       = NULL;\r
+  Token.OptionCount   = OptCnt;\r
+  Token.OptionList    = ReqOpt;\r
+  Token.Context       = Private;\r
+\r
+  if (DontUseBuffer) {\r
+    Token.BufferSize  = 0;\r
+    Token.Buffer      = NULL;\r
+  } else {\r
+    Token.BufferSize  = *BufferSize;\r
+    Token.Buffer      = BufferPtr;\r
+  }\r
+\r
+  Token.CheckPacket     = PxeBcMtftp6CheckPacket;\r
+  Token.TimeoutCallback = NULL;\r
+  Token.PacketNeeded    = NULL;\r
+\r
+  Status = Mtftp6->ReadFile (Mtftp6, &Token);\r
+  //\r
+  // Get the real size of received buffer.\r
+  //\r
+  *BufferSize = Token.BufferSize;\r
+\r
+  Mtftp6->Configure (Mtftp6, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is used to write the data of a file using Tftp.\r
+\r
+  @param[in]       Private        Pointer to PxeBc private data.\r
+  @param[in]       Config         Pointer to EFI_MTFTP6_CONFIG_DATA.\r
+  @param[in]       Filename       Pointer to boot file name.\r
+  @param[in]       Overwrite      Indicate whether with overwrite attribute.\r
+  @param[in]       BlockSize      Pointer to required block size.\r
+  @param[in]       BufferPtr      Pointer to buffer.\r
+  @param[in, out]  BufferSize     Pointer to buffer size.\r
+\r
+  @retval EFI_SUCCESS        Successfully wrote the data into a special file.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval other              Write data into file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp6WriteFile (\r
+  IN     PXEBC_PRIVATE_DATA           *Private,\r
+  IN     EFI_MTFTP6_CONFIG_DATA       *Config,\r
+  IN     UINT8                        *Filename,\r
+  IN     BOOLEAN                      Overwrite,\r
+  IN     UINTN                        *BlockSize,\r
+  IN     UINT8                        *BufferPtr,\r
+  IN OUT UINT64                       *BufferSize\r
+  )\r
+{\r
+  EFI_MTFTP6_PROTOCOL                 *Mtftp6;\r
+  EFI_MTFTP6_TOKEN                    Token;\r
+  EFI_MTFTP6_OPTION                   ReqOpt[1];\r
+  UINT32                              OptCnt;\r
+  UINT8                               OptBuf[128];\r
+  EFI_STATUS                          Status;\r
+\r
+  Status                    = EFI_DEVICE_ERROR;\r
+  Mtftp6                    = Private->Mtftp6;\r
+  OptCnt                    = 0;\r
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+  Status = Mtftp6->Configure (Mtftp6, Config);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (BlockSize != NULL) {\r
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+    ReqOpt[0].ValueStr  = OptBuf;\r
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+    OptCnt++;\r
+  }\r
+\r
+  Token.Event           = NULL;\r
+  Token.OverrideData    = NULL;\r
+  Token.Filename        = Filename;\r
+  Token.ModeStr         = NULL;\r
+  Token.OptionCount     = OptCnt;\r
+  Token.OptionList      = ReqOpt;\r
+  Token.BufferSize      = *BufferSize;\r
+  Token.Buffer          = BufferPtr;\r
+  Token.CheckPacket     = PxeBcMtftp6CheckPacket;\r
+  Token.TimeoutCallback = NULL;\r
+  Token.PacketNeeded    = NULL;\r
+\r
+  Status = Mtftp6->WriteFile (Mtftp6, &Token);\r
+  //\r
+  // Get the real size of transmitted buffer.\r
+  //\r
+  *BufferSize = Token.BufferSize;\r
+\r
+  Mtftp6->Configure (Mtftp6, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to read the data (file) from a directory using Tftp.\r
+\r
+  @param[in]       Private        Pointer to PxeBc private data.\r
+  @param[in]       Config         Pointer to EFI_MTFTP6_CONFIG_DATA.\r
+  @param[in]       Filename       Pointer to boot file name.\r
+  @param[in]       BlockSize      Pointer to required block size.\r
+  @param[in]       BufferPtr      Pointer to buffer.\r
+  @param[in, out]  BufferSize     Pointer to buffer size.\r
+  @param[in]       DontUseBuffer  Indicates whether to use a receive buffer.\r
+\r
+  @retval EFI_SUCCESS        Successfully obtained the data from the file included in directory.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Operation failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp6ReadDirectory (\r
+  IN     PXEBC_PRIVATE_DATA            *Private,\r
+  IN     EFI_MTFTP6_CONFIG_DATA        *Config,\r
+  IN     UINT8                         *Filename,\r
+  IN     UINTN                         *BlockSize,\r
+  IN     UINT8                         *BufferPtr,\r
+  IN OUT UINT64                        *BufferSize,\r
+  IN     BOOLEAN                       DontUseBuffer\r
+  )\r
+{\r
+  EFI_MTFTP6_PROTOCOL                  *Mtftp6;\r
+  EFI_MTFTP6_TOKEN                     Token;\r
+  EFI_MTFTP6_OPTION                    ReqOpt[1];\r
+  UINT32                               OptCnt;\r
+  UINT8                                OptBuf[128];\r
+  EFI_STATUS                           Status;\r
+\r
+  Status                    = EFI_DEVICE_ERROR;\r
+  Mtftp6                    = Private->Mtftp6;\r
+  OptCnt                    = 0;\r
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+  Status = Mtftp6->Configure (Mtftp6, Config);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (BlockSize != NULL) {\r
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+    ReqOpt[0].ValueStr  = OptBuf;\r
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+    OptCnt++;\r
+  }\r
+\r
+  Token.Event         = NULL;\r
+  Token.OverrideData  = NULL;\r
+  Token.Filename      = Filename;\r
+  Token.ModeStr       = NULL;\r
+  Token.OptionCount   = OptCnt;\r
+  Token.OptionList    = ReqOpt;\r
+  Token.Context       = Private;\r
+\r
+  if (DontUseBuffer) {\r
+    Token.BufferSize  = 0;\r
+    Token.Buffer      = NULL;\r
+  } else {\r
+    Token.BufferSize  = *BufferSize;\r
+    Token.Buffer      = BufferPtr;\r
+  }\r
+\r
+  Token.CheckPacket     = PxeBcMtftp6CheckPacket;\r
+  Token.TimeoutCallback = NULL;\r
+  Token.PacketNeeded    = NULL;\r
+\r
+  Status = Mtftp6->ReadDirectory (Mtftp6, &Token);\r
+  //\r
+  // Get the real size of received buffer.\r
+  //\r
+  *BufferSize = Token.BufferSize;\r
+\r
+  Mtftp6->Configure (Mtftp6, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This is a callback function when packets are received or transmitted in Mtftp driver.\r
+\r
+  A callback function that is provided by the caller to intercept\r
+  the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the\r
+  EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept\r
+  EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to\r
+  EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().\r
+\r
+  @param[in]  This           Pointer to EFI_MTFTP4_PROTOCOL.\r
+  @param[in]  Token          Pointer to EFI_MTFTP4_TOKEN.\r
+  @param[in]  PacketLen      Length of EFI_MTFTP4_PACKET.\r
+  @param[in]  Packet         Pointer to EFI_MTFTP4_PACKET to be checked.\r
+\r
+  @retval EFI_SUCCESS    The current operation succeeeded.\r
+  @retval EFI_ABORTED    Abort the current transfer process.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcMtftp4CheckPacket (\r
+  IN EFI_MTFTP4_PROTOCOL        *This,\r
+  IN EFI_MTFTP4_TOKEN           *Token,\r
+  IN UINT16                     PacketLen,\r
+  IN EFI_MTFTP4_PACKET          *Packet\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA                  *Private;\r
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;\r
+  EFI_STATUS                          Status;\r
+\r
+  Private   = (PXEBC_PRIVATE_DATA *) Token->Context;\r
+  Callback  = Private->PxeBcCallback;\r
+  Status    = EFI_SUCCESS;\r
+\r
+  if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) {\r
+    //\r
+    // Store the tftp error message into mode data and set the received flag.\r
+    //\r
+    Private->Mode.TftpErrorReceived   = TRUE;\r
+    Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;\r
+    AsciiStrnCpy (\r
+      Private->Mode.TftpError.ErrorString,\r
+      (CHAR8 *) Packet->Error.ErrorMessage,\r
+      PXE_MTFTP_ERROR_STRING_LENGTH\r
+      );\r
+  }\r
+\r
+  if (Callback != NULL) {\r
+    //\r
+    // Callback to user if has when received any tftp packet.\r
+    //\r
+    Status = Callback->Callback (\r
+                        Callback,\r
+                        Private->Function,\r
+                        TRUE,\r
+                        PacketLen,\r
+                        (EFI_PXE_BASE_CODE_PACKET *) Packet\r
+                        );\r
+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+      //\r
+      // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.\r
+      //\r
+      Status = EFI_ABORTED;\r
+    } else {\r
+      //\r
+      // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.\r
+      //\r
+      Status = EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to get size of a file using Tftp.\r
+\r
+  @param[in]      Private        Pointer to PxeBc private data.\r
+  @param[in]      Config         Pointer to EFI_MTFTP4_CONFIG_DATA.\r
+  @param[in]      Filename       Pointer to boot file name.\r
+  @param[in]      BlockSize      Pointer to required block size.\r
+  @param[in, out] BufferSize     Pointer to buffer size.\r
+\r
+  @retval EFI_SUCCESS        Successfully obtained the size of file.\r
+  @retval EFI_NOT_FOUND      Parse the tftp options failed.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Did not obtain the size of the file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp4GetFileSize (\r
+  IN     PXEBC_PRIVATE_DATA         *Private,\r
+  IN     EFI_MTFTP4_CONFIG_DATA     *Config,\r
+  IN     UINT8                      *Filename,\r
+  IN     UINTN                      *BlockSize,\r
+  IN OUT UINT64                     *BufferSize\r
+  )\r
+{\r
+  EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+  EFI_MTFTP4_OPTION   ReqOpt[2];\r
+  EFI_MTFTP4_PACKET   *Packet;\r
+  EFI_MTFTP4_OPTION   *Option;\r
+  UINT32              PktLen;\r
+  UINT8               OptBuf[128];\r
+  UINT32              OptCnt;\r
+  EFI_STATUS          Status;\r
+\r
+  *BufferSize               = 0;\r
+  Status                    = EFI_DEVICE_ERROR;\r
+  Mtftp4                    = Private->Mtftp4;\r
+  Packet                    = NULL;\r
+  Option                    = NULL;\r
+  PktLen                    = 0;\r
+  OptCnt                    = 1;\r
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+  Status = Mtftp4->Configure (Mtftp4, Config);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Build the required options for get info.\r
+  //\r
+  ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];\r
+  PxeBcUintnToAscDec (0, OptBuf);\r
+  ReqOpt[0].ValueStr  = OptBuf;\r
+\r
+  if (BlockSize != NULL) {\r
+    ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+    ReqOpt[1].ValueStr  = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);\r
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr);\r
+    OptCnt++;\r
+  }\r
+\r
+  Status = Mtftp4->GetInfo (\r
+                     Mtftp4,\r
+                     FALSE,\r
+                     Filename,\r
+                     NULL,\r
+                     (UINT8) OptCnt,\r
+                     ReqOpt,\r
+                     &PktLen,\r
+                     &Packet\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    if (Status == EFI_TFTP_ERROR) {\r
+      //\r
+      // Store the tftp error message into mode data and set the received flag.\r
+      //\r
+      Private->Mode.TftpErrorReceived   = TRUE;\r
+      Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;\r
+      AsciiStrnCpy (\r
+        Private->Mode.TftpError.ErrorString,\r
+        (CHAR8 *) Packet->Error.ErrorMessage,\r
+        PXE_MTFTP_ERROR_STRING_LENGTH\r
+        );\r
+    }\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Parse the options in the reply packet.\r
+  //\r
+  OptCnt = 0;\r
+  Status = Mtftp4->ParseOptions (\r
+                     Mtftp4,\r
+                     PktLen,\r
+                     Packet,\r
+                     (UINT32 *) &OptCnt,\r
+                     &Option\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Parse out the value of "tsize" option.\r
+  //\r
+  Status = EFI_NOT_FOUND;\r
+  while (OptCnt != 0) {\r
+    if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {\r
+      *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));\r
+      Status      = EFI_SUCCESS;\r
+    }\r
+    OptCnt--;\r
+  }\r
+  FreePool (Option);\r
+\r
+ON_ERROR:\r
+  if (Packet != NULL) {\r
+    FreePool (Packet);\r
+  }\r
+  Mtftp4->Configure (Mtftp4, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to read the data of a file using Tftp.\r
+\r
+  @param[in]      Private        Pointer to PxeBc private data.\r
+  @param[in]      Config         Pointer to EFI_MTFTP4_CONFIG_DATA.\r
+  @param[in]      Filename       Pointer to boot file name.\r
+  @param[in]      BlockSize      Pointer to required block size.\r
+  @param[in]      BufferPtr      Pointer to buffer.\r
+  @param[in, out] BufferSize     Pointer to buffer size.\r
+  @param[in]      DontUseBuffer  Indicates whether to use a receive buffer.\r
+\r
+  @retval EFI_SUCCESS        Successfully read the data from the special file.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Read data from file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp4ReadFile (\r
+  IN     PXEBC_PRIVATE_DATA         *Private,\r
+  IN     EFI_MTFTP4_CONFIG_DATA     *Config,\r
+  IN     UINT8                      *Filename,\r
+  IN     UINTN                      *BlockSize,\r
+  IN     UINT8                      *BufferPtr,\r
+  IN OUT UINT64                     *BufferSize,\r
+  IN     BOOLEAN                    DontUseBuffer\r
+  )\r
+{\r
+  EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+  EFI_MTFTP4_TOKEN    Token;\r
+  EFI_MTFTP4_OPTION   ReqOpt[1];\r
+  UINT32              OptCnt;\r
+  UINT8               OptBuf[128];\r
+  EFI_STATUS          Status;\r
+\r
+  Status                    = EFI_DEVICE_ERROR;\r
+  Mtftp4                    = Private->Mtftp4;\r
+  OptCnt                    = 0;\r
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+  Status = Mtftp4->Configure (Mtftp4, Config);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (BlockSize != NULL) {\r
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+    ReqOpt[0].ValueStr  = OptBuf;\r
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+    OptCnt++;\r
+  }\r
+\r
+  Token.Event         = NULL;\r
+  Token.OverrideData  = NULL;\r
+  Token.Filename      = Filename;\r
+  Token.ModeStr       = NULL;\r
+  Token.OptionCount   = OptCnt;\r
+  Token.OptionList    = ReqOpt;\r
+  Token.Context       = Private;\r
+\r
+  if (DontUseBuffer) {\r
+    Token.BufferSize  = 0;\r
+    Token.Buffer      = NULL;\r
+  } else {\r
+    Token.BufferSize  = *BufferSize;\r
+    Token.Buffer      = BufferPtr;\r
+  }\r
+\r
+  Token.CheckPacket     = PxeBcMtftp4CheckPacket;\r
+  Token.TimeoutCallback = NULL;\r
+  Token.PacketNeeded    = NULL;\r
+\r
+  Status = Mtftp4->ReadFile (Mtftp4, &Token);\r
+  //\r
+  // Get the real size of received buffer.\r
+  //\r
+  *BufferSize = Token.BufferSize;\r
+\r
+  Mtftp4->Configure (Mtftp4, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to write the data of a file using Tftp.\r
+\r
+  @param[in]       Private        Pointer to PxeBc private data.\r
+  @param[in]       Config         Pointer to EFI_MTFTP4_CONFIG_DATA.\r
+  @param[in]       Filename       Pointer to boot file name.\r
+  @param[in]       Overwrite      Indicates whether to use the overwrite attribute.\r
+  @param[in]       BlockSize      Pointer to required block size.\r
+  @param[in]       BufferPtr      Pointer to buffer.\r
+  @param[in, out]  BufferSize     Pointer to buffer size.\r
+\r
+  @retval EFI_SUCCESS        Successfully write the data  into the special file.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval other              Write data into file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp4WriteFile (\r
+  IN     PXEBC_PRIVATE_DATA         *Private,\r
+  IN     EFI_MTFTP4_CONFIG_DATA     *Config,\r
+  IN     UINT8                      *Filename,\r
+  IN     BOOLEAN                    Overwrite,\r
+  IN     UINTN                      *BlockSize,\r
+  IN     UINT8                      *BufferPtr,\r
+  IN OUT UINT64                     *BufferSize\r
+  )\r
+{\r
+  EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+  EFI_MTFTP4_TOKEN    Token;\r
+  EFI_MTFTP4_OPTION   ReqOpt[1];\r
+  UINT32              OptCnt;\r
+  UINT8               OptBuf[128];\r
+  EFI_STATUS          Status;\r
+\r
+  Status                    = EFI_DEVICE_ERROR;\r
+  Mtftp4                    = Private->Mtftp4;\r
+  OptCnt                    = 0;\r
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+  Status  = Mtftp4->Configure (Mtftp4, Config);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (BlockSize != NULL) {\r
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+    ReqOpt[0].ValueStr  = OptBuf;\r
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+    OptCnt++;\r
+  }\r
+\r
+  Token.Event           = NULL;\r
+  Token.OverrideData    = NULL;\r
+  Token.Filename        = Filename;\r
+  Token.ModeStr         = NULL;\r
+  Token.OptionCount     = OptCnt;\r
+  Token.OptionList      = ReqOpt;\r
+  Token.BufferSize      = *BufferSize;\r
+  Token.Buffer          = BufferPtr;\r
+  Token.CheckPacket     = PxeBcMtftp4CheckPacket;\r
+  Token.TimeoutCallback = NULL;\r
+  Token.PacketNeeded    = NULL;\r
+\r
+  Status = Mtftp4->WriteFile (Mtftp4, &Token);\r
+  //\r
+  // Get the real size of transmitted buffer.\r
+  //\r
+  *BufferSize = Token.BufferSize;\r
+\r
+  Mtftp4->Configure (Mtftp4, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to get data (file) from a directory using Tftp.\r
+\r
+  @param[in]       Private        Pointer to PxeBc private data.\r
+  @param[in]       Config         Pointer to EFI_MTFTP4_CONFIG_DATA.\r
+  @param[in]       Filename       Pointer to boot file name.\r
+  @param[in]       BlockSize      Pointer to required block size.\r
+  @param[in]       BufferPtr      Pointer to buffer.\r
+  @param[in, out]  BufferSize     Pointer to buffer size.\r
+  @param[in]       DontUseBuffer  Indicates whether to use a receive buffer.\r
+\r
+  @retval EFI_SUCCES         Successfully obtained the data from the file included in the directory.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Operation failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp4ReadDirectory (\r
+  IN     PXEBC_PRIVATE_DATA            *Private,\r
+  IN     EFI_MTFTP4_CONFIG_DATA        *Config,\r
+  IN     UINT8                         *Filename,\r
+  IN     UINTN                         *BlockSize,\r
+  IN     UINT8                         *BufferPtr,\r
+  IN OUT UINT64                        *BufferSize,\r
+  IN     BOOLEAN                       DontUseBuffer\r
+  )\r
+{\r
+  EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+  EFI_MTFTP4_TOKEN    Token;\r
+  EFI_MTFTP4_OPTION   ReqOpt[1];\r
+  UINT32              OptCnt;\r
+  UINT8               OptBuf[128];\r
+  EFI_STATUS          Status;\r
+\r
+  Status                    = EFI_DEVICE_ERROR;\r
+  Mtftp4                    = Private->Mtftp4;\r
+  OptCnt                    = 0;\r
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+  Status = Mtftp4->Configure (Mtftp4, Config);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (BlockSize != NULL) {\r
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+    ReqOpt[0].ValueStr  = OptBuf;\r
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+    OptCnt++;\r
+  }\r
+\r
+  Token.Event         = NULL;\r
+  Token.OverrideData  = NULL;\r
+  Token.Filename      = Filename;\r
+  Token.ModeStr       = NULL;\r
+  Token.OptionCount   = OptCnt;\r
+  Token.OptionList    = ReqOpt;\r
+  Token.Context       = Private;\r
+\r
+  if (DontUseBuffer) {\r
+    Token.BufferSize  = 0;\r
+    Token.Buffer      = NULL;\r
+  } else {\r
+    Token.BufferSize  = *BufferSize;\r
+    Token.Buffer      = BufferPtr;\r
+  }\r
+\r
+  Token.CheckPacket     = PxeBcMtftp4CheckPacket;\r
+  Token.TimeoutCallback = NULL;\r
+  Token.PacketNeeded    = NULL;\r
+\r
+  Status = Mtftp4->ReadDirectory (Mtftp4, &Token);\r
+  //\r
+  // Get the real size of received buffer.\r
+  //\r
+  *BufferSize = Token.BufferSize;\r
+\r
+  Mtftp4->Configure (Mtftp4, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is wrapper to get the file size using TFTP.\r
+\r
+  @param[in]      Private        Pointer to PxeBc private data.\r
+  @param[in]      Config         Pointer to configure data.\r
+  @param[in]      Filename       Pointer to boot file name.\r
+  @param[in]      BlockSize      Pointer to required block size.\r
+  @param[in, out] BufferSize     Pointer to buffer size.\r
+\r
+  @retval EFI_SUCCESS        Successfully obtained the size of file.\r
+  @retval EFI_NOT_FOUND      Parse the tftp options failed.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Did not obtain the size of the file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpGetFileSize (\r
+  IN     PXEBC_PRIVATE_DATA         *Private,\r
+  IN     VOID                       *Config,\r
+  IN     UINT8                      *Filename,\r
+  IN     UINTN                      *BlockSize,\r
+  IN OUT UINT64                     *BufferSize\r
+  )\r
+{\r
+  if (Private->PxeBc.Mode->UsingIpv6) {\r
+    return PxeBcMtftp6GetFileSize (\r
+             Private,\r
+             (EFI_MTFTP6_CONFIG_DATA *) Config,\r
+             Filename,\r
+             BlockSize,\r
+             BufferSize\r
+             );\r
+  } else {\r
+    return PxeBcMtftp4GetFileSize (\r
+             Private,\r
+             (EFI_MTFTP4_CONFIG_DATA *) Config,\r
+             Filename,\r
+             BlockSize,\r
+             BufferSize\r
+             );\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function is a wrapper to get file using TFTP.\r
+\r
+  @param[in]      Private        Pointer to PxeBc private data.\r
+  @param[in]      Config         Pointer to config data.\r
+  @param[in]      Filename       Pointer to boot file name.\r
+  @param[in]      BlockSize      Pointer to required block size.\r
+  @param[in]      BufferPtr      Pointer to buffer.\r
+  @param[in, out] BufferSize     Pointer to buffer size.\r
+  @param[in]      DontUseBuffer  Indicates whether to use a receive buffer.\r
+\r
+  @retval EFI_SUCCESS        Sucessfully read the data from the special file.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Read data from file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpReadFile (\r
+  IN     PXEBC_PRIVATE_DATA         *Private,\r
+  IN     VOID                       *Config,\r
+  IN     UINT8                      *Filename,\r
+  IN     UINTN                      *BlockSize,\r
+  IN     UINT8                      *BufferPtr,\r
+  IN OUT UINT64                     *BufferSize,\r
+  IN     BOOLEAN                    DontUseBuffer\r
+  )\r
+{\r
+  if (Private->PxeBc.Mode->UsingIpv6) {\r
+    return PxeBcMtftp6ReadFile (\r
+             Private,\r
+             (EFI_MTFTP6_CONFIG_DATA *) Config,\r
+             Filename,\r
+             BlockSize,\r
+             BufferPtr,\r
+             BufferSize,\r
+             DontUseBuffer\r
+             );\r
+  } else {\r
+    return PxeBcMtftp4ReadFile (\r
+             Private,\r
+             (EFI_MTFTP4_CONFIG_DATA *) Config,\r
+             Filename,\r
+             BlockSize,\r
+             BufferPtr,\r
+             BufferSize,\r
+             DontUseBuffer\r
+             );\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function is a wrapper to write file using TFTP.\r
+\r
+  @param[in]       Private        Pointer to PxeBc private data.\r
+  @param[in]       Config         Pointer to config data.\r
+  @param[in]       Filename       Pointer to boot file name.\r
+  @param[in]       Overwrite      Indicate whether with overwrite attribute.\r
+  @param[in]       BlockSize      Pointer to required block size.\r
+  @param[in]       BufferPtr      Pointer to buffer.\r
+  @param[in, out]  BufferSize     Pointer to buffer size.\r
+\r
+  @retval EFI_SUCCESS        Successfully wrote the data into a special file.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval other              Write data into file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpWriteFile (\r
+  IN     PXEBC_PRIVATE_DATA         *Private,\r
+  IN     VOID                       *Config,\r
+  IN     UINT8                      *Filename,\r
+  IN     BOOLEAN                    Overwrite,\r
+  IN     UINTN                      *BlockSize,\r
+  IN     UINT8                      *BufferPtr,\r
+  IN OUT UINT64                     *BufferSize\r
+  )\r
+{\r
+  if (Private->PxeBc.Mode->UsingIpv6) {\r
+    return PxeBcMtftp6WriteFile (\r
+             Private,\r
+             (EFI_MTFTP6_CONFIG_DATA *) Config,\r
+             Filename,\r
+             Overwrite,\r
+             BlockSize,\r
+             BufferPtr,\r
+             BufferSize\r
+             );\r
+  } else {\r
+    return PxeBcMtftp4WriteFile (\r
+             Private,\r
+             (EFI_MTFTP4_CONFIG_DATA *) Config,\r
+             Filename,\r
+             Overwrite,\r
+             BlockSize,\r
+             BufferPtr,\r
+             BufferSize\r
+             );\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function is a wrapper to get the data (file) from a directory using TFTP.\r
+\r
+  @param[in]       Private        Pointer to PxeBc private data.\r
+  @param[in]       Config         Pointer to config data.\r
+  @param[in]       Filename       Pointer to boot file name.\r
+  @param[in]       BlockSize      Pointer to required block size.\r
+  @param[in]       BufferPtr      Pointer to buffer.\r
+  @param[in, out]  BufferSize     Pointer to buffer size.\r
+  @param[in]       DontUseBuffer  Indicatse whether to use a receive buffer.\r
+\r
+  @retval EFI_SUCCES         Successfully obtained the data from the file included in the directory.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Operation failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpReadDirectory (\r
+  IN     PXEBC_PRIVATE_DATA            *Private,\r
+  IN     VOID                          *Config,\r
+  IN     UINT8                         *Filename,\r
+  IN     UINTN                         *BlockSize,\r
+  IN     UINT8                         *BufferPtr,\r
+  IN OUT UINT64                        *BufferSize,\r
+  IN     BOOLEAN                       DontUseBuffer\r
+  )\r
+{\r
+  if (Private->PxeBc.Mode->UsingIpv6) {\r
+    return PxeBcMtftp6ReadDirectory (\r
+             Private,\r
+             (EFI_MTFTP6_CONFIG_DATA *) Config,\r
+             Filename,\r
+             BlockSize,\r
+             BufferPtr,\r
+             BufferSize,\r
+             DontUseBuffer\r
+             );\r
+  } else {\r
+    return PxeBcMtftp4ReadDirectory (\r
+             Private,\r
+             (EFI_MTFTP4_CONFIG_DATA *) Config,\r
+             Filename,\r
+             BlockSize,\r
+             BufferPtr,\r
+             BufferSize,\r
+             DontUseBuffer\r
+             );\r
+  }\r
+}\r
+\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h
new file mode 100644 (file)
index 0000000..1064195
--- /dev/null
@@ -0,0 +1,136 @@
+/** @file\r
+  Functions declaration related with Mtftp for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2007 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_MTFTP_H__\r
+#define __EFI_PXEBC_MTFTP_H__\r
+\r
+#define PXE_MTFTP_OPTION_BLKSIZE_INDEX     0\r
+#define PXE_MTFTP_OPTION_TIMEOUT_INDEX     1\r
+#define PXE_MTFTP_OPTION_TSIZE_INDEX       2\r
+#define PXE_MTFTP_OPTION_MULTICAST_INDEX   3\r
+#define PXE_MTFTP_OPTION_MAXIMUM_INDEX     4\r
+\r
+#define PXE_MTFTP_ERROR_STRING_LENGTH      127   // refer to definition of struct EFI_PXE_BASE_CODE_TFTP_ERROR.\r
+#define PXE_MTFTP_DEFAULT_BLOCK_SIZE       512   // refer to rfc-1350.\r
+\r
+\r
+/**\r
+  This function is wrapper to get the file size using TFTP.\r
+\r
+  @param[in]      Private        Pointer to PxeBc private data.\r
+  @param[in]      Config         Pointer to configure data.\r
+  @param[in]      Filename       Pointer to boot file name.\r
+  @param[in]      BlockSize      Pointer to required block size.\r
+  @param[in, out] BufferSize     Pointer to buffer size.\r
+\r
+  @retval EFI_SUCCESS        Successfully obtained the size of file.\r
+  @retval EFI_NOT_FOUND      Parse the tftp ptions failed.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Did not obtain the size of the file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpGetFileSize (\r
+  IN     PXEBC_PRIVATE_DATA         *Private,\r
+  IN     VOID                       *Config,\r
+  IN     UINT8                      *Filename,\r
+  IN     UINTN                      *BlockSize,\r
+  IN OUT UINT64                     *BufferSize\r
+  );\r
+\r
+\r
+/**\r
+  This function is a wrapper to get a file using TFTP.\r
+\r
+  @param[in]      Private        Pointer to PxeBc private data.\r
+  @param[in]      Config         Pointer to config data.\r
+  @param[in]      Filename       Pointer to boot file name.\r
+  @param[in]      BlockSize      Pointer to required block size.\r
+  @param[in]      BufferPtr      Pointer to buffer.\r
+  @param[in, out] BufferSize     Pointer to buffer size.\r
+  @param[in]      DontUseBuffer  Indicates whether to use a receive buffer.\r
+\r
+  @retval EFI_SUCCESS        Successfully read the data from the special file.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Read data from file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpReadFile (\r
+  IN     PXEBC_PRIVATE_DATA         *Private,\r
+  IN     VOID                       *Config,\r
+  IN     UINT8                      *Filename,\r
+  IN     UINTN                      *BlockSize,\r
+  IN     UINT8                      *BufferPtr,\r
+  IN OUT UINT64                     *BufferSize,\r
+  IN     BOOLEAN                    DontUseBuffer\r
+  );\r
+\r
+\r
+/**\r
+  This function is a wrapper to put file with TFTP.\r
+\r
+  @param[in]       Private        Pointer to PxeBc private data.\r
+  @param[in]       Config         Pointer to config data.\r
+  @param[in]       Filename       Pointer to boot file name.\r
+  @param[in]       Overwrite      Indicates whether to use an overwrite attribute.\r
+  @param[in]       BlockSize      Pointer to required block size.\r
+  @param[in]       BufferPtr      Pointer to buffer.\r
+  @param[in, out]  BufferSize     Pointer to buffer size.\r
+\r
+  @retval EFI_SUCCESS        Successfully wrote the data into the special file.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval other              Write data into file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpWriteFile (\r
+  IN     PXEBC_PRIVATE_DATA         *Private,\r
+  IN     VOID                       *Config,\r
+  IN     UINT8                      *Filename,\r
+  IN     BOOLEAN                    Overwrite,\r
+  IN     UINTN                      *BlockSize,\r
+  IN     UINT8                      *BufferPtr,\r
+  IN OUT UINT64                     *BufferSize\r
+  );\r
+\r
+\r
+/**\r
+  This function is a wrapper to get the data (file) from a directory using TFTP.\r
+\r
+  @param[in]       Private        Pointer to PxeBc private data.\r
+  @param[in]       Config         Pointer to config data.\r
+  @param[in]       Filename       Pointer to boot file name.\r
+  @param[in]       BlockSize      Pointer to required block size.\r
+  @param[in]       BufferPtr      Pointer to buffer.\r
+  @param[in, out]  BufferSize     Pointer to buffer size.\r
+  @param[in]       DontUseBuffer  Indicates whether with a receive buffer.\r
+\r
+  @retval EFI_SUCCES         Successfully obtained the data from the file included in directory.\r
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.\r
+  @retval Others             Operation failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpReadDirectory (\r
+  IN     PXEBC_PRIVATE_DATA            *Private,\r
+  IN     VOID                          *Config,\r
+  IN     UINT8                         *Filename,\r
+  IN     UINTN                         *BlockSize,\r
+  IN     UINT8                         *BufferPtr,\r
+  IN OUT UINT64                        *BufferSize,\r
+  IN     BOOLEAN                       DontUseBuffer\r
+  );\r
+#endif\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
new file mode 100644 (file)
index 0000000..7ad070c
--- /dev/null
@@ -0,0 +1,1588 @@
+/** @file\r
+  Support functions implementation for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2007 - 2010, 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
+  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
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+\r
+/**\r
+  This function returns SMBIOS string given the string number.\r
+\r
+  @param[in]  Smbios              The pointer to the SMBIOS structure\r
+  @param[in]  StringNumber        String number to return. 0 is used to skip all\r
+                                  strings and point to the next SMBIOS structure.\r
+\r
+  @return String                  The pointer to the next SMBIOS structure if\r
+                                  StringNumber == 0.\r
+\r
+**/\r
+CHAR8 *\r
+PxeBcGetSmbiosString (\r
+  IN  SMBIOS_STRUCTURE_POINTER  *Smbios,\r
+  IN  UINT16                    StringNumber\r
+  )\r
+{\r
+  UINT16                        Index;\r
+  CHAR8                         *String;\r
+\r
+  //\r
+  // Skip over formatted section.\r
+  //\r
+  String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);\r
+\r
+  //\r
+  // Look through unformated section.\r
+  //\r
+  for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) {\r
+    if (StringNumber == Index) {\r
+      return String;\r
+    }\r
+\r
+    //\r
+    // Skip zero string.\r
+    //\r
+    while (*String != 0) {\r
+      String++;\r
+    }\r
+    String++;\r
+\r
+    if (*String == 0) {\r
+      //\r
+      // If double NULL then we are done.\r
+      //  Return pointer to next structure in Smbios.\r
+      //  if you pass in a 0 you will always get here\r
+      //\r
+      Smbios->Raw = (UINT8 *)++String;\r
+      return NULL;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  This function obtains the system guid and the serial number from the smbios table.\r
+\r
+  @param[out]  SystemGuid     The pointer of the returned system guid.\r
+\r
+  @retval EFI_SUCCESS         Successfully obtained the system guid.\r
+  @retval EFI_NOT_FOUND       Did not find the SMBIOS table.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcGetSystemGuid (\r
+  OUT EFI_GUID              *SystemGuid\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  SMBIOS_TABLE_ENTRY_POINT  *SmbiosTable;\r
+  SMBIOS_STRUCTURE_POINTER  Smbios;\r
+  SMBIOS_STRUCTURE_POINTER  SmbiosEnd;\r
+  UINT16                    Index;\r
+\r
+  SmbiosTable = NULL;\r
+  Status      = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);\r
+\r
+  if (EFI_ERROR (Status) || SmbiosTable == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Smbios.Hdr    = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress;\r
+  SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength);\r
+\r
+  for (Index = 0; Index < SmbiosTable->TableLength; Index++) {\r
+    if (Smbios.Hdr->Type == 1) {\r
+      if (Smbios.Hdr->Length < 0x19) {\r
+        //\r
+        // Older version did not support Guid and Serial number\r
+        //\r
+        continue;\r
+      }\r
+      //\r
+      // SMBIOS tables are byte packed so we need to do a byte copy to\r
+      // prevend alignment faults on Itanium-based platform.\r
+      //\r
+      CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));\r
+      PxeBcGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber);\r
+\r
+      return EFI_SUCCESS;\r
+    }\r
+    //\r
+    // Make Smbios point to the next record\r
+    //\r
+    PxeBcGetSmbiosString (&Smbios, 0);\r
+\r
+    if (Smbios.Raw >= SmbiosEnd.Raw) {\r
+      //\r
+      // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e.\r
+      // given this we must double check against the length of the structure.\r
+      //\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+  Flush the previous configration using the new station Ip address.\r
+\r
+  @param[in]   Private        The pointer to the PxeBc private data.\r
+  @param[in]   StationIp      The pointer to the station Ip address.\r
+  @param[in]   SubnetMask     The pointer to the subnet mask address for v4.\r
+\r
+  @retval EFI_SUCCESS         Successfully flushed the previous configuration.\r
+  @retval Others              Failed to flush using the new station Ip.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcFlushStaionIp (\r
+  PXEBC_PRIVATE_DATA       *Private,\r
+  EFI_IP_ADDRESS           *StationIp,\r
+  EFI_IP_ADDRESS           *SubnetMask     OPTIONAL\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_MODE   *Mode;\r
+  EFI_STATUS               Status;\r
+\r
+  ASSERT (StationIp != NULL);\r
+\r
+  Mode   = Private->PxeBc.Mode;\r
+  Status = EFI_SUCCESS;\r
+\r
+  if (Mode->UsingIpv6) {\r
+\r
+    CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));\r
+    CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+    //\r
+    // Reconfigure the Ip6 instance to capture background ICMP6 packets with new station Ip address.\r
+    //\r
+    Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);\r
+    Private->Ip6->Configure (Private->Ip6, NULL);\r
+\r
+    Status = Private->Ip6->Configure (Private->Ip6, &Private->Ip6CfgData);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+  } else {\r
+    ASSERT (SubnetMask != NULL);\r
+    CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+    //\r
+    // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address.\r
+    //\r
+    Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);\r
+    Private->Ip4->Configure (Private->Ip4, NULL);\r
+\r
+    Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+  }\r
+\r
+ON_EXIT:\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Notify the callback function when an event is triggered.\r
+\r
+  @param[in]  Event           The triggered event.\r
+  @param[in]  Context         The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcCommonNotify (\r
+  IN EFI_EVENT           Event,\r
+  IN VOID                *Context\r
+  )\r
+{\r
+  *((BOOLEAN *) Context) = TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Do arp resolution from arp cache in PxeBcMode.\r
+\r
+  @param  Mode           The pointer to EFI_PXE_BASE_CODE_MODE.\r
+  @param  Ip4Addr        The Ip4 address for resolution.\r
+  @param  MacAddress     The resoluted MAC address if the resolution is successful.\r
+                         The value is undefined if the resolution fails.\r
+\r
+  @retval TRUE           Found an matched entry.\r
+  @retval FALSE          Did not find a matched entry.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckArpCache (\r
+  IN  EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN  EFI_IPv4_ADDRESS          *Ip4Addr,\r
+  OUT EFI_MAC_ADDRESS           *MacAddress\r
+  )\r
+{\r
+  UINT32       Index;\r
+\r
+  ASSERT (!Mode->UsingIpv6);\r
+\r
+  //\r
+  // Check whether the current Arp cache in mode data contains this information or not.\r
+  //\r
+  for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {\r
+    if (EFI_IP4_EQUAL (&Mode->ArpCache[Index].IpAddr.v4, Ip4Addr)) {\r
+      CopyMem (\r
+        MacAddress,\r
+        &Mode->ArpCache[Index].MacAddr,\r
+        sizeof (EFI_MAC_ADDRESS)\r
+        );\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  Update the arp cache periodically.\r
+\r
+  @param  Event              The pointer to EFI_PXE_BC_PROTOCOL.\r
+  @param  Context            Context of the timer event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcArpCacheUpdate (\r
+  IN EFI_EVENT            Event,\r
+  IN VOID                 *Context\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA      *Private;\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+  EFI_ARP_FIND_DATA       *ArpEntry;\r
+  UINT32                  EntryLength;\r
+  UINT32                  EntryCount;\r
+  UINT32                  Index;\r
+  EFI_STATUS              Status;\r
+\r
+  Private = (PXEBC_PRIVATE_DATA *) Context;\r
+  Mode    = Private->PxeBc.Mode;\r
+\r
+  ASSERT (!Mode->UsingIpv6);\r
+\r
+  //\r
+  // Get the current Arp cache from Arp driver.\r
+  //\r
+  Status = Private->Arp->Find (\r
+                           Private->Arp,\r
+                           TRUE,\r
+                           NULL,\r
+                           &EntryLength,\r
+                           &EntryCount,\r
+                           &ArpEntry,\r
+                           TRUE\r
+                           );\r
+  if (EFI_ERROR (Status)) {\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Update the Arp cache in mode data.\r
+  //\r
+  Mode->ArpCacheEntries = MIN (EntryCount, EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES);\r
+\r
+  for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {\r
+    CopyMem (\r
+      &Mode->ArpCache[Index].IpAddr,\r
+      ArpEntry + 1,\r
+      ArpEntry->SwAddressLength\r
+      );\r
+    CopyMem (\r
+      &Mode->ArpCache[Index].MacAddr,\r
+      (UINT8 *) (ArpEntry + 1) + ArpEntry->SwAddressLength,\r
+      ArpEntry->HwAddressLength\r
+      );\r
+    ArpEntry = (EFI_ARP_FIND_DATA *) ((UINT8 *) ArpEntry + EntryLength);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Notify function to handle the received ICMP message in DPC.\r
+\r
+  @param  Context               The PXEBC private data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmpErrorDpcHandle (\r
+  IN VOID                      *Context\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_IP4_RECEIVE_DATA         *RxData;\r
+  EFI_IP4_PROTOCOL             *Ip4;\r
+  PXEBC_PRIVATE_DATA           *Private;\r
+  EFI_PXE_BASE_CODE_MODE       *Mode;\r
+  UINT8                        Type;\r
+  UINTN                        Index;\r
+  UINT32                       CopiedLen;\r
+  UINT8                        *IcmpError;\r
+\r
+  Private = (PXEBC_PRIVATE_DATA *) Context;\r
+  Mode    = &Private->Mode;\r
+  Status  = Private->IcmpToken.Status;\r
+  RxData  = Private->IcmpToken.Packet.RxData;\r
+  Ip4     = Private->Ip4;\r
+\r
+  ASSERT (!Mode->UsingIpv6);\r
+\r
+  if (Status == EFI_ABORTED) {\r
+    //\r
+    // It's triggered by user cancellation.\r
+    //\r
+    return;\r
+  }\r
+\r
+  if (RxData == NULL) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (Status != EFI_ICMP_ERROR) {\r
+    //\r
+    // The return status should be recognized as EFI_ICMP_ERROR.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (EFI_IP4 (RxData->Header->SourceAddress) != 0 &&\r
+      !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) {\r
+    //\r
+    // The source address of the received packet should be a valid unicast address.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) {\r
+    //\r
+    // The destination address of the received packet should be equal to the host address.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) {\r
+    //\r
+    // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);\r
+\r
+  if (Type != ICMP_DEST_UNREACHABLE &&\r
+      Type != ICMP_SOURCE_QUENCH &&\r
+      Type != ICMP_REDIRECT &&\r
+      Type != ICMP_TIME_EXCEEDED &&\r
+      Type != ICMP_PARAMETER_PROBLEM) {\r
+    //\r
+    // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Copy the right ICMP error message into mode data.\r
+  //\r
+  CopiedLen = 0;\r
+  IcmpError = (UINT8 *) &Mode->IcmpError;\r
+\r
+  for (Index = 0; Index < RxData->FragmentCount; Index++) {\r
+    CopiedLen += RxData->FragmentTable[Index].FragmentLength;\r
+    if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {\r
+      CopyMem (\r
+        IcmpError,\r
+        RxData->FragmentTable[Index].FragmentBuffer,\r
+        RxData->FragmentTable[Index].FragmentLength\r
+        );\r
+    } else {\r
+      CopyMem (\r
+        IcmpError,\r
+        RxData->FragmentTable[Index].FragmentBuffer,\r
+        CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)\r
+        );\r
+    }\r
+    IcmpError += CopiedLen;\r
+  }\r
+\r
+ON_EXIT:\r
+  Private->IcmpToken.Status = EFI_NOT_READY;\r
+  Ip4->Receive (Ip4, &Private->IcmpToken);\r
+}\r
+\r
+\r
+/**\r
+  Callback function to update the latest ICMP6 error message.\r
+\r
+  @param  Event                 The event signalled.\r
+  @param  Context               The context passed in using the event notifier.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmpErrorUpdate (\r
+  IN EFI_EVENT            Event,\r
+  IN VOID                 *Context\r
+  )\r
+{\r
+  QueueDpc (TPL_CALLBACK, PxeBcIcmpErrorDpcHandle, Context);\r
+}\r
+\r
+\r
+/**\r
+  Notify function to handle the received ICMP6 message in DPC.\r
+\r
+  @param  Context               The PXEBC private data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmp6ErrorDpcHandle (\r
+  IN VOID                 *Context\r
+  )\r
+{\r
+  PXEBC_PRIVATE_DATA      *Private;\r
+  EFI_IP6_RECEIVE_DATA    *RxData;\r
+  EFI_IP6_PROTOCOL        *Ip6;\r
+  EFI_PXE_BASE_CODE_MODE  *Mode;\r
+  EFI_STATUS              Status;\r
+  UINTN                   Index;\r
+  UINT8                   Type;\r
+  UINT32                  CopiedLen;\r
+  UINT8                   *Icmp6Error;\r
+\r
+  Private = (PXEBC_PRIVATE_DATA *) Context;\r
+  Mode    = &Private->Mode;\r
+  Status  = Private->Icmp6Token.Status;\r
+  RxData  = Private->Icmp6Token.Packet.RxData;\r
+  Ip6     = Private->Ip6;\r
+\r
+  ASSERT (Mode->UsingIpv6);\r
+\r
+  if (Status == EFI_ABORTED) {\r
+    //\r
+    // It's triggered by user cancellation.\r
+    //\r
+    return;\r
+  }\r
+\r
+  if (RxData == NULL) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (Status != EFI_ICMP_ERROR) {\r
+    //\r
+    // The return status should be recognized as EFI_ICMP_ERROR.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) {\r
+    //\r
+    // The source address of the received packet should be a valid unicast address.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) &&\r
+      !EFI_IP6_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v6)) {\r
+    //\r
+    // The destination address of the received packet should be equal to the host address.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (RxData->Header->NextHeader != IP6_ICMP) {\r
+    //\r
+    // The nextheader in the header of the receveid packet should be IP6_ICMP.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);\r
+\r
+  if (Type != ICMP_V6_DEST_UNREACHABLE &&\r
+      Type != ICMP_V6_PACKET_TOO_BIG &&\r
+      Type != ICMP_V6_PACKET_TOO_BIG &&\r
+      Type != ICMP_V6_PARAMETER_PROBLEM) {\r
+    //\r
+    // The type of the receveid packet should be an ICMP6 error message.\r
+    //\r
+    gBS->SignalEvent (RxData->RecycleSignal);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Copy the right ICMP6 error message into mode data.\r
+  //\r
+  CopiedLen  = 0;\r
+  Icmp6Error = (UINT8 *) &Mode->IcmpError;\r
+\r
+  for (Index = 0; Index < RxData->FragmentCount; Index++) {\r
+    CopiedLen += RxData->FragmentTable[Index].FragmentLength;\r
+    if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {\r
+      CopyMem (\r
+        Icmp6Error,\r
+        RxData->FragmentTable[Index].FragmentBuffer,\r
+        RxData->FragmentTable[Index].FragmentLength\r
+        );\r
+    } else {\r
+      CopyMem (\r
+        Icmp6Error,\r
+        RxData->FragmentTable[Index].FragmentBuffer,\r
+        CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)\r
+        );\r
+    }\r
+    Icmp6Error += CopiedLen;\r
+  }\r
+\r
+ON_EXIT:\r
+  Private->Icmp6Token.Status = EFI_NOT_READY;\r
+  Ip6->Receive (Ip6, &Private->Icmp6Token);\r
+}\r
+\r
+\r
+/**\r
+  Callback function to update the latest ICMP6 error message.\r
+\r
+  @param  Event                 The event signalled.\r
+  @param  Context               The context passed in using the event notifier.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmp6ErrorUpdate (\r
+  IN EFI_EVENT               Event,\r
+  IN VOID                    *Context\r
+  )\r
+{\r
+  QueueDpc (TPL_CALLBACK, PxeBcIcmp6ErrorDpcHandle, Context);\r
+}\r
+\r
+\r
+/**\r
+  This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+  @param[in]       Udp4                 The pointer to EFI_UDP4_PROTOCOL.\r
+  @param[in]       StationIp            The pointer to the station address.\r
+  @param[in]       SubnetMask           The pointer to the subnet mask.\r
+  @param[in]       Gateway              The pointer to the gateway address.\r
+  @param[in, out]  SrcPort              The pointer to the source port.\r
+  @param[in]       DoNotFragment        If TRUE, fragment is not enabled.\r
+                                        Otherwise, fragment is enabled.\r
+\r
+  @retval          EFI_SUCCESS          Successfully configured this instance.\r
+  @retval          Others               Failed to configure this instance.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcConfigUdp4Write (\r
+  IN     EFI_UDP4_PROTOCOL  *Udp4,\r
+  IN     EFI_IPv4_ADDRESS   *StationIp,\r
+  IN     EFI_IPv4_ADDRESS   *SubnetMask,\r
+  IN     EFI_IPv4_ADDRESS   *Gateway,\r
+  IN OUT UINT16             *SrcPort,\r
+  IN     BOOLEAN            DoNotFragment\r
+  )\r
+{\r
+  EFI_UDP4_CONFIG_DATA  Udp4CfgData;\r
+  EFI_STATUS            Status;\r
+\r
+  ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData));\r
+\r
+  Udp4CfgData.TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;\r
+  Udp4CfgData.ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;\r
+  Udp4CfgData.TypeOfService      = DEFAULT_ToS;\r
+  Udp4CfgData.TimeToLive         = DEFAULT_TTL;\r
+  Udp4CfgData.AllowDuplicatePort = TRUE;\r
+  Udp4CfgData.DoNotFragment      = DoNotFragment;\r
+\r
+  CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp));\r
+  CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask));\r
+\r
+  Udp4CfgData.StationPort = *SrcPort;\r
+\r
+  //\r
+  // Reset the UDPv4 instance.\r
+  //\r
+  Udp4->Configure (Udp4, NULL);\r
+\r
+  Status = Udp4->Configure (Udp4, &Udp4CfgData);\r
+  if (!EFI_ERROR (Status) && !EFI_IP4_EQUAL (Gateway, &mZeroIp4Addr)) {\r
+    //\r
+    // The basic configuration is OK, need to add the default route entry\r
+    //\r
+    Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway);\r
+    if (EFI_ERROR (Status)) {\r
+      Udp4->Configure (Udp4, NULL);\r
+    }\r
+  }\r
+\r
+  if (!EFI_ERROR (Status) && *SrcPort == 0) {\r
+    Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL);\r
+    *SrcPort = Udp4CfgData.StationPort;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to configure a UDPv6 instance for UdpWrite.\r
+\r
+  @param[in]       Udp6                 The pointer to EFI_UDP6_PROTOCOL.\r
+  @param[in]       StationIp            The pointer to the station address.\r
+  @param[in, out]  SrcPort              The pointer to the source port.\r
+\r
+  @retval          EFI_SUCCESS          Successfully configured this instance.\r
+  @retval          Others               Failed to configure this instance.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcConfigUdp6Write (\r
+  IN     EFI_UDP6_PROTOCOL  *Udp6,\r
+  IN     EFI_IPv6_ADDRESS   *StationIp,\r
+  IN OUT UINT16             *SrcPort\r
+  )\r
+{\r
+  EFI_UDP6_CONFIG_DATA  CfgData;\r
+  EFI_STATUS            Status;\r
+\r
+  ZeroMem (&CfgData, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+  CfgData.ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;\r
+  CfgData.TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;\r
+  CfgData.HopLimit           = PXEBC_DEFAULT_HOPLIMIT;\r
+  CfgData.AllowDuplicatePort = TRUE;\r
+  CfgData.StationPort        = *SrcPort;\r
+\r
+  CopyMem (&CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  //\r
+  // Reset the UDPv6 instance.\r
+  //\r
+  Udp6->Configure (Udp6, NULL);\r
+\r
+  Status = Udp6->Configure (Udp6, &CfgData);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (!EFI_ERROR (Status) && *SrcPort == 0) {\r
+    Udp6->GetModeData (Udp6, &CfgData, NULL, NULL, NULL);\r
+    *SrcPort = CfgData.StationPort;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+  @param[in]       Udp4                 The pointer to EFI_UDP4_PROTOCOL.\r
+  @param[in]       Session              The pointer to the UDP4 session data.\r
+  @param[in]       TimeoutEvent         The event for timeout.\r
+  @param[in]       Gateway              The pointer to the gateway address.\r
+  @param[in]       HeaderSize           An optional field which may be set to the length of a header\r
+                                        at HeaderPtr to be prefixed to the data at BufferPtr.\r
+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be\r
+                                        prefixed to the data at BufferPtr.\r
+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.\r
+  @param[in]       BufferPtr            A pointer to the data to be written.\r
+\r
+  @retval          EFI_SUCCESS          Successfully send out data using Udp4Write.\r
+  @retval          Others               Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp4Write (\r
+  IN EFI_UDP4_PROTOCOL       *Udp4,\r
+  IN EFI_UDP4_SESSION_DATA   *Session,\r
+  IN EFI_EVENT               TimeoutEvent,\r
+  IN EFI_IPv4_ADDRESS        *Gateway      OPTIONAL,\r
+  IN UINTN                   *HeaderSize   OPTIONAL,\r
+  IN VOID                    *HeaderPtr    OPTIONAL,\r
+  IN UINTN                   *BufferSize,\r
+  IN VOID                    *BufferPtr\r
+  )\r
+{\r
+  EFI_UDP4_COMPLETION_TOKEN Token;\r
+  EFI_UDP4_TRANSMIT_DATA    *TxData;\r
+  UINT32                    TxLength;\r
+  UINT32                    FragCount;\r
+  UINT32                    DataLength;\r
+  BOOLEAN                   IsDone;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // Arrange one fragment buffer for data, and another fragment buffer for header if has.\r
+  //\r
+  FragCount = (HeaderSize != NULL) ? 2 : 1;\r
+  TxLength  = sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA);\r
+  TxData    = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (TxLength);\r
+  if (TxData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TxData->FragmentCount                               = FragCount;\r
+  TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;\r
+  TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;\r
+  DataLength                                          = (UINT32) *BufferSize;\r
+\r
+  if (HeaderSize != NULL) {\r
+    TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;\r
+    TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;\r
+    DataLength                             += (UINT32) *HeaderSize;\r
+  }\r
+\r
+  if (Gateway != NULL) {\r
+    TxData->GatewayAddress  = Gateway;\r
+  }\r
+\r
+  TxData->UdpSessionData  = Session;\r
+  TxData->DataLength      = DataLength;\r
+  Token.Packet.TxData     = TxData;\r
+  Token.Status            = EFI_NOT_READY;\r
+  IsDone                  = FALSE;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  PxeBcCommonNotify,\r
+                  &IsDone,\r
+                  &Token.Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Udp4->Transmit (Udp4, &Token);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.\r
+  //\r
+  while (!IsDone &&\r
+         Token.Status == EFI_NOT_READY &&\r
+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+    Udp4->Poll (Udp4);\r
+  }\r
+\r
+  Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;\r
+\r
+ON_EXIT:\r
+  if (Token.Event != NULL) {\r
+    gBS->CloseEvent (Token.Event);\r
+  }\r
+  FreePool (TxData);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+  @param[in]       Udp6                 The pointer to EFI_UDP6_PROTOCOL.\r
+  @param[in]       Session              The pointer to the UDP6 session data.\r
+  @param[in]       TimeoutEvent         The event for timeout.\r
+  @param[in]       HeaderSize           An optional field which may be set to the length of a header\r
+                                        at HeaderPtr to be prefixed to the data at BufferPtr.\r
+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be\r
+                                        prefixed to the data at BufferPtr.\r
+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.\r
+  @param[in]       BufferPtr            A pointer to the data to be written.\r
+\r
+  @retval          EFI_SUCCESS          Successfully sent out data using Udp6Write.\r
+  @retval          Others               Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp6Write (\r
+  IN EFI_UDP6_PROTOCOL       *Udp6,\r
+  IN EFI_UDP6_SESSION_DATA   *Session,\r
+  IN EFI_EVENT               TimeoutEvent,\r
+  IN UINTN                   *HeaderSize   OPTIONAL,\r
+  IN VOID                    *HeaderPtr    OPTIONAL,\r
+  IN UINTN                   *BufferSize,\r
+  IN VOID                    *BufferPtr\r
+  )\r
+{\r
+  EFI_UDP6_COMPLETION_TOKEN Token;\r
+  EFI_UDP6_TRANSMIT_DATA    *TxData;\r
+  UINT32                    TxLength;\r
+  UINT32                    FragCount;\r
+  UINT32                    DataLength;\r
+  BOOLEAN                   IsDone;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // Arrange one fragment buffer for data, and another fragment buffer for header if has.\r
+  //\r
+  FragCount = (HeaderSize != NULL) ? 2 : 1;\r
+  TxLength  = sizeof (EFI_UDP6_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA);\r
+  TxData    = (EFI_UDP6_TRANSMIT_DATA *) AllocateZeroPool (TxLength);\r
+  if (TxData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TxData->FragmentCount                               = FragCount;\r
+  TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;\r
+  TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;\r
+  DataLength                                          = (UINT32) *BufferSize;\r
+\r
+  if (HeaderSize != NULL) {\r
+    TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;\r
+    TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;\r
+    DataLength                             += (UINT32) *HeaderSize;\r
+  }\r
+\r
+  TxData->UdpSessionData  = Session;\r
+  TxData->DataLength      = DataLength;\r
+  Token.Packet.TxData     = TxData;\r
+  Token.Status            = EFI_NOT_READY;\r
+  IsDone                  = FALSE;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  PxeBcCommonNotify,\r
+                  &IsDone,\r
+                  &Token.Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Udp6->Transmit (Udp6, &Token);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.\r
+  //\r
+  while (!IsDone &&\r
+         Token.Status == EFI_NOT_READY &&\r
+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+    Udp6->Poll (Udp6);\r
+  }\r
+\r
+  Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;\r
+\r
+ON_EXIT:\r
+  if (Token.Event != NULL) {\r
+    gBS->CloseEvent (Token.Event);\r
+  }\r
+  FreePool (TxData);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Check the received packet using the Ip filter.\r
+\r
+  @param[in]  Mode                The pointer to the mode data of PxeBc.\r
+  @param[in]  Session             The pointer to the current UDPv4 session.\r
+  @param[in]  OpFlags             Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Passed the Ip filter successfully.\r
+  @retval     FALSE               Failed to pass the Ip filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByIpFilter (\r
+  IN EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN VOID                      *Session,\r
+  IN UINT16                    OpFlags\r
+  )\r
+{\r
+  EFI_IP_ADDRESS               DestinationIp;\r
+  UINTN                        Index;\r
+\r
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) == 0) {\r
+    return TRUE;\r
+  }\r
+\r
+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) {\r
+    return TRUE;\r
+  }\r
+\r
+  //\r
+  // Convert the destination address in session data to host order.\r
+  //\r
+  if (Mode->UsingIpv6) {\r
+    CopyMem (\r
+      &DestinationIp,\r
+      &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress,\r
+      sizeof (EFI_IPv6_ADDRESS)\r
+      );\r
+    NTOHLLL (&DestinationIp.v6);\r
+  } else {\r
+    ZeroMem (&DestinationIp, sizeof (EFI_IP_ADDRESS));\r
+    CopyMem (\r
+      &DestinationIp,\r
+      &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress,\r
+      sizeof (EFI_IPv4_ADDRESS)\r
+      );\r
+    EFI_NTOHL (DestinationIp);\r
+  }\r
+\r
+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0 &&\r
+      (IP4_IS_MULTICAST (DestinationIp.Addr[0]) ||\r
+       IP6_IS_MULTICAST (&DestinationIp))) {\r
+    return TRUE;\r
+  }\r
+\r
+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0 &&\r
+      IP4_IS_LOCAL_BROADCAST (DestinationIp.Addr[0])) {\r
+    ASSERT (!Mode->UsingIpv6);\r
+    return TRUE;\r
+  }\r
+\r
+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&\r
+      (EFI_IP4_EQUAL (&Mode->StationIp.v4, &DestinationIp) ||\r
+       EFI_IP6_EQUAL (&Mode->StationIp.v6, &DestinationIp))) {\r
+    //\r
+    // Matched if the dest address is equal to the station address.\r
+    //\r
+    return TRUE;\r
+  }\r
+\r
+  for (Index = 0; Index < Mode->IpFilter.IpCnt; Index++) {\r
+    ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);\r
+    if (EFI_IP4_EQUAL (&Mode->IpFilter.IpList[Index].v4, &DestinationIp) ||\r
+        EFI_IP6_EQUAL (&Mode->IpFilter.IpList[Index].v6, &DestinationIp)) {\r
+      //\r
+      // Matched if the dest address is equal to any of address in the filter list.\r
+      //\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  Filter the received packet using the destination Ip.\r
+\r
+  @param[in]       Mode           The pointer to the mode data of PxeBc.\r
+  @param[in]       Session        The pointer to the current UDPv4 session.\r
+  @param[in, out]  DestIp         The pointer to the destination Ip address.\r
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Passed the IPv4 filter successfully.\r
+  @retval     FALSE               Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByDestIp (\r
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN     VOID                      *Session,\r
+  IN OUT EFI_IP_ADDRESS            *DestIp,\r
+  IN     UINT16                    OpFlags\r
+  )\r
+{\r
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) {\r
+    //\r
+    // Copy the destination address from the received packet if accept any.\r
+    //\r
+    if (DestIp != NULL) {\r
+      if (Mode->UsingIpv6) {\r
+        CopyMem (\r
+          DestIp,\r
+          &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress,\r
+          sizeof (EFI_IPv6_ADDRESS)\r
+          );\r
+      } else {\r
+        ZeroMem (DestIp, sizeof (EFI_IP_ADDRESS));\r
+        CopyMem (\r
+          DestIp,\r
+          &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress,\r
+          sizeof (EFI_IPv4_ADDRESS)\r
+          );\r
+      }\r
+\r
+    }\r
+    return TRUE;\r
+  } else if (DestIp != NULL &&\r
+             (EFI_IP4_EQUAL (DestIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||\r
+              EFI_IP6_EQUAL (DestIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress))) {\r
+    //\r
+    // The destination address in the received packet is matched if present.\r
+    //\r
+    return TRUE;\r
+  } else if (EFI_IP4_EQUAL (&Mode->StationIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||\r
+             EFI_IP6_EQUAL (&Mode->StationIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress)) {\r
+    //\r
+    // The destination address in the received packet is equal to the host address.\r
+    //\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  Check the received packet using the destination port.\r
+\r
+  @param[in]       PxeBcMode      The pointer to the mode data of PxeBc.\r
+  @param[in]       Session        The pointer to the current UDPv4 session.\r
+  @param[in, out]  DestPort       The pointer to the destination port.\r
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Passed the IPv4 filter successfully.\r
+  @retval     FALSE               Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByDestPort (\r
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN     VOID                      *Session,\r
+  IN OUT UINT16                    *DestPort,\r
+  IN     UINT16                    OpFlags\r
+  )\r
+{\r
+  UINT16       Port;\r
+\r
+  if (Mode->UsingIpv6) {\r
+    Port = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort;\r
+  } else {\r
+    Port = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort;\r
+  }\r
+\r
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) {\r
+    //\r
+    // Return the destination port in the received packet if accept any.\r
+    //\r
+    if (DestPort != NULL) {\r
+      *DestPort = Port;\r
+    }\r
+    return TRUE;\r
+  } else if (DestPort != NULL && *DestPort == Port) {\r
+    //\r
+    // The destination port in the received packet is matched if present.\r
+    //\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  Filter the received packet using the source Ip.\r
+\r
+  @param[in]       Mode           The pointer to the mode data of PxeBc.\r
+  @param[in]       Session        The pointer to the current UDPv4 session.\r
+  @param[in, out]  SrcIp          The pointer to the source Ip address.\r
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Passed the IPv4 filter successfully.\r
+  @retval     FALSE               Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcFilterBySrcIp (\r
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN     VOID                      *Session,\r
+  IN OUT EFI_IP_ADDRESS            *SrcIp,\r
+  IN     UINT16                    OpFlags\r
+  )\r
+{\r
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) {\r
+    //\r
+    // Copy the source address from the received packet if accept any.\r
+    //\r
+    if (SrcIp != NULL) {\r
+      if (Mode->UsingIpv6) {\r
+        CopyMem (\r
+          SrcIp,\r
+          &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress,\r
+          sizeof (EFI_IPv6_ADDRESS)\r
+          );\r
+      } else {\r
+        ZeroMem (SrcIp, sizeof (EFI_IP_ADDRESS));\r
+        CopyMem (\r
+          SrcIp,\r
+          &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress,\r
+          sizeof (EFI_IPv4_ADDRESS)\r
+          );\r
+      }\r
+\r
+    }\r
+    return TRUE;\r
+  } else if (SrcIp != NULL &&\r
+             (EFI_IP4_EQUAL (SrcIp, &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress) ||\r
+              EFI_IP6_EQUAL (SrcIp, &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress))) {\r
+    //\r
+    // The source address in the received packet is matched if present.\r
+    //\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  Filter the received packet using the source port.\r
+\r
+  @param[in]       Mode           The pointer to the mode data of PxeBc.\r
+  @param[in]       Session        The pointer to the current UDPv4 session.\r
+  @param[in, out]  SrcPort        The pointer to the source port.\r
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Passed the IPv4 filter successfully.\r
+  @retval     FALSE               Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcFilterBySrcPort (\r
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN     VOID                      *Session,\r
+  IN OUT UINT16                    *SrcPort,\r
+  IN     UINT16                    OpFlags\r
+  )\r
+{\r
+  UINT16       Port;\r
+\r
+  if (Mode->UsingIpv6) {\r
+    Port = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort;\r
+  } else {\r
+    Port = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort;\r
+  }\r
+\r
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) {\r
+    //\r
+    // Return the source port in the received packet if accept any.\r
+    //\r
+    if (SrcPort != NULL) {\r
+      *SrcPort = Port;\r
+    }\r
+    return TRUE;\r
+  } else if (SrcPort != NULL && *SrcPort == Port) {\r
+    //\r
+    // The source port in the received packet is matched if present.\r
+    //\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  This function is to receive packet using Udp4Read.\r
+\r
+  @param[in]       Udp4                 The pointer to EFI_UDP4_PROTOCOL.\r
+  @param[in]       Token                The pointer to EFI_UDP4_COMPLETION_TOKEN.\r
+  @param[in]       Mode                 The pointer to EFI_PXE_BASE_CODE_MODE.\r
+  @param[in]       TimeoutEvent         The event for timeout.\r
+  @param[in]       OpFlags              The UDP operation flags.\r
+  @param[in]       IsDone               The pointer to the IsDone flag.\r
+  @param[out]      IsMatched            The pointer to the IsMatched flag.\r
+  @param[in, out]  DestIp               The pointer to the destination address.\r
+  @param[in, out]  DestPort             The pointer to the destination port.\r
+  @param[in, out]  SrcIp                The pointer to the source address.\r
+  @param[in, out]  SrcPort              The pointer to the source port.\r
+\r
+  @retval          EFI_SUCCESS          Successfully read the data using Udp4.\r
+  @retval          Others               Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp4Read (\r
+  IN     EFI_UDP4_PROTOCOL            *Udp4,\r
+  IN     EFI_UDP4_COMPLETION_TOKEN    *Token,\r
+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,\r
+  IN     EFI_EVENT                    TimeoutEvent,\r
+  IN     UINT16                       OpFlags,\r
+  IN     BOOLEAN                      *IsDone,\r
+     OUT BOOLEAN                      *IsMatched,\r
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,\r
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL\r
+  )\r
+{\r
+  EFI_UDP4_RECEIVE_DATA     *RxData;\r
+  EFI_UDP4_SESSION_DATA     *Session;\r
+  EFI_STATUS                Status;\r
+\r
+  Token->Status = EFI_NOT_READY;\r
+  *IsDone       = FALSE;\r
+\r
+  Status = Udp4->Receive (Udp4, Token);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.\r
+  //\r
+  while (!(*IsDone) &&\r
+         Token->Status == EFI_NOT_READY &&\r
+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+    //\r
+    // Poll the token utill reply/ICMPv6 error message received or timeout.\r
+    //\r
+    Udp4->Poll (Udp4);\r
+    if (Token->Status == EFI_ICMP_ERROR ||\r
+        Token->Status == EFI_NETWORK_UNREACHABLE ||\r
+        Token->Status == EFI_HOST_UNREACHABLE ||\r
+        Token->Status == EFI_PROTOCOL_UNREACHABLE ||\r
+        Token->Status == EFI_PORT_UNREACHABLE) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // check whether this packet matches the filters\r
+    //\r
+    RxData    = Token->Packet.RxData;\r
+    Session   = &RxData->UdpSession;\r
+\r
+    *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);\r
+\r
+    if (*IsMatched) {\r
+      *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);\r
+    }\r
+\r
+    if (*IsMatched) {\r
+      *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);\r
+    }\r
+\r
+    if (*IsMatched) {\r
+      *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);\r
+    }\r
+\r
+    if (*IsMatched) {\r
+      *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);\r
+    }\r
+\r
+    if (!(*IsMatched)) {\r
+      //\r
+      // Recycle the receiving buffer if not matched.\r
+      //\r
+      gBS->SignalEvent (RxData->RecycleSignal);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to receive packets using Udp6Read.\r
+\r
+  @param[in]       Udp6                 The pointer to EFI_UDP6_PROTOCOL.\r
+  @param[in]       Token                The pointer to EFI_UDP6_COMPLETION_TOKEN.\r
+  @param[in]       Mode                 The pointer to EFI_PXE_BASE_CODE_MODE.\r
+  @param[in]       TimeoutEvent         The event for timeout.\r
+  @param[in]       OpFlags              The UDP operation flags.\r
+  @param[in]       IsDone               The pointer to the IsDone flag.\r
+  @param[out]      IsMatched            The pointer to the IsMatched flag.\r
+  @param[in, out]  DestIp               The pointer to the destination address.\r
+  @param[in, out]  DestPort             The pointer to the destination port.\r
+  @param[in, out]  SrcIp                The pointer to the source address.\r
+  @param[in, out]  SrcPort              The pointer to the source port.\r
+\r
+  @retval          EFI_SUCCESS          Successfully read data using Udp6.\r
+  @retval          Others               Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp6Read (\r
+  IN     EFI_UDP6_PROTOCOL            *Udp6,\r
+  IN     EFI_UDP6_COMPLETION_TOKEN    *Token,\r
+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,\r
+  IN     EFI_EVENT                    TimeoutEvent,\r
+  IN     UINT16                       OpFlags,\r
+  IN     BOOLEAN                      *IsDone,\r
+     OUT BOOLEAN                      *IsMatched,\r
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,\r
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL\r
+  )\r
+{\r
+  EFI_UDP6_RECEIVE_DATA     *RxData;\r
+  EFI_UDP6_SESSION_DATA     *Session;\r
+  EFI_STATUS                Status;\r
+\r
+  Token->Status = EFI_NOT_READY;\r
+  *IsDone       = FALSE;\r
+\r
+  Status = Udp6->Receive (Udp6, Token);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.\r
+  //\r
+  while (!(*IsDone) &&\r
+         Token->Status == EFI_NOT_READY &&\r
+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+    //\r
+    // Poll the token utill reply/ICMPv6 error message received or timeout.\r
+    //\r
+    Udp6->Poll (Udp6);\r
+    if (Token->Status == EFI_ICMP_ERROR ||\r
+        Token->Status == EFI_NETWORK_UNREACHABLE ||\r
+        Token->Status == EFI_HOST_UNREACHABLE ||\r
+        Token->Status == EFI_PROTOCOL_UNREACHABLE ||\r
+        Token->Status == EFI_PORT_UNREACHABLE) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // check whether this packet matches the filters\r
+    //\r
+    RxData    = Token->Packet.RxData;\r
+    Session   = &RxData->UdpSession;\r
+\r
+    *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);\r
+\r
+    if (*IsMatched) {\r
+      *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);\r
+    }\r
+\r
+    if (*IsMatched) {\r
+      *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);\r
+    }\r
+\r
+    if (*IsMatched) {\r
+      *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);\r
+    }\r
+\r
+    if (*IsMatched) {\r
+      *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);\r
+    }\r
+\r
+    if (!(*IsMatched)) {\r
+      //\r
+      // Recycle the receiving buffer if not matched.\r
+      //\r
+      gBS->SignalEvent (RxData->RecycleSignal);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is to display the IPv4 address.\r
+\r
+  @param[in]  Ip        The pointer to the IPv4 address.\r
+\r
+**/\r
+VOID\r
+PxeBcShowIp4Addr (\r
+  IN EFI_IPv4_ADDRESS   *Ip\r
+  )\r
+{\r
+  UINTN                 Index;\r
+\r
+  for (Index = 0; Index < 4; Index++) {\r
+    AsciiPrint ("%d", Ip->Addr[Index]);\r
+    if (Index < 3) {\r
+      AsciiPrint (".");\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function is to display the IPv6 address.\r
+\r
+  @param[in]  Ip        The pointer to the IPv6 address.\r
+\r
+**/\r
+VOID\r
+PxeBcShowIp6Addr (\r
+  IN EFI_IPv6_ADDRESS   *Ip\r
+  )\r
+{\r
+  UINTN                 Index;\r
+\r
+  for (Index = 0; Index < 16; Index++) {\r
+\r
+    if (Ip->Addr[Index] != 0) {\r
+      AsciiPrint ("%x", Ip->Addr[Index]);\r
+    }\r
+    Index++;\r
+    if (Index > 15) {\r
+      return;\r
+    }\r
+    if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) {\r
+      AsciiPrint ("0");\r
+    }\r
+    AsciiPrint ("%x", Ip->Addr[Index]);\r
+    if (Index < 15) {\r
+      AsciiPrint (":");\r
+    }\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function is to convert UINTN to ASCII string with the required formatting.\r
+\r
+  @param[in]  Number         Numeric value to be converted.\r
+  @param[in]  Buffer         The pointer to the buffer for ASCII string.\r
+  @param[in]  Length         The length of the required format.\r
+\r
+**/\r
+VOID\r
+PxeBcUintnToAscDecWithFormat (\r
+  IN UINTN                       Number,\r
+  IN UINT8                       *Buffer,\r
+  IN INTN                        Length\r
+  )\r
+{\r
+  UINTN                          Remainder;\r
+\r
+  while (Length > 0) {\r
+    Length--;\r
+    Remainder      = Number % 10;\r
+    Number        /= 10;\r
+    Buffer[Length] = (UINT8) ('0' + Remainder);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function is to convert a UINTN to a ASCII string, and return the\r
+  actual length of the buffer.\r
+\r
+  @param[in]  Number         Numeric value to be converted.\r
+  @param[in]  Buffer         The pointer to the buffer for ASCII string.\r
+\r
+  @return     Length         The actual length of the ASCII string.\r
+\r
+**/\r
+UINTN\r
+PxeBcUintnToAscDec (\r
+  IN UINTN               Number,\r
+  IN UINT8               *Buffer\r
+  )\r
+{\r
+  UINTN           Index;\r
+  UINTN           Length;\r
+  CHAR8           TempStr[64];\r
+\r
+  Index           = 63;\r
+  TempStr[Index]  = 0;\r
+\r
+  do {\r
+    Index--;\r
+    TempStr[Index] = (CHAR8) ('0' + (Number % 10));\r
+    Number         = (UINTN) (Number / 10);\r
+  } while (Number != 0);\r
+\r
+  AsciiStrCpy ((CHAR8 *) Buffer, &TempStr[Index]);\r
+\r
+  Length = AsciiStrLen ((CHAR8 *) Buffer);\r
+\r
+  return Length;\r
+}\r
+\r
+\r
+/**\r
+  This function is to convert unicode hex number to a UINT8.\r
+\r
+  @param[out]  Digit                   The converted UINT8 for output.\r
+  @param[in]   Char                    The unicode hex number to be converted.\r
+\r
+  @retval      EFI_SUCCESS             Successfully converted the unicode hex.\r
+  @retval      EFI_INVALID_PARAMETER   Failed to convert the unicode hex.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUniHexToUint8 (\r
+  OUT UINT8                *Digit,\r
+  IN  CHAR16               Char\r
+  )\r
+{\r
+  if ((Char >= L'0') && (Char <= L'9')) {\r
+    *Digit = (UINT8) (Char - L'0');\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if ((Char >= L'A') && (Char <= L'F')) {\r
+    *Digit = (UINT8) (Char - L'A' + 0x0A);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if ((Char >= L'a') && (Char <= L'f')) {\r
+    *Digit = (UINT8) (Char - L'a' + 0x0A);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h
new file mode 100644 (file)
index 0000000..0d78205
--- /dev/null
@@ -0,0 +1,491 @@
+/** @file\r
+  Support functions declaration for UefiPxeBc Driver.\r
+\r
+  Copyright (c) 2007 - 2010, 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
+  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
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_SUPPORT_H__\r
+#define __EFI_PXEBC_SUPPORT_H__\r
+\r
+\r
+#define ICMP_DEST_UNREACHABLE      3\r
+#define ICMP_SOURCE_QUENCH         4\r
+#define ICMP_REDIRECT              5\r
+#define ICMP_ECHO_REQUEST          8\r
+#define ICMP_TIME_EXCEEDED         11\r
+#define ICMP_PARAMETER_PROBLEM     12\r
+\r
+\r
+/**\r
+  This function obtain the system guid and serial number from the smbios table.\r
+\r
+  @param[out]  SystemGuid     The pointer of returned system guid.\r
+\r
+  @retval EFI_SUCCESS         Successfully obtained the system guid.\r
+  @retval EFI_NOT_FOUND       Did not find the SMBIOS table.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcGetSystemGuid (\r
+  OUT EFI_GUID              *SystemGuid\r
+  );\r
+\r
+\r
+/**\r
+  Flush the previous configration using the new station Ip address.\r
+\r
+  @param[in]   Private        Pointer to PxeBc private data.\r
+  @param[in]   StationIp      Pointer to the station Ip address.\r
+  @param[in]   SubnetMask     Pointer to the subnet mask address for v4.\r
+\r
+  @retval EFI_SUCCESS         Successfully flushed the previous config.\r
+  @retval Others              Failed to flush using the new station Ip.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcFlushStaionIp (\r
+  PXEBC_PRIVATE_DATA       *Private,\r
+  EFI_IP_ADDRESS           *StationIp,\r
+  EFI_IP_ADDRESS           *SubnetMask     OPTIONAL\r
+  );\r
+\r
+\r
+/**\r
+  Notify callback function when an event is triggered.\r
+\r
+  @param[in]  Event           The triggered event.\r
+  @param[in]  Context         The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcCommonNotify (\r
+  IN EFI_EVENT           Event,\r
+  IN VOID                *Context\r
+  );\r
+\r
+\r
+/**\r
+  Perform arp resolution from the arp cache in PxeBcMode.\r
+\r
+  @param  Mode           Pointer to EFI_PXE_BASE_CODE_MODE.\r
+  @param  Ip4Addr        The Ip4 address for resolution.\r
+  @param  MacAddress     The resoluted MAC address if the resolution is successful.\r
+                         The value is undefined if resolution fails.\r
+\r
+  @retval TRUE           Found a matched entry.\r
+  @retval FALSE          Did not find a matched entry.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckArpCache (\r
+  IN  EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN  EFI_IPv4_ADDRESS          *Ip4Addr,\r
+  OUT EFI_MAC_ADDRESS           *MacAddress\r
+  );\r
+\r
+\r
+/**\r
+  Update arp cache periodically.\r
+\r
+  @param  Event              Pointer to EFI_PXE_BC_PROTOCOL.\r
+  @param  Context            Context of the timer event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcArpCacheUpdate (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  );\r
+\r
+\r
+/**\r
+  xxx\r
+\r
+  @param  Event                 The event signaled.\r
+  @param  Context               The context passed in by the event notifier.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmpErrorUpdate (\r
+  IN EFI_EVENT             Event,\r
+  IN VOID                  *Context\r
+  );\r
+\r
+\r
+/**\r
+  xxx\r
+\r
+  @param  Event                 The event signaled.\r
+  @param  Context               The context passed in by the event notifier.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmp6ErrorUpdate (\r
+  IN EFI_EVENT             Event,\r
+  IN VOID                  *Context\r
+  );\r
+\r
+\r
+/**\r
+  This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+  @param[in]       Udp4                 Pointer to EFI_UDP4_PROTOCOL.\r
+  @param[in]       StationIp            Pointer to the station address.\r
+  @param[in]       SubnetMask           Pointer to the subnet mask.\r
+  @param[in]       Gateway              Pointer to the gateway address.\r
+  @param[in, out]  SrcPort              Pointer to the source port.\r
+  @param[in]       DoNotFragment        The flag of DoNotFragment bit in the IPv4\r
+                                        packet.\r
+\r
+  @retval          EFI_SUCCESS          Successfully configured this instance.\r
+  @retval          Others               Failed to configure this instance.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcConfigUdp4Write (\r
+  IN     EFI_UDP4_PROTOCOL  *Udp4,\r
+  IN     EFI_IPv4_ADDRESS   *StationIp,\r
+  IN     EFI_IPv4_ADDRESS   *SubnetMask,\r
+  IN     EFI_IPv4_ADDRESS   *Gateway,\r
+  IN OUT UINT16             *SrcPort,\r
+  IN     BOOLEAN            DoNotFragment\r
+  );\r
+\r
+\r
+/**\r
+  This function is to configure a UDPv6 instance for UdpWrite.\r
+\r
+  @param[in]       Udp6                 Pointer to EFI_UDP6_PROTOCOL.\r
+  @param[in]       StationIp            Pointer to the station address.\r
+  @param[in, out]  SrcPort              Pointer to the source port.\r
+\r
+  @retval          EFI_SUCCESS          Successfuly configured this instance.\r
+  @retval          Others               Failed to configure this instance.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcConfigUdp6Write (\r
+  IN     EFI_UDP6_PROTOCOL  *Udp6,\r
+  IN     EFI_IPv6_ADDRESS   *StationIp,\r
+  IN OUT UINT16             *SrcPort\r
+  );\r
+\r
+/**\r
+  This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+  @param[in]       Udp4                 Pointer to EFI_UDP4_PROTOCOL.\r
+  @param[in]       Session              Pointer to the UDP4 session data.\r
+  @param[in]       TimeoutEvent         The event for timeout.\r
+  @param[in]       Gateway              Pointer to the gateway address.\r
+  @param[in]       HeaderSize           An optional field which may be set to the length of a header\r
+                                        at HeaderPtr to be prefixed to the data at BufferPtr.\r
+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be\r
+                                        prefixed to the data at BufferPtr.\r
+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.\r
+  @param[in]       BufferPtr            A pointer to the data to be written.\r
+\r
+  @retval          EFI_SUCCESS          Successfully sent out data with Udp4Write.\r
+  @retval          Others               Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp4Write (\r
+  IN EFI_UDP4_PROTOCOL       *Udp4,\r
+  IN EFI_UDP4_SESSION_DATA   *Session,\r
+  IN EFI_EVENT               TimeoutEvent,\r
+  IN EFI_IPv4_ADDRESS        *Gateway      OPTIONAL,\r
+  IN UINTN                   *HeaderSize   OPTIONAL,\r
+  IN VOID                    *HeaderPtr    OPTIONAL,\r
+  IN UINTN                   *BufferSize,\r
+  IN VOID                    *BufferPtr\r
+  );\r
+\r
+\r
+/**\r
+  This function is to configure a UDPv6 instance for UdpWrite.\r
+\r
+  @param[in]       Udp6                 Pointer to EFI_UDP6_PROTOCOL.\r
+  @param[in]       Session              Pointer to the UDP6 session data.\r
+  @param[in]       TimeoutEvent         The event for timeout.\r
+  @param[in]       HeaderSize           An optional field which may be set to the length of a header\r
+                                        at HeaderPtr to be prefixed to the data at BufferPtr.\r
+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be\r
+                                        prefixed to the data at BufferPtr.\r
+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.\r
+  @param[in]       BufferPtr            A pointer to the data to be written.\r
+\r
+  @retval          EFI_SUCCESS          Successfully to send out data with Udp6Write.\r
+  @retval          Others               Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp6Write (\r
+  IN EFI_UDP6_PROTOCOL       *Udp6,\r
+  IN EFI_UDP6_SESSION_DATA   *Session,\r
+  IN EFI_EVENT               TimeoutEvent,\r
+  IN UINTN                   *HeaderSize   OPTIONAL,\r
+  IN VOID                    *HeaderPtr    OPTIONAL,\r
+  IN UINTN                   *BufferSize,\r
+  IN VOID                    *BufferPtr\r
+  );\r
+\r
+\r
+/**\r
+  Check the received packet with the Ip filter.\r
+\r
+  @param[in]  Mode                Pointer to mode data of PxeBc.\r
+  @param[in]  Session             Pointer to the current UDPv4 session.\r
+  @param[in]  OpFlags             Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Succesfully passed the Ip filter.\r
+  @retval     FALSE               Failed to pass the Ip filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByIpFilter (\r
+  IN EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN VOID                      *Session,\r
+  IN UINT16                    OpFlags\r
+  );\r
+\r
+\r
+/**\r
+  Filter the received packet with the destination Ip.\r
+\r
+  @param[in]       Mode           Pointer to mode data of PxeBc.\r
+  @param[in]       Session        Pointer to the current UDPv4 session.\r
+  @param[in, out]  DestIp         Pointer to the dest Ip address.\r
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Succesfully passed the IPv4 filter.\r
+  @retval     FALSE               Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByDestIp (\r
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN     VOID                      *Session,\r
+  IN OUT EFI_IP_ADDRESS            *DestIp,\r
+  IN     UINT16                    OpFlags\r
+  );\r
+\r
+\r
+/**\r
+  Check the received packet with the destination port.\r
+\r
+  @param[in]       PxeBcMode      Pointer to mode data of PxeBc.\r
+  @param[in]       Session        Pointer to the current UDPv4 session.\r
+  @param[in, out]  DestPort       Pointer to the destination port.\r
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Succesfully passed the IPv4 filter.\r
+  @retval     FALSE               Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByDestPort (\r
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN     VOID                      *Session,\r
+  IN OUT UINT16                    *DestPort,\r
+  IN     UINT16                    OpFlags\r
+  );\r
+\r
+\r
+/**\r
+  Filter the received packet with the source Ip.\r
+\r
+  @param[in]       Mode           Pointer to mode data of PxeBc.\r
+  @param[in]       Session        Pointer to the current UDPv4 session.\r
+  @param[in, out]  SrcIp          Pointer to the source Ip address.\r
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Succesfully passed the IPv4 filter.\r
+  @retval     FALSE               Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcFilterBySrcIp (\r
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN     VOID                      *Session,\r
+  IN OUT EFI_IP_ADDRESS            *SrcIp,\r
+  IN     UINT16                    OpFlags\r
+  );\r
+\r
+\r
+/**\r
+  Filter the received packet with the source port.\r
+\r
+  @param[in]       Mode           Pointer to mode data of PxeBc.\r
+  @param[in]       Session        Pointer to the current UDPv4 session.\r
+  @param[in, out]  SrcPort        Pointer to the source port.\r
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.\r
+\r
+  @retval     TRUE                Succesfully passed the IPv4 filter.\r
+  @retval     FALSE               Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcFilterBySrcPort (\r
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,\r
+  IN     VOID                      *Session,\r
+  IN OUT UINT16                    *SrcPort,\r
+  IN     UINT16                    OpFlags\r
+  );\r
+\r
+\r
+/**\r
+  This function is to receive packet with Udp4Read.\r
+\r
+  @param[in]       Udp4                 Pointer to EFI_UDP4_PROTOCOL.\r
+  @param[in]       Token                Pointer to EFI_UDP4_COMPLETION_TOKEN.\r
+  @param[in]       Mode                 Pointer to EFI_PXE_BASE_CODE_MODE.\r
+  @param[in]       TimeoutEvent         The event for timeout.\r
+  @param[in]       OpFlags              The UDP operation flags.\r
+  @param[in]       IsDone               Pointer to IsDone flag.\r
+  @param[out]      IsMatched            Pointer to IsMatched flag.\r
+  @param[in, out]  DestIp               Pointer to destination address.\r
+  @param[in, out]  DestPort             Pointer to destination port.\r
+  @param[in, out]  SrcIp                Pointer to source address.\r
+  @param[in, out]  SrcPort              Pointer to source port.\r
+\r
+  @retval          EFI_SUCCESS          Successfully read data with Udp4.\r
+  @retval          Others               Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp4Read (\r
+  IN     EFI_UDP4_PROTOCOL            *Udp4,\r
+  IN     EFI_UDP4_COMPLETION_TOKEN    *Token,\r
+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,\r
+  IN     EFI_EVENT                    TimeoutEvent,\r
+  IN     UINT16                       OpFlags,\r
+  IN     BOOLEAN                      *IsDone,\r
+     OUT BOOLEAN                      *IsMatched,\r
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,\r
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL\r
+  );\r
+\r
+\r
+/**\r
+  This function is to receive packet with Udp6Read.\r
+\r
+  @param[in]       Udp6                 Pointer to EFI_UDP6_PROTOCOL.\r
+  @param[in]       Token                Pointer to EFI_UDP6_COMPLETION_TOKEN.\r
+  @param[in]       Mode                 Pointer to EFI_PXE_BASE_CODE_MODE.\r
+  @param[in]       TimeoutEvent         The event for timeout.\r
+  @param[in]       OpFlags              The UDP operation flags.\r
+  @param[in]       IsDone               Pointer to IsDone flag.\r
+  @param[out]      IsMatched            Pointer to IsMatched flag.\r
+  @param[in, out]  DestIp               Pointer to destination address.\r
+  @param[in, out]  DestPort             Pointer to destination port.\r
+  @param[in, out]  SrcIp                Pointer to source address.\r
+  @param[in, out]  SrcPort              Pointer to source port.\r
+\r
+  @retval          EFI_SUCCESS          Successfully read data with Udp6.\r
+  @retval          Others               Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp6Read (\r
+  IN     EFI_UDP6_PROTOCOL            *Udp6,\r
+  IN     EFI_UDP6_COMPLETION_TOKEN    *Token,\r
+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,\r
+  IN     EFI_EVENT                    TimeoutEvent,\r
+  IN     UINT16                       OpFlags,\r
+  IN     BOOLEAN                      *IsDone,\r
+     OUT BOOLEAN                      *IsMatched,\r
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,\r
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,\r
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL\r
+  );\r
+\r
+\r
+/**\r
+  This function is to display the IPv4 address.\r
+\r
+  @param[in]  Ip        Pointer to the IPv4 address.\r
+\r
+**/\r
+VOID\r
+PxeBcShowIp4Addr (\r
+  IN EFI_IPv4_ADDRESS   *Ip\r
+  );\r
+\r
+\r
+/**\r
+  This function is to display the IPv6 address.\r
+\r
+  @param[in]  Ip        Pointer to the IPv6 address.\r
+\r
+**/\r
+VOID\r
+PxeBcShowIp6Addr (\r
+  IN EFI_IPv6_ADDRESS   *Ip\r
+  );\r
+\r
+\r
+/**\r
+  This function is to convert UINTN to ASCII string with required format.\r
+\r
+  @param[in]  Number         Numeric value to be converted.\r
+  @param[in]  Buffer         Pointer to the buffer for ASCII string.\r
+  @param[in]  Length         Length of the required format.\r
+\r
+**/\r
+VOID\r
+PxeBcUintnToAscDecWithFormat (\r
+  IN UINTN                       Number,\r
+  IN UINT8                       *Buffer,\r
+  IN INTN                        Length\r
+  );\r
+\r
+\r
+/**\r
+  This function is to convert a UINTN to a ASCII string, and return the\r
+  actual length of the buffer.\r
+\r
+  @param[in]  Number         Numeric value to be converted.\r
+  @param[in]  Buffer         Pointer to the buffer for ASCII string.\r
+\r
+  @return     Length         The actual length of the ASCII string.\r
+\r
+**/\r
+UINTN\r
+PxeBcUintnToAscDec (\r
+  IN UINTN               Number,\r
+  IN UINT8               *Buffer\r
+  );\r
+\r
+/**\r
+  This function is to convert unicode hex number to a UINT8.\r
+\r
+  @param[out]  Digit                   The converted UINT8 for output.\r
+  @param[in]   Char                    The unicode hex number to be converted.\r
+\r
+  @retval      EFI_SUCCESS             Successfully converted the unicode hex.\r
+  @retval      EFI_INVALID_PARAMETER   Failed to convert the unicode hex.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUniHexToUint8 (\r
+  OUT UINT8                *Digit,\r
+  IN  CHAR16               Char\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf b/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
new file mode 100644 (file)
index 0000000..1e690b8
--- /dev/null
@@ -0,0 +1,98 @@
+## @file\r
+#  Component name for module PxeBc\r
+#\r
+#  Copyright (c) 2007 - 2010, 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
+#  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
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = UefiPxeBcDxe\r
+  FILE_GUID                      = B95E9FDA-26DE-48d2-8807-1F9107AC5E3A\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = PxeBcDriverEntryPoint\r
+  UNLOAD_IMAGE                   = NetLibDefaultUnload\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF\r
+#\r
+\r
+[Sources]\r
+  ComponentName.c\r
+  PxeBcDriver.c\r
+  PxeBcDriver.h\r
+  PxeBcImpl.c\r
+  PxeBcImpl.h\r
+  PxeBcBoot.c\r
+  PxeBcBoot.h\r
+  PxeBcDhcp6.c\r
+  PxeBcDhcp6.h\r
+  PxeBcDhcp4.c\r
+  PxeBcDhcp4.h\r
+  PxeBcMtftp.c\r
+  PxeBcMtftp.h\r
+  PxeBcSupport.c\r
+  PxeBcSupport.h\r
+\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  UefiLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  BaseMemoryLib\r
+  MemoryAllocationLib\r
+  DebugLib\r
+  NetLib\r
+  DpcLib\r
+  DevicePathLib\r
+  PcdLib\r
+\r
+\r
+[Guids]\r
+  gEfiSmbiosTableGuid\r
+\r
+\r
+[Protocols]\r
+  gEfiDevicePathProtocolGuid\r
+  gEfiNetworkInterfaceIdentifierProtocolGuid_31\r
+  gEfiArpServiceBindingProtocolGuid\r
+  gEfiArpProtocolGuid\r
+  gEfiIp4ServiceBindingProtocolGuid\r
+  gEfiIp4ProtocolGuid\r
+  gEfiIp6ServiceBindingProtocolGuid\r
+  gEfiIp6ProtocolGuid\r
+  gEfiIp6ConfigProtocolGuid\r
+  gEfiUdp4ServiceBindingProtocolGuid\r
+  gEfiUdp4ProtocolGuid\r
+  gEfiMtftp4ServiceBindingProtocolGuid\r
+  gEfiMtftp4ProtocolGuid\r
+  gEfiDhcp4ServiceBindingProtocolGuid\r
+  gEfiDhcp4ProtocolGuid\r
+  gEfiUdp6ServiceBindingProtocolGuid\r
+  gEfiUdp6ProtocolGuid\r
+  gEfiMtftp6ServiceBindingProtocolGuid\r
+  gEfiMtftp6ProtocolGuid\r
+  gEfiDhcp6ServiceBindingProtocolGuid\r
+  gEfiDhcp6ProtocolGuid\r
+  gEfiPxeBaseCodeCallbackProtocolGuid\r
+  gEfiPxeBaseCodeProtocolGuid\r
+  gEfiLoadFileProtocolGuid\r
+\r
+[Pcd]\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdTftpBlockSize     ## CONSUMES\r