Add NetworkPkg (P.UDK2010.UP3.Network.P1)

git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524
diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.c b/NetworkPkg/Application/IfConfig6/IfConfig6.c
new file mode 100644
index 0000000..b2eada6
--- /dev/null
+++ b/NetworkPkg/Application/IfConfig6/IfConfig6.c
@@ -0,0 +1,1781 @@
+/** @file

+  The implementation for Shell application IfConfig6.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include <Library/ShellLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/BaseLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/DebugLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/HiiLib.h>

+#include <Library/NetLib.h>

+

+#include <Protocol/Ip6.h>

+#include <Protocol/Ip6Config.h>

+

+#include "IfConfig6.h"

+

+EFI_HII_HANDLE      mHiiHandle;

+

+EFI_GUID            mEfiIfConfig6Guid     = EFI_IFCONFIG6_GUID;

+SHELL_PARAM_ITEM    mIfConfig6CheckList[] = {

+  {

+    L"-b",

+    TypeFlag

+  },

+  {

+    L"-s",

+    TypeMaxValue

+  },

+  {

+    L"-l",

+    TypeValue

+  },

+  {

+    L"-r",

+    TypeValue

+  },

+  {

+    L"-?",

+    TypeFlag

+  },

+  {

+    NULL,

+    TypeMax

+  },

+};

+

+VAR_CHECK_ITEM  mSetCheckList[] = {

+  {

+   L"auto",

+    0x00000001,

+    0x00000001,

+    FlagTypeSingle

+  },

+  {

+    L"man",

+    0x00000002,

+    0x00000001,

+    FlagTypeSingle

+  },

+  {

+    L"host",

+    0x00000004,

+    0x00000002,

+    FlagTypeSingle

+  },

+  {

+    L"dad",

+    0x00000008,

+    0x00000004,

+    FlagTypeSingle

+  },

+  {

+    L"gw",

+    0x00000010,

+    0x00000008,

+    FlagTypeSingle

+  },

+  {

+    L"dns",

+    0x00000020,

+    0x00000010,

+    FlagTypeSingle

+  },

+  {

+    L"id",

+    0x00000040,

+    0x00000020,

+    FlagTypeSingle

+  },

+  {

+    NULL,

+    0x0,

+    0x0,

+    FlagTypeSkipUnknown

+  },

+};

+

+/**

+  Split a string with specified separator and save the substring to a list.

+

+  @param[in]    String       The pointer of the input string.

+  @param[in]    Separator    The specified separator.

+

+  @return The pointer of headnode of ARG_LIST.

+

+**/

+ARG_LIST *

+SplitStrToList (

+  IN CONST CHAR16    *String,

+  IN CHAR16          Separator

+  )

+{

+  CHAR16      *Str;

+  CHAR16      *ArgStr;

+  ARG_LIST    *ArgList;

+  ARG_LIST    *ArgNode;

+

+  if (*String == L'\0') {

+    return NULL;

+  }

+

+  //

+  // Copy the CONST string to a local copy.

+  //

+  Str     = (CHAR16 *) AllocateZeroPool (StrSize (String));

+  ASSERT (Str != NULL);

+  Str     = StrCpy (Str, String);

+  ArgStr  = Str;

+

+  //

+  // init a node for the list head.

+  //

+  ArgNode = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST));

+  ASSERT (ArgNode != NULL);

+  ArgList = ArgNode;

+

+  //

+  // Split the local copy and save in the list node.

+  //

+  while (*Str != L'\0') {

+    if (*Str == Separator) {

+      *Str          = L'\0';

+      ArgNode->Arg  = ArgStr;

+      ArgStr        = Str + 1;

+      ArgNode->Next = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST));

+      ASSERT (ArgNode->Next != NULL);

+      ArgNode = ArgNode->Next;

+    }

+

+    Str++;

+  }

+

+  ArgNode->Arg  = ArgStr;

+  ArgNode->Next = NULL;

+

+  return ArgList;

+}

+

+/**

+  Check the correctness of input Args with '-s' option.

+

+  @param[in]    CheckList    The pointer of VAR_CHECK_ITEM array.

+  @param[in]    Name         The pointer of input arg.

+  @param[in]    Init         The switch to execute the check.

+

+  @return The value of VAR_CHECK_CODE.

+

+**/

+VAR_CHECK_CODE

+IfConfig6RetriveCheckListByName(

+  IN VAR_CHECK_ITEM    *CheckList,

+  IN CHAR16            *Name,

+  IN BOOLEAN           Init

+)

+{

+  STATIC UINT32     CheckDuplicate;

+  STATIC UINT32     CheckConflict;

+  VAR_CHECK_CODE    RtCode;

+  UINT32            Index;

+  VAR_CHECK_ITEM    Arg;

+

+  if (Init) {

+    CheckDuplicate = 0;

+    CheckConflict  = 0;

+    return VarCheckOk;

+  }

+

+  RtCode  = VarCheckOk;

+  Index   = 0;

+  Arg     = CheckList[Index];

+

+  //

+  // Check the Duplicated/Conflicted/Unknown input Args.

+  //

+  while (Arg.FlagStr != NULL) {

+    if (StrCmp (Arg.FlagStr, Name) == 0) {

+

+      if (CheckDuplicate & Arg.FlagID) {

+        RtCode = VarCheckDuplicate;

+        break;

+      }

+

+      if (CheckConflict & Arg.ConflictMask) {

+        RtCode = VarCheckConflict;

+        break;

+      }

+

+      CheckDuplicate |= Arg.FlagID;

+      CheckConflict  |= Arg.ConflictMask;

+      break;

+    }

+

+    Arg = CheckList[++Index];

+  }

+

+  if (Arg.FlagStr == NULL) {

+    RtCode = VarCheckUnknown;

+  }

+

+  return RtCode;

+}

+

+/**

+  The notify function of create event when performing a manual config.

+

+  @param[in]    CheckList    The pointer of Event.

+  @param[in]    Context      The pointer of Context.

+

+**/

+VOID

+EFIAPI

+IfConfig6ManualAddressNotify (

+  IN EFI_EVENT    Event,

+  IN VOID         *Context

+  )

+{

+  *((BOOLEAN *) Context) = TRUE;

+}

+

+/**

+  Print MAC address.

+

+  @param[in]    Node    The pointer of MAC address buffer.

+  @param[in]    Size    The size of MAC address buffer.

+

+**/

+VOID

+IfConfig6PrintMacAddr (

+  IN UINT8     *Node,

+  IN UINT32    Size

+  )

+{

+  UINTN    Index;

+

+  ASSERT (Size <= MACADDRMAXSIZE);

+

+  for (Index = 0; Index < Size; Index++) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_BODY), mHiiHandle, Node[Index]);

+    if (Index + 1 < Size) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);

+    }

+  }

+

+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);

+}

+

+/**

+  Print IPv6 address.

+

+  @param[in]    Ip           The pointer of Ip bufffer in EFI_IPv6_ADDRESS format.

+  @param[in]    PrefixLen    The pointer of PrefixLen that describes the size Prefix.

+

+**/

+VOID

+IfConfig6PrintIpAddr (

+  IN EFI_IPv6_ADDRESS    *Ip,

+  IN UINT8               *PrefixLen

+  )

+{

+  UINTN      Index;

+  BOOLEAN    Short;

+

+  Short = FALSE;

+

+  for (Index = 0; Index < PREFIXMAXLEN; Index = Index + 2) {

+

+    if (!Short && (Index + 1 < PREFIXMAXLEN) && (Index % 2 == 0) && (Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0)) {

+      //

+      // Deal with the case of ::.

+      //

+      if (Index == 0) {

+        //

+        // :: is at the beginning of the address.

+        //

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);

+      }

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);

+

+      while ((Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0) && (Index < PREFIXMAXLEN)) {

+        Index = Index + 2;

+        if (Index > PREFIXMAXLEN - 2) {

+          break;

+        }

+      }

+

+      Short = TRUE;

+

+      if (Index == PREFIXMAXLEN) {

+        //

+        // :: is at the end of the address.

+        //

+        break;

+      }

+    }

+

+    if (Index < PREFIXMAXLEN - 1) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index]);

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index + 1]);

+    }

+

+    if (Index + 2 < PREFIXMAXLEN) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);

+    }

+  }

+

+  if (PrefixLen != NULL) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_PREFIX_LEN), mHiiHandle, *PrefixLen);

+  }

+}

+

+/**

+  Pick up host IPv6 address in string format from Args with "-s" option and convert it to EFI_IP6_CONFIG_MANUAL_ADDRESS format.

+

+  @param[in, out]    Arg        The pointer of the address of ARG_LIST which save Args with the "-s" option.

+  @param[out]        Buf        The pointer of the address of EFI_IP6_CONFIG_MANUAL_ADDRESS.

+  @param[out]        Address    The pointer of BufSize that describes the size of Buf in bytes.

+

+  @retval EFI_SUCCESS    The convertion is successful.

+  @retval Others         Does't find the host address, or it is an invalid IPv6 address in string format.

+

+**/

+EFI_STATUS

+IfConfig6ParseManualAddressList (

+  IN OUT ARG_LIST                         **Arg,

+     OUT EFI_IP6_CONFIG_MANUAL_ADDRESS    **Buf,

+     OUT UINTN                            *BufSize

+  )

+{

+  EFI_STATUS                       Status;

+  EFI_IP6_CONFIG_MANUAL_ADDRESS    *AddrBuf;

+  ARG_LIST                         *VarArg;

+  EFI_IPv6_ADDRESS                 Address;

+  UINT8                            Prefix;

+  UINT8                            AddrCnt;

+

+  AddrCnt  = 0;

+  *BufSize = 0;

+  *Buf     = NULL;

+  VarArg   = *Arg;

+  Status   = EFI_SUCCESS;

+

+  //

+  // Go through the list to check the correctness of input host ip6 address.

+  //

+  while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {

+

+    Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);

+

+    if (EFI_ERROR (Status)) {

+      //

+      // host ip ip ... gw

+      //

+      break;

+    }

+

+    VarArg = VarArg->Next;

+    AddrCnt++;

+  }

+

+  if (AddrCnt == 0) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));

+  ASSERT (AddrBuf != NULL);

+

+  AddrCnt = 0;

+  VarArg  = *Arg;

+  Status  = EFI_SUCCESS;

+

+  //

+  // Go through the list to fill in the EFI_IP6_CONFIG_MANUAL_ADDRESS structure.

+  //

+  while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {

+

+    Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);

+

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+

+    //

+    // If prefix length is not set, set it as Zero here. In the IfConfigSetInterfaceInfo()

+    // Zero prefix, length will be transfered to default prefix length.

+    //

+    if (Prefix == 0xFF) {

+      Prefix = 0;

+    }

+    AddrBuf[AddrCnt].IsAnycast    = FALSE;

+    AddrBuf[AddrCnt].PrefixLength = Prefix;

+    IP6_COPY_ADDRESS (&AddrBuf[AddrCnt].Address, &Address);

+    VarArg = VarArg->Next;

+    AddrCnt++;

+  }

+

+  *Arg = VarArg;

+

+  if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) {

+    goto ON_ERROR;

+  }

+

+  *Buf     = AddrBuf;

+  *BufSize = AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  FreePool (AddrBuf);

+  return Status;

+}

+

+/**

+  Pick up gw/dns IPv6 address in string format from Args with "-s" option and convert it to EFI_IPv6_ADDRESS format.

+

+  @param[in, out]    Arg        The pointer of the address of ARG_LIST that save Args with the "-s" option.

+  @param[out]        Buf        The pointer of the address of EFI_IPv6_ADDRESS.

+  @param[out]        Address    The pointer of BufSize that describes the size of Buf in bytes.

+

+  @retval EFI_SUCCESS    The conversion is successful.

+  @retval Others         Doesn't find the host address, or it is an invalid IPv6 address in string format.

+

+**/

+EFI_STATUS

+IfConfig6ParseGwDnsAddressList (

+  IN OUT ARG_LIST            **Arg,

+     OUT EFI_IPv6_ADDRESS    **Buf,

+     OUT UINTN               *BufSize

+  )

+{

+  EFI_STATUS          Status;

+  EFI_IPv6_ADDRESS    *AddrBuf;

+  ARG_LIST            *VarArg;

+  EFI_IPv6_ADDRESS    Address;

+  UINT8               Prefix;

+  UINT8               AddrCnt;

+

+  AddrCnt  = 0;

+  *BufSize = 0;

+  *Buf     = NULL;

+  VarArg   = *Arg;

+  Status   = EFI_SUCCESS;

+

+  //

+  // Go through the list to check the correctness of input gw/dns address.

+  //

+  while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {

+

+    Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);

+

+    if (EFI_ERROR (Status)) {

+      //

+      // gw ip ip ... host

+      //

+      break;

+    }

+

+    VarArg = VarArg->Next;

+    AddrCnt++;

+  }

+

+  if (AddrCnt == 0) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IPv6_ADDRESS));

+  ASSERT (AddrBuf != NULL);

+

+  AddrCnt = 0;

+  VarArg  = *Arg;

+  Status  = EFI_SUCCESS;

+

+  //

+  // Go through the list to fill in the EFI_IPv6_ADDRESS structure.

+  //

+  while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {

+

+    Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);

+

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+

+    IP6_COPY_ADDRESS (&AddrBuf[AddrCnt], &Address);

+

+    VarArg = VarArg->Next;

+    AddrCnt++;

+  }

+

+  *Arg = VarArg;

+

+  if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) {

+   goto ON_ERROR;

+  }

+

+  *Buf     = AddrBuf;

+  *BufSize = AddrCnt * sizeof (EFI_IPv6_ADDRESS);

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  FreePool (AddrBuf);

+  return Status;

+}

+

+/**

+  Parse InterfaceId in string format from Args with the "-s" option and convert it to EFI_IP6_CONFIG_INTERFACE_ID format.

+

+  @param[in, out]   Arg     The pointer of the address of ARG_LIST that saves Args with the "-s" option.

+  @param[out]       IfId    The pointer of EFI_IP6_CONFIG_INTERFACE_ID.

+

+  @retval EFI_SUCCESS              The get status processed successfullly.

+  @retval EFI_INVALID_PARAMETER    The get status process failed.

+

+**/

+EFI_STATUS

+IfConfig6ParseInterfaceId (

+  IN OUT ARG_LIST                       **Arg,

+     OUT EFI_IP6_CONFIG_INTERFACE_ID    **IfId

+  )

+{

+  UINT8     Index;

+  UINT8     NodeVal;

+  CHAR16    *IdStr;

+

+  if (*Arg == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Index = 0;

+  IdStr = (*Arg)->Arg;

+  ASSERT (IfId != NULL);

+  *IfId = AllocateZeroPool (sizeof (EFI_IP6_CONFIG_INTERFACE_ID));

+  ASSERT (*IfId != NULL);

+

+  while ((*IdStr != L'\0') && (Index < 8)) {

+

+    NodeVal = 0;

+    while ((*IdStr != L':') && (*IdStr != L'\0')) {

+

+      if ((*IdStr <= L'F') && (*IdStr >= L'A')) {

+        NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'A' + 10);

+      } else if ((*IdStr <= L'f') && (*IdStr >= L'a')) {

+        NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'a' + 10);

+      } else if ((*IdStr <= L'9') && (*IdStr >= L'0')) {

+        NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'0');

+      } else {

+        FreePool (*IfId);

+        return EFI_INVALID_PARAMETER;

+      }

+

+      IdStr++;

+    }

+

+    (*IfId)->Id[Index++] = NodeVal;

+

+    if (*IdStr == L':') {

+      IdStr++;

+    }

+  }

+

+  *Arg = (*Arg)->Next;

+  return EFI_SUCCESS;

+}

+

+/**

+  Parse dad in string format from Args with the "-s" option and convert it to UINT32 format.

+

+  @param[in, out]   Arg      The pointer of the address of ARG_LIST that saves Args with the "-s" option.

+  @param[out]       Xmits    The pointer of Xmits.

+

+  @retval EFI_SUCCESS    The get status processed successfully.

+  @retval others         The get status process failed.

+

+**/

+EFI_STATUS

+IfConfig6ParseDadXmits (

+  IN OUT ARG_LIST    **Arg,

+     OUT UINT32      *Xmits

+  )

+{

+  CHAR16    *ValStr;

+

+  if (*Arg == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  ValStr = (*Arg)->Arg;

+  *Xmits = 0;

+

+  while (*ValStr != L'\0') {

+

+    if ((*ValStr <= L'9') && (*ValStr >= L'0')) {

+

+      *Xmits = (*Xmits * 10) + (*ValStr - L'0');

+

+    } else {

+

+      return EFI_INVALID_PARAMETER;

+    }

+

+    ValStr++;

+  }

+

+  *Arg = (*Arg)->Next;

+  return EFI_SUCCESS;

+}

+

+/**

+  The get current status of all handles.

+

+  @param[in]   ImageHandle    The handle of  ImageHandle.

+  @param[in]   IfName         The pointer of  IfName(interface name).

+  @param[in]   IfList         The pointer of  IfList(interface list).

+

+  @retval EFI_SUCCESS    The get status processed successfully.

+  @retval others         The get status process failed.

+

+**/

+EFI_STATUS

+IfConfig6GetInterfaceInfo (

+  IN EFI_HANDLE    ImageHandle,

+  IN CHAR16        *IfName,

+  IN LIST_ENTRY    *IfList

+  )

+{

+  EFI_STATUS                       Status;

+  UINTN                            HandleIndex;

+  UINTN                            HandleNum;

+  EFI_HANDLE                       *HandleBuffer;

+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;

+  EFI_IP6_CONFIG_INTERFACE_INFO    *IfInfo;

+  IFCONFIG6_INTERFACE_CB           *IfCb;

+  UINTN                            DataSize;

+

+  HandleBuffer = NULL;

+  HandleNum    = 0;

+

+  IfInfo       = NULL;

+  IfCb         = NULL;

+

+  //

+  // Locate all the handles with ip6 service binding protocol.

+  //

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiIp6ServiceBindingProtocolGuid,

+                  NULL,

+                  &HandleNum,

+                  &HandleBuffer

+                 );

+  if (EFI_ERROR (Status) || (HandleNum == 0)) {

+    return EFI_ABORTED;

+  }

+

+  //

+  // Enumerate all handles that installed with ip6 service binding protocol.

+  //

+  for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {

+    IfCb      = NULL;

+    IfInfo    = NULL;

+    DataSize  = 0;

+

+    //

+    // Ip6config protocol and ip6 service binding protocol are installed

+    // on the same handle.

+    //

+    ASSERT (HandleBuffer != NULL);

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[HandleIndex],

+                    &gEfiIp6ConfigProtocolGuid,

+                    (VOID **) &Ip6Cfg

+                    );

+

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+    //

+    // Get the interface information size.

+    //

+    Status = Ip6Cfg->GetData (

+                       Ip6Cfg,

+                       Ip6ConfigDataTypeInterfaceInfo,

+                       &DataSize,

+                       NULL

+                       );

+

+    if (Status != EFI_BUFFER_TOO_SMALL) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);

+      goto ON_ERROR;

+    }

+

+    IfInfo = AllocateZeroPool (DataSize);

+

+    if (IfInfo == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_ERROR;

+    }

+    //

+    // Get the interface info.

+    //

+    Status = Ip6Cfg->GetData (

+                       Ip6Cfg,

+                       Ip6ConfigDataTypeInterfaceInfo,

+                       &DataSize,

+                       IfInfo

+                       );

+

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);

+      goto ON_ERROR;

+    }

+    //

+    // Check the interface name if required.

+    //

+    if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) != 0)) {

+      FreePool (IfInfo);

+      continue;

+    }

+

+    DataSize = 0;

+    //

+    // Get the size of dns server list.

+    //

+    Status = Ip6Cfg->GetData (

+                       Ip6Cfg,

+                       Ip6ConfigDataTypeDnsServer,

+                       &DataSize,

+                       NULL

+                       );

+

+    if ((Status != EFI_BUFFER_TOO_SMALL) && (Status != EFI_NOT_FOUND)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);

+      goto ON_ERROR;

+    }

+

+    IfCb = AllocateZeroPool (sizeof (IFCONFIG6_INTERFACE_CB) + DataSize);

+

+    if (IfCb == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_ERROR;

+    }

+

+    IfCb->NicHandle = HandleBuffer[HandleIndex];

+    IfCb->IfInfo    = IfInfo;

+    IfCb->IfCfg     = Ip6Cfg;

+    IfCb->DnsCnt    = (UINT32) (DataSize / sizeof (EFI_IPv6_ADDRESS));

+

+    //

+    // Get the dns server list if has.

+    //

+    if (DataSize > 0) {

+

+      Status = Ip6Cfg->GetData (

+                         Ip6Cfg,

+                         Ip6ConfigDataTypeDnsServer,

+                         &DataSize,

+                         IfCb->DnsAddr

+                         );

+

+      if (EFI_ERROR (Status)) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);

+        goto ON_ERROR;

+      }

+    }

+    //

+    // Get the interface id if has.

+    //

+    DataSize   = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);

+    IfCb->IfId = AllocateZeroPool (DataSize);

+

+    if (IfCb->IfId == NULL) {

+      goto ON_ERROR;

+    }

+

+    Status = Ip6Cfg->GetData (

+                       Ip6Cfg,

+                       Ip6ConfigDataTypeAltInterfaceId,

+                       &DataSize,

+                       IfCb->IfId

+                       );

+

+    if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);

+      goto ON_ERROR;

+    }

+

+    if (Status == EFI_NOT_FOUND) {

+      FreePool (IfCb->IfId);

+      IfCb->IfId = NULL;

+    }

+    //

+    // Get the config policy.

+    //

+    DataSize = sizeof (EFI_IP6_CONFIG_POLICY);

+    Status   = Ip6Cfg->GetData (

+                         Ip6Cfg,

+                         Ip6ConfigDataTypePolicy,

+                         &DataSize,

+                         &IfCb->Policy

+                         );

+

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);

+      goto ON_ERROR;

+    }

+    //

+    // Get the dad transmits.

+    //

+    DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);

+    Status   = Ip6Cfg->GetData (

+                         Ip6Cfg,

+                         Ip6ConfigDataTypeDupAddrDetectTransmits,

+                         &DataSize,

+                         &IfCb->Xmits

+                         );

+

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);

+      goto ON_ERROR;

+    }

+

+    InsertTailList (IfList, &IfCb->Link);

+

+    if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) == 0)) {

+      //

+      // Only need the appointed interface, keep the allocated buffer.

+      //

+      IfCb   = NULL;

+      IfInfo = NULL;

+      break;

+    }

+  }

+

+  if (HandleBuffer != NULL) {

+    FreePool (HandleBuffer);

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  if (IfInfo != NULL) {

+    FreePool (IfInfo);

+  }

+

+  if (IfCb != NULL) {

+    if (IfCb->IfId != NULL) {

+      FreePool (IfCb->IfId);

+    }

+

+    FreePool (IfCb);

+  }

+

+  return Status;

+}

+

+/**

+  The list process of the IfConfig6 application.

+

+  @param[in]   IfList    The pointer of IfList(interface list).

+

+  @retval EFI_SUCCESS    The IfConfig6 list processed successfully.

+  @retval others         The IfConfig6 list process failed.

+

+**/

+EFI_STATUS

+IfConfig6ShowInterfaceInfo (

+  IN LIST_ENTRY    *IfList

+  )

+{

+  EFI_STATUS                Status;

+  LIST_ENTRY                *Entry;

+  IFCONFIG6_INTERFACE_CB    *IfCb;

+  UINTN                     Index;

+

+  Entry  = IfList->ForwardLink;

+  Status = EFI_SUCCESS;

+

+  if (IsListEmpty (IfList)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);

+  }

+

+  //

+  // Go through the interface list.

+  //

+  while (Entry != IfList) {

+

+    IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);

+

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle);

+

+    //

+    // Print interface name.

+    //

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IF_NAME), mHiiHandle, IfCb->IfInfo->Name);

+

+    //

+    // Print interface config policy.

+    //

+    if (IfCb->Policy == Ip6ConfigPolicyAutomatic) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_AUTO), mHiiHandle);

+    } else {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_MAN), mHiiHandle);

+    }

+

+    //

+    // Print dad transmit.

+    //

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DAD_TRANSMITS), mHiiHandle, IfCb->Xmits);

+

+    //

+    // Print interface id if has.

+    //

+    if (IfCb->IfId != NULL) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_INTERFACE_ID_HEAD), mHiiHandle);

+

+      IfConfig6PrintMacAddr (

+        IfCb->IfId->Id,

+        8

+        );

+    }

+    //

+    // Print mac address of the interface.

+    //

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_HEAD), mHiiHandle);

+

+    IfConfig6PrintMacAddr (

+      IfCb->IfInfo->HwAddress.Addr,

+      IfCb->IfInfo->HwAddressSize

+      );

+

+    //

+    // Print ip addresses list of the interface.

+    //

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_HEAD), mHiiHandle);

+

+    for (Index = 0; Index < IfCb->IfInfo->AddressInfoCount; Index++) {

+      IfConfig6PrintIpAddr (

+        &IfCb->IfInfo->AddressInfo[Index].Address,

+        &IfCb->IfInfo->AddressInfo[Index].PrefixLength

+        );

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);

+    }

+

+    //

+    // Print dns server addresses list of the interface if has.

+    //

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DNS_ADDR_HEAD), mHiiHandle);

+

+    for (Index = 0; Index < IfCb->DnsCnt; Index++) {

+      IfConfig6PrintIpAddr (

+        &IfCb->DnsAddr[Index],

+        NULL

+        );

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);

+    }

+

+    //

+    // Print route table of the interface if has.

+    //

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_ROUTE_HEAD), mHiiHandle);

+

+    for (Index = 0; Index < IfCb->IfInfo->RouteCount; Index++) {

+      IfConfig6PrintIpAddr (

+        &IfCb->IfInfo->RouteTable[Index].Destination,

+        &IfCb->IfInfo->RouteTable[Index].PrefixLength

+        );

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_JOINT), mHiiHandle);

+

+      IfConfig6PrintIpAddr (

+        &IfCb->IfInfo->RouteTable[Index].Gateway,

+        NULL

+        );

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);

+    }

+

+    Entry = Entry->ForwardLink;

+  }

+

+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle);

+

+  return Status;

+}

+

+/**

+  The clean process of the IfConfig6 application.

+

+  @param[in]   IfList    The pointer of IfList(interface list).

+

+  @retval EFI_SUCCESS    The IfConfig6 clean processed successfully.

+  @retval others         The IfConfig6 clean process failed.

+

+**/

+EFI_STATUS

+IfConfig6ClearInterfaceInfo (

+  IN LIST_ENTRY    *IfList

+  )

+{

+  EFI_STATUS                Status;

+  LIST_ENTRY                *Entry;

+  IFCONFIG6_INTERFACE_CB    *IfCb;

+  EFI_IP6_CONFIG_POLICY     Policy;

+

+  Policy = Ip6ConfigPolicyAutomatic;

+  Entry  = IfList->ForwardLink;

+  Status = EFI_SUCCESS;

+

+  if (IsListEmpty (IfList)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);

+  }

+

+  //

+  // Go through the interface list.

+  //

+  while (Entry != IfList) {

+

+    IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);

+

+    Status = IfCb->IfCfg->SetData (

+                            IfCb->IfCfg,

+                            Ip6ConfigDataTypePolicy,

+                            sizeof (EFI_IP6_CONFIG_POLICY),

+                            &Policy

+                            );

+

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+

+    Entry  = Entry->ForwardLink;

+  }

+

+  return Status;

+}

+

+/**

+  The set process of the IfConfig6 application.

+

+  @param[in]   IfList    The pointer of IfList(interface list).

+  @param[in]   VarArg    The pointer of ARG_LIST(Args with "-s" option).

+

+  @retval EFI_SUCCESS    The IfConfig6 set processed successfully.

+  @retval others         The IfConfig6 set process failed.

+

+**/

+EFI_STATUS

+IfConfig6SetInterfaceInfo (

+  IN LIST_ENTRY    *IfList,

+  IN ARG_LIST      *VarArg

+  )

+{

+  EFI_STATUS                       Status;

+  IFCONFIG6_INTERFACE_CB           *IfCb;

+  EFI_IP6_CONFIG_MANUAL_ADDRESS    *CfgManAddr;

+  EFI_IPv6_ADDRESS                 *CfgAddr;

+  UINTN                            AddrSize;

+  EFI_IP6_CONFIG_INTERFACE_ID      *InterfaceId;

+  UINT32                           DadXmits;

+  UINT32                           CurDadXmits;

+  UINTN                            CurDadXmitsLen;

+  EFI_IP6_CONFIG_POLICY            Policy;

+

+  VAR_CHECK_CODE                   CheckCode;

+  EFI_EVENT                        TimeOutEvt;

+  EFI_EVENT                        MappedEvt;

+  BOOLEAN                          IsAddressOk;

+

+  UINTN                            DataSize;

+  UINT32                           Index;

+  UINT32                           Index2;

+  BOOLEAN                          IsAddressSet;

+  EFI_IP6_CONFIG_INTERFACE_INFO    *IfInfo;

+

+  CfgManAddr  = NULL;

+  CfgAddr     = NULL;

+  TimeOutEvt  = NULL;

+  MappedEvt   = NULL;

+  IfInfo      = NULL;

+  InterfaceId = NULL;

+  CurDadXmits = 0;

+

+  if (IsListEmpty (IfList)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);

+    return EFI_INVALID_PARAMETER;

+  }

+  //

+  // Make sure to set only one interface each time.

+  //

+  IfCb   = BASE_CR (IfList->ForwardLink, IFCONFIG6_INTERFACE_CB, Link);

+  Status = EFI_SUCCESS;

+

+  //

+  // Initialize check list mechanism.

+  //

+  CheckCode = IfConfig6RetriveCheckListByName(

+                NULL,

+                NULL,

+                TRUE

+                );

+

+  //

+  // Create events & timers for asynchronous settings.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_TIMER,

+                  TPL_CALLBACK,

+                  NULL,

+                  NULL,

+                  &TimeOutEvt

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  IfConfig6ManualAddressNotify,

+                  &IsAddressOk,

+                  &MappedEvt

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // Parse the setting variables.

+  //

+  while (VarArg != NULL) {

+     //

+     // Check invalid parameters (duplication & unknown & conflict).

+     //

+    CheckCode = IfConfig6RetriveCheckListByName(

+                  mSetCheckList,

+                  VarArg->Arg,

+                  FALSE

+                  );

+

+    if (VarCheckOk != CheckCode) {

+      switch (CheckCode) {

+        case VarCheckDuplicate:

+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_DUPLICATE_COMMAND), mHiiHandle, VarArg->Arg);

+          break;

+

+        case VarCheckConflict:

+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_CONFLICT_COMMAND), mHiiHandle, VarArg->Arg);

+          break;

+

+        case VarCheckUnknown:

+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_UNKNOWN_COMMAND), mHiiHandle, VarArg->Arg);

+          break;

+

+        default:

+          break;

+      }

+

+      VarArg = VarArg->Next;

+      continue;

+    }

+    //

+    // Process valid variables.

+    //

+    if (StrCmp(VarArg->Arg, L"auto") == 0) {

+      //

+      // Set automaic config policy

+      //

+      Policy = Ip6ConfigPolicyAutomatic;

+      Status = IfCb->IfCfg->SetData (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypePolicy,

+                              sizeof (EFI_IP6_CONFIG_POLICY),

+                              &Policy

+                              );

+

+      if (EFI_ERROR(Status)) {

+        goto ON_EXIT;

+      }

+

+      VarArg= VarArg->Next;

+

+    } else if (StrCmp (VarArg->Arg, L"man") == 0) {

+      //

+      // Set manual config policy.

+      //

+      Policy = Ip6ConfigPolicyManual;

+      Status = IfCb->IfCfg->SetData (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypePolicy,

+                              sizeof (EFI_IP6_CONFIG_POLICY),

+                              &Policy

+                              );

+

+      if (EFI_ERROR(Status)) {

+        goto ON_EXIT;

+      }

+

+      VarArg= VarArg->Next;

+

+    } else if (StrCmp (VarArg->Arg, L"host") == 0) {

+      //

+      // Parse till the next tag or the end of command line.

+      //

+      VarArg = VarArg->Next;

+      Status = IfConfig6ParseManualAddressList (

+                 &VarArg,

+                 &CfgManAddr,

+                 &AddrSize

+                 );

+

+      if (EFI_ERROR (Status)) {

+        if (Status == EFI_INVALID_PARAMETER) {

+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"host");

+          continue;

+        } else {

+          goto ON_EXIT;

+        }

+      }

+      //

+      // Set static host ip6 address list.

+      //   This is a asynchronous process.

+      //

+      IsAddressOk = FALSE;

+

+      Status = IfCb->IfCfg->RegisterDataNotify (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypeManualAddress,

+                              MappedEvt

+                              );

+      if (EFI_ERROR (Status)) {

+        goto ON_EXIT;

+      }

+

+      Status = IfCb->IfCfg->SetData (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypeManualAddress,

+                              AddrSize,

+                              CfgManAddr

+                              );

+

+      if (Status == EFI_NOT_READY) {

+        //

+        // Get current dad transmits count.

+        //

+        CurDadXmitsLen = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);

+        IfCb->IfCfg->GetData (

+                       IfCb->IfCfg,

+                       Ip6ConfigDataTypeDupAddrDetectTransmits,

+                       &CurDadXmitsLen,

+                       &CurDadXmits

+                       );

+

+        gBS->SetTimer (TimeOutEvt, TimerRelative, 50000000 + 10000000 * CurDadXmits);

+

+        while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {

+          if (IsAddressOk) {

+            Status = EFI_SUCCESS;

+            break;

+          }

+        }

+      }

+

+      IfCb->IfCfg->UnregisterDataNotify (

+                     IfCb->IfCfg,

+                     Ip6ConfigDataTypeManualAddress,

+                     MappedEvt

+                     );

+

+      if (EFI_ERROR (Status)) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_MAN_HOST), mHiiHandle, Status);

+        goto ON_EXIT;

+      }

+

+      //

+      // Check whether the address is set successfully.

+      //

+      DataSize = 0;

+

+      Status = IfCb->IfCfg->GetData (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypeInterfaceInfo,

+                              &DataSize,

+                              NULL

+                              );

+

+      if (Status != EFI_BUFFER_TOO_SMALL) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);

+        goto ON_EXIT;

+      }

+

+      IfInfo = AllocateZeroPool (DataSize);

+

+      if (IfInfo == NULL) {

+        Status = EFI_OUT_OF_RESOURCES;

+        goto ON_EXIT;

+      }

+

+      Status = IfCb->IfCfg->GetData (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypeInterfaceInfo,

+                              &DataSize,

+                              IfInfo

+                              );

+

+      if (EFI_ERROR (Status)) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);

+        goto ON_EXIT;

+      }

+

+      for ( Index = 0; Index < (UINTN) (AddrSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); Index++) {

+        IsAddressSet = FALSE;

+        //

+        // By default, the prefix length 0 is regarded as 64.

+        //

+        if (CfgManAddr[Index].PrefixLength == 0) {

+          CfgManAddr[Index].PrefixLength = 64;

+        }

+

+        for (Index2 = 0; Index2 < IfInfo->AddressInfoCount; Index2++) {

+          if (EFI_IP6_EQUAL (&IfInfo->AddressInfo[Index2].Address, &CfgManAddr[Index].Address) &&

+              (IfInfo->AddressInfo[Index2].PrefixLength == CfgManAddr[Index].PrefixLength)) {

+            IsAddressSet = TRUE;

+            break;

+          }

+        }

+

+        if (!IsAddressSet) {

+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_ADDRESS_FAILED), mHiiHandle);

+          IfConfig6PrintIpAddr (

+            &CfgManAddr[Index].Address,

+            &CfgManAddr[Index].PrefixLength

+            );

+        }

+      }

+

+    } else if (StrCmp (VarArg->Arg, L"gw") == 0) {

+      //

+      // Parse till the next tag or the end of command line.

+      //

+      VarArg = VarArg->Next;

+      Status = IfConfig6ParseGwDnsAddressList (

+                 &VarArg,

+                 &CfgAddr,

+                 &AddrSize

+                 );

+

+      if (EFI_ERROR (Status)) {

+        if (Status == EFI_INVALID_PARAMETER) {

+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"gw");

+          continue;

+        } else {

+          goto ON_EXIT;

+        }

+      }

+      //

+      // Set static gateway ip6 address list.

+      //

+      Status = IfCb->IfCfg->SetData (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypeGateway,

+                              AddrSize,

+                              CfgAddr

+                              );

+

+      if (EFI_ERROR (Status)) {

+        goto ON_EXIT;

+      }

+

+    } else if (StrCmp (VarArg->Arg, L"dns") == 0) {

+      //

+      // Parse till the next tag or the end of command line.

+      //

+      VarArg = VarArg->Next;

+      Status = IfConfig6ParseGwDnsAddressList (

+                 &VarArg,

+                 &CfgAddr,

+                 &AddrSize

+                 );

+

+      if (EFI_ERROR (Status)) {

+        if (Status == EFI_INVALID_PARAMETER) {

+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"dns");

+          continue;

+        } else {

+          goto ON_EXIT;

+        }

+      }

+      //

+      // Set static dhs server ip6 address list.

+      //

+      Status = IfCb->IfCfg->SetData (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypeDnsServer,

+                              AddrSize,

+                              CfgAddr

+                              );

+

+      if (EFI_ERROR (Status)) {

+        goto ON_EXIT;

+      }

+

+    } else if (StrCmp (VarArg->Arg, L"id") == 0) {

+      //

+      // Parse till the next tag or the end of command line.

+      //

+      VarArg = VarArg->Next;

+      Status = IfConfig6ParseInterfaceId (&VarArg, &InterfaceId);

+

+      if (EFI_ERROR (Status)) {

+        goto ON_EXIT;

+      }

+      //

+      // Set alternative interface id.

+      //

+      Status = IfCb->IfCfg->SetData (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypeAltInterfaceId,

+                              sizeof (EFI_IP6_CONFIG_INTERFACE_ID),

+                              InterfaceId

+                              );

+

+      if (EFI_ERROR (Status)) {

+        goto ON_EXIT;

+      }

+

+    } else if (StrCmp (VarArg->Arg, L"dad") == 0) {

+      //

+      // Parse till the next tag or the end of command line.

+      //

+      VarArg = VarArg->Next;

+      Status = IfConfig6ParseDadXmits (&VarArg, &DadXmits);

+

+      if (EFI_ERROR (Status)) {

+        goto ON_EXIT;

+      }

+      //

+      // Set dad transmits count.

+      //

+      Status = IfCb->IfCfg->SetData (

+                              IfCb->IfCfg,

+                              Ip6ConfigDataTypeDupAddrDetectTransmits,

+                              sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS),

+                              &DadXmits

+                              );

+

+      if (EFI_ERROR(Status)) {

+        goto ON_EXIT;

+      }

+    }

+  }

+

+ON_EXIT:

+

+  if (CfgManAddr != NULL) {

+    FreePool (CfgManAddr);

+  }

+

+  if (CfgAddr != NULL) {

+    FreePool (CfgAddr);

+  }

+

+  if (MappedEvt != NULL) {

+    gBS->CloseEvent (MappedEvt);

+  }

+

+  if (TimeOutEvt != NULL) {

+    gBS->CloseEvent (TimeOutEvt);

+  }

+

+  if (IfInfo != NULL) {

+    FreePool (IfInfo);

+  }

+

+  return Status;

+

+}

+

+/**

+  The IfConfig6 main process.

+

+  @param[in]   Private    The pointer of IFCONFIG6_PRIVATE_DATA.

+

+  @retval EFI_SUCCESS    IfConfig6 processed successfully.

+  @retval others         The IfConfig6 process failed.

+

+**/

+EFI_STATUS

+IfConfig6 (

+  IN IFCONFIG6_PRIVATE_DATA    *Private

+  )

+{

+  EFI_STATUS    Status;

+

+  //

+  // Get configure information of all interfaces.

+  //

+  Status = IfConfig6GetInterfaceInfo (

+             Private->ImageHandle,

+             Private->IfName,

+             &Private->IfList

+             );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  switch (Private->OpCode) {

+  case IfConfig6OpList:

+    Status = IfConfig6ShowInterfaceInfo (&Private->IfList);

+    break;

+

+  case IfConfig6OpClear:

+    Status = IfConfig6ClearInterfaceInfo (&Private->IfList);

+    break;

+

+  case IfConfig6OpSet:

+    Status = IfConfig6SetInterfaceInfo (&Private->IfList, Private->VarArg);

+    break;

+

+  default:

+    Status = EFI_ABORTED;

+  }

+

+ON_EXIT:

+

+  return Status;

+}

+

+/**

+  The IfConfig6 cleanup process, free the allocated memory.

+

+  @param[in]   Private    The pointer of  IFCONFIG6_PRIVATE_DATA.

+

+**/

+VOID

+IfConfig6Cleanup (

+  IN IFCONFIG6_PRIVATE_DATA    *Private

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *NextEntry;

+  IFCONFIG6_INTERFACE_CB    *IfCb;

+  ARG_LIST                  *ArgNode;

+  ARG_LIST                  *ArgHead;

+

+  ASSERT (Private != NULL);

+

+  //

+  // Clean the list which save the set config Args.

+  //

+  if (Private->VarArg != NULL) {

+    ArgHead = Private->VarArg;

+

+    while (ArgHead->Next != NULL) {

+      ArgNode = ArgHead->Next;

+      FreePool (ArgHead);

+      ArgHead = ArgNode;

+    }

+

+    FreePool (ArgHead);

+  }

+

+  if (Private->IfName != NULL)

+    FreePool (Private->IfName);

+

+

+  //

+  // Clean the IFCONFIG6_INTERFACE_CB list.

+  //

+  Entry     = Private->IfList.ForwardLink;

+  NextEntry = Entry->ForwardLink;

+

+  while (Entry != &Private->IfList) {

+

+    IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);

+

+    RemoveEntryList (&IfCb->Link);

+

+    if (IfCb->IfId != NULL) {

+

+      FreePool (IfCb->IfId);

+    }

+

+    if (IfCb->IfInfo != NULL) {

+

+      FreePool (IfCb->IfInfo);

+    }

+

+    FreePool (IfCb);

+

+    Entry     = NextEntry;

+    NextEntry = Entry->ForwardLink;

+  }

+

+  FreePool (Private);

+}

+

+/**

+  This is the declaration of an EFI image entry point. This entry point is

+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including

+  both device drivers and bus drivers.

+

+  The entry point for the IfConfig6 application which parses the command line input and calls the IfConfig6 process.

+

+  @param[in] ImageHandle    The image handle of this application.

+  @param[in] SystemTable    The pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS    The operation completed successfully.

+  @retval Others         Some errors occur.

+

+**/

+EFI_STATUS

+EFIAPI

+IfConfig6Initialize (

+  IN  EFI_HANDLE         ImageHandle,

+  IN  EFI_SYSTEM_TABLE    *SystemTable

+  )

+{

+  EFI_STATUS                Status;

+  IFCONFIG6_PRIVATE_DATA    *Private;

+  LIST_ENTRY                *ParamPackage;

+  CONST CHAR16              *ValueStr;

+  ARG_LIST                  *ArgList;

+  CHAR16                    *ProblemParam;

+  CHAR16                    *Str;

+

+  Private = NULL;

+

+  //

+  // Register our string package with HII and return the handle to it.

+  //

+  mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IfConfig6Strings, NULL);

+  ASSERT (mHiiHandle != NULL);

+

+  Status = ShellCommandLineParseEx (mIfConfig6CheckList, &ParamPackage, &ProblemParam, TRUE, FALSE);

+  if (EFI_ERROR (Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_COMMAND), mHiiHandle, ProblemParam);

+    goto ON_EXIT;

+  }

+

+  //

+  // To handle no option.

+  //

+  if (!ShellCommandLineGetFlag (ParamPackage, L"-r") && !ShellCommandLineGetFlag (ParamPackage, L"-s") &&

+      !ShellCommandLineGetFlag (ParamPackage, L"-?") && !ShellCommandLineGetFlag (ParamPackage, L"-l")) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_LACK_OPTION), mHiiHandle);

+    goto ON_EXIT;

+  }

+  //

+  // To handle conflict options.

+  //

+  if (((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-s"))) ||

+      ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) ||

+      ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) ||

+      ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) ||

+      ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) ||

+      ((ShellCommandLineGetFlag (ParamPackage, L"-l")) && (ShellCommandLineGetFlag (ParamPackage, L"-?")))) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_CONFLICT_OPTIONS), mHiiHandle);

+    goto ON_EXIT;

+  }

+  //

+  // To show the help information of ifconfig6 command.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_HELP), mHiiHandle);

+    goto ON_EXIT;

+  }

+

+  Status = EFI_INVALID_PARAMETER;

+

+  Private = AllocateZeroPool (sizeof (IFCONFIG6_PRIVATE_DATA));

+

+  if (Private == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  InitializeListHead (&Private->IfList);

+

+  //

+  // To get interface name for the list option.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"-l")) {

+    Private->OpCode = IfConfig6OpList;

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");

+    if (ValueStr != NULL) {

+      Str             = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr));

+      ASSERT (Str != NULL);

+

+      Str             = StrCpy (Str, ValueStr);

+      Private->IfName = Str;

+    }

+  }

+  //

+  // To get interface name for the clear option.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"-r")) {

+    Private->OpCode = IfConfig6OpClear;

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-r");

+    if (ValueStr != NULL) {

+      Str             = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr));

+      ASSERT (Str != NULL);

+

+      Str             = StrCpy (Str, ValueStr);

+      Private->IfName = Str;

+    }

+  }

+  //

+  // To get interface name and corresponding Args for the set option.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"-s")) {

+

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");

+    if (ValueStr == NULL) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_INTERFACE), mHiiHandle);

+      goto ON_EXIT;

+    }

+    //

+    // To split the configuration into multi-section.

+    //

+    ArgList         = SplitStrToList (ValueStr, L' ');

+    ASSERT (ArgList != NULL);

+

+    Private->OpCode = IfConfig6OpSet;

+    Private->IfName = ArgList->Arg;

+

+    Private->VarArg = ArgList->Next;

+

+    if (Private->IfName == NULL || Private->VarArg == NULL) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_COMMAND), mHiiHandle);

+      goto ON_EXIT;

+    }

+  }

+  //

+  // Main process of ifconfig6.

+  //

+  Status = IfConfig6 (Private);

+

+ON_EXIT:

+

+  ShellCommandLineFreeVarList (ParamPackage);

+  HiiRemovePackages (mHiiHandle);

+  if (Private != NULL)

+    IfConfig6Cleanup (Private);

+

+  return Status;

+}

+

diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.h b/NetworkPkg/Application/IfConfig6/IfConfig6.h
new file mode 100644
index 0000000..eea7df5
--- /dev/null
+++ b/NetworkPkg/Application/IfConfig6/IfConfig6.h
@@ -0,0 +1,84 @@
+/** @file

+  The interface function declaration of shell application IfConfig6.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _IFCONFIG6_H_

+#define _IFCONFIG6_H_

+

+#define EFI_IFCONFIG6_GUID \

+  { \

+    0xbab7296b, 0x222c, 0x4408, {0x9e, 0x6c, 0xc2, 0x5c, 0x18, 0x7e, 0xff, 0x33} \

+  }

+

+enum {

+  IfConfig6OpList     = 1,

+  IfConfig6OpSet      = 2,

+  IfConfig6OpClear    = 3

+};

+

+typedef enum {

+  VarCheckReserved      = -1,

+  VarCheckOk            = 0,

+  VarCheckDuplicate,

+  VarCheckConflict,

+  VarCheckUnknown,

+  VarCheckLackValue,

+  VarCheckOutOfMem

+} VAR_CHECK_CODE;

+

+typedef enum {

+  FlagTypeSingle         = 0,

+  FlagTypeNeedVar,

+  FlagTypeNeedSet,

+  FlagTypeSkipUnknown

+} VAR_CHECK_FLAG_TYPE;

+

+#define MACADDRMAXSIZE    32

+#define PREFIXMAXLEN      16 

+

+typedef struct _IFCONFIG6_INTERFACE_CB {

+  EFI_HANDLE                                  NicHandle;

+  LIST_ENTRY                                  Link;

+  EFI_IP6_CONFIG_PROTOCOL                     *IfCfg;

+  EFI_IP6_CONFIG_INTERFACE_INFO               *IfInfo; 

+  EFI_IP6_CONFIG_INTERFACE_ID                 *IfId;

+  EFI_IP6_CONFIG_POLICY                       Policy;

+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS    Xmits;

+  UINT32                                      DnsCnt;

+  EFI_IPv6_ADDRESS                            DnsAddr[1];

+} IFCONFIG6_INTERFACE_CB;

+

+typedef struct _ARG_LIST ARG_LIST;

+

+struct _ARG_LIST {

+  ARG_LIST    *Next;

+  CHAR16      *Arg;

+};

+

+typedef struct _IFCONFIG6_PRIVATE_DATA {

+  EFI_HANDLE  ImageHandle;

+  LIST_ENTRY  IfList;

+

+  UINT32      OpCode;

+  CHAR16      *IfName;

+  ARG_LIST    *VarArg;

+} IFCONFIG6_PRIVATE_DATA;

+

+typedef struct _VAR_CHECK_ITEM{

+  CHAR16                 *FlagStr;

+  UINT32                 FlagID;

+  UINT32                 ConflictMask;

+  VAR_CHECK_FLAG_TYPE    FlagType;

+} VAR_CHECK_ITEM;

+#endif

diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.inf b/NetworkPkg/Application/IfConfig6/IfConfig6.inf
new file mode 100644
index 0000000..dd3ab64
--- /dev/null
+++ b/NetworkPkg/Application/IfConfig6/IfConfig6.inf
@@ -0,0 +1,52 @@
+## @file

+#  Component description file for Shell application IfConfig6.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010006

+  BASE_NAME                      = IfConfig6

+  FILE_GUID                      = 6F71926E-60CE-428d-AA58-A3D9FB879429

+  MODULE_TYPE                    = UEFI_APPLICATION

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = IfConfig6Initialize

+

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF

+#

+[Sources]

+  IfConfig6Strings.uni

+  IfConfig6.c

+  IfConfig6.h

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+  ShellPkg/ShellPkg.dec

+  

+[LibraryClasses]

+  BaseLib

+  UefiBootServicesTableLib

+  UefiApplicationEntryPoint

+  BaseMemoryLib

+  ShellLib

+  MemoryAllocationLib

+  DebugLib

+  HiiLib

+  NetLib

+

+[Protocols]

+  gEfiIp6ServiceBindingProtocolGuid             ## CONSUMS

+  gEfiIp6ConfigProtocolGuid                     ## CONSUMS

diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni b/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni
new file mode 100644
index 0000000..a5e7fd0
--- /dev/null
+++ b/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni
Binary files differ
diff --git a/NetworkPkg/Application/IpsecConfig/Delete.c b/NetworkPkg/Application/IpsecConfig/Delete.c
new file mode 100644
index 0000000..caeb1c8
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Delete.c
@@ -0,0 +1,110 @@
+/** @file

+  The implementation of delete policy entry function in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecConfig.h"

+#include "Indexer.h"

+#include "Delete.h"

+#include "Match.h"

+#include "ForEach.h"

+

+/**

+  Private function to delete entry information in database.

+

+  @param[in] Selector    The pointer to EFI_IPSEC_CONFIG_SELECTOR structure.

+  @param[in] Data        The pointer to Data.

+  @param[in] Context     The pointer to DELETE_POLICY_ENTRY_CONTEXT.

+

+  @retval EFI_ABORTED    Abort the iteration.

+  @retval EFI_SUCCESS    Continue the iteration.

+**/

+EFI_STATUS

+DeletePolicyEntry (

+  IN EFI_IPSEC_CONFIG_SELECTOR      *Selector,

+  IN VOID                           *Data,

+  IN DELETE_POLICY_ENTRY_CONTEXT    *Context

+  )

+{

+  if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {

+    Context->Status = mIpSecConfig->SetData (

+                                      mIpSecConfig,

+                                      Context->DataType,

+                                      Selector,

+                                      NULL,

+                                      NULL

+                                      );

+    //

+    // Abort the iteration after the insertion.

+    //

+    return EFI_ABORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Flush or delete entry information in the database according to datatype.

+

+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS      Delete entry information successfully.

+  @retval EFI_NOT_FOUND    Can't find the specified entry.

+  @retval Others           Some mistaken case.

+**/

+EFI_STATUS

+FlushOrDeletePolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN LIST_ENTRY                    *ParamPackage

+  )

+{

+  EFI_STATUS                     Status;

+  DELETE_POLICY_ENTRY_CONTEXT    Context;

+  CONST CHAR16                   *ValueStr;

+

+  //

+  // If user wants to remove all.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"-f")) {

+    Status = mIpSecConfig->SetData (

+                             mIpSecConfig,

+                             DataType,

+                             NULL,

+                             NULL,

+                             NULL

+                             );

+  } else {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");

+    if (ValueStr == NULL) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);

+      return EFI_NOT_FOUND;

+    }

+

+    Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);

+    if (!EFI_ERROR (Status)) {

+      Context.DataType  = DataType;

+      Context.Status    = EFI_NOT_FOUND;

+      ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) DeletePolicyEntry, &Context);

+      Status = Context.Status;

+

+      if (Status == EFI_NOT_FOUND) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);

+      } else if (EFI_ERROR (Status)) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DELETE_FAILED), mHiiHandle, mAppName);

+      }

+    }

+  }

+

+  return Status;

+}

diff --git a/NetworkPkg/Application/IpsecConfig/Delete.h b/NetworkPkg/Application/IpsecConfig/Delete.h
new file mode 100644
index 0000000..49f3b0d
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Delete.h
@@ -0,0 +1,42 @@
+/** @file

+  The internal structure and function declaration of delete policy entry function

+  in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __DELETE_H_

+#define __DELETE_H_

+

+typedef struct {

+  EFI_IPSEC_CONFIG_DATA_TYPE    DataType;

+  POLICY_ENTRY_INDEXER          Indexer;

+  EFI_STATUS                    Status;      //Indicate whether deletion succeeds.

+} DELETE_POLICY_ENTRY_CONTEXT;

+

+/**

+  Flush or delete entry information in the database according to datatype.

+

+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS      Delete entry information successfully.

+  @retval EFI_NOT_FOUND    Can't find the specified entry.

+  @retval Others           Some mistaken case.

+**/

+EFI_STATUS

+FlushOrDeletePolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN LIST_ENTRY                    *ParamPackage

+  );

+

+#endif

diff --git a/NetworkPkg/Application/IpsecConfig/Dump.c b/NetworkPkg/Application/IpsecConfig/Dump.c
new file mode 100644
index 0000000..004ab10
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Dump.c
@@ -0,0 +1,530 @@
+/** @file

+  The implementation of dump policy entry function in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecConfig.h"

+#include "Dump.h"

+#include "ForEach.h"

+#include "Helper.h"

+

+/**

+  Private function called to get the version infomation from an EFI_IP_ADDRESS_INFO structure.

+

+  @param[in] AddressInfo    The pointer to the EFI_IP_ADDRESS_INFO structure.

+

+  @return the value of version.

+**/

+UINTN

+GetVerFromAddrInfo (

+  IN EFI_IP_ADDRESS_INFO    *AddressInfo

+)

+{

+  if((AddressInfo->PrefixLength <= 32) && (AddressInfo->Address.Addr[1] == 0) &&

+     (AddressInfo->Address.Addr[2] == 0) && (AddressInfo->Address.Addr[3] == 0)) {

+    return IP_VERSION_4;

+  } else {

+    return IP_VERSION_6;

+  }

+}

+

+/**

+  Private function called to get the version information from a EFI_IP_ADDRESS structure.

+

+  @param[in] Address    The pointer to the EFI_IP_ADDRESS structure.

+

+  @return The value of the version.

+**/

+UINTN

+GetVerFromIpAddr (

+  IN EFI_IP_ADDRESS    *Address

+)

+{

+  if ((Address->Addr[1] == 0) && (Address->Addr[2] == 0) && (Address->Addr[3] == 0)) {

+    return IP_VERSION_4;

+  } else {

+    return IP_VERSION_6;

+  }

+}

+

+/**

+  Private function called to print an ASCII string in unicode char format.

+

+  @param[in] Str       The pointer to the ASCII string.

+  @param[in] Length    The value of the ASCII string length.

+**/

+VOID

+DumpAsciiString (

+  IN CHAR8    *Str,

+  IN UINTN    Length

+  )

+{

+  UINTN    Index;

+  for (Index = 0; Index < Length; Index++) {

+    Print (L"%c", (CHAR16) Str[Index]);

+  }

+}

+

+/**

+  Private function called to print EFI_IP_ADDRESS_INFO content.

+

+  @param[in] AddressInfo    The pointer to the EFI_IP_ADDRESS_INFO structure.

+**/

+VOID

+DumpAddressInfo (

+  IN EFI_IP_ADDRESS_INFO    *AddressInfo

+  )

+{

+  if (IP_VERSION_4 == GetVerFromAddrInfo (AddressInfo)) {

+    Print (

+      L"%d.%d.%d.%d",

+      (UINTN) AddressInfo->Address.v4.Addr[0],

+      (UINTN) AddressInfo->Address.v4.Addr[1],

+      (UINTN) AddressInfo->Address.v4.Addr[2],

+      (UINTN) AddressInfo->Address.v4.Addr[3]

+      );

+    if (AddressInfo->PrefixLength != 32) {

+      Print (L"/%d", (UINTN) AddressInfo->PrefixLength);

+    }

+  }

+

+  if (IP_VERSION_6 == GetVerFromAddrInfo (AddressInfo)) {

+    Print (

+      L"%x:%x:%x:%x:%x:%x:%x:%x",

+      (((UINT16) AddressInfo->Address.v6.Addr[0]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[1]),

+      (((UINT16) AddressInfo->Address.v6.Addr[2]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[3]),

+      (((UINT16) AddressInfo->Address.v6.Addr[4]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[5]),

+      (((UINT16) AddressInfo->Address.v6.Addr[6]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[7]),

+      (((UINT16) AddressInfo->Address.v6.Addr[8]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[9]),

+      (((UINT16) AddressInfo->Address.v6.Addr[10]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[11]),

+      (((UINT16) AddressInfo->Address.v6.Addr[12]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[13]),

+      (((UINT16) AddressInfo->Address.v6.Addr[14]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[15])

+      );

+    if (AddressInfo->PrefixLength != 128) {

+      Print (L"/%d", AddressInfo->PrefixLength);

+    }

+  }

+}

+

+/**

+  Private function called to print EFI_IP_ADDRESS content.

+

+  @param[in] IpAddress    The pointer to the EFI_IP_ADDRESS structure.

+**/

+VOID

+DumpIpAddress (

+  IN EFI_IP_ADDRESS    *IpAddress

+  )

+{

+  if (IP_VERSION_4 == GetVerFromIpAddr (IpAddress)) {

+    Print (

+      L"%d.%d.%d.%d",

+      (UINTN) IpAddress->v4.Addr[0],

+      (UINTN) IpAddress->v4.Addr[1],

+      (UINTN) IpAddress->v4.Addr[2],

+      (UINTN) IpAddress->v4.Addr[3]

+      );

+  }

+

+  if (IP_VERSION_6 == GetVerFromIpAddr (IpAddress)) {

+    Print (

+      L"%x:%x:%x:%x:%x:%x:%x:%x",

+      (((UINT16) IpAddress->v6.Addr[0]) << 8) | ((UINT16) IpAddress->v6.Addr[1]),

+      (((UINT16) IpAddress->v6.Addr[2]) << 8) | ((UINT16) IpAddress->v6.Addr[3]),

+      (((UINT16) IpAddress->v6.Addr[4]) << 8) | ((UINT16) IpAddress->v6.Addr[5]),

+      (((UINT16) IpAddress->v6.Addr[6]) << 8) | ((UINT16) IpAddress->v6.Addr[7]),

+      (((UINT16) IpAddress->v6.Addr[8]) << 8) | ((UINT16) IpAddress->v6.Addr[9]),

+      (((UINT16) IpAddress->v6.Addr[10]) << 8) | ((UINT16) IpAddress->v6.Addr[11]),

+      (((UINT16) IpAddress->v6.Addr[12]) << 8) | ((UINT16) IpAddress->v6.Addr[13]),

+      (((UINT16) IpAddress->v6.Addr[14]) << 8) | ((UINT16) IpAddress->v6.Addr[15])

+      );

+  }

+

+}

+

+/**

+  Private function called to print EFI_IPSEC_SPD_SELECTOR content.

+

+  @param[in] Selector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.

+**/

+VOID

+DumpSpdSelector (

+  IN EFI_IPSEC_SPD_SELECTOR    *Selector

+  )

+{

+  UINT32    Index;

+  CHAR16    *Str;

+

+  for (Index = 0; Index < Selector->LocalAddressCount; Index++) {

+    if (Index > 0) {

+      Print (L",");

+    }

+

+    DumpAddressInfo (&Selector->LocalAddress[Index]);

+  }

+

+  if (Index == 0) {

+    Print (L"localhost");

+  }

+

+  Print (L" -> ");

+

+  for (Index = 0; Index < Selector->RemoteAddressCount; Index++) {

+    if (Index > 0) {

+      Print (L",");

+    }

+

+    DumpAddressInfo (&Selector->RemoteAddress[Index]);

+  }

+

+  Str = MapIntegerToString (Selector->NextLayerProtocol, mMapIpProtocol);

+  if (Str != NULL) {

+    Print (L" %s", Str);

+  } else {

+    Print (L" proto:%d", (UINTN) Selector->NextLayerProtocol);

+  }

+

+  if ((Selector->NextLayerProtocol == EFI_IP4_PROTO_TCP) || (Selector->NextLayerProtocol == EFI_IP4_PROTO_UDP)) {

+    Print (L" port:");

+    if (Selector->LocalPort != EFI_IPSEC_ANY_PORT) {

+      Print (L"%d", Selector->LocalPort);

+      if (Selector->LocalPortRange != 0) {

+        Print (L"~%d", (UINTN) Selector->LocalPort + Selector->LocalPortRange);

+      }

+    } else {

+      Print (L"any");

+    }

+

+    Print (L" -> ");

+    if (Selector->RemotePort != EFI_IPSEC_ANY_PORT) {

+      Print (L"%d", Selector->RemotePort);

+      if (Selector->RemotePortRange != 0) {

+        Print (L"~%d", (UINTN) Selector->RemotePort + Selector->RemotePortRange);

+      }

+    } else {

+      Print (L"any");

+    }

+  } else if (Selector->NextLayerProtocol == EFI_IP4_PROTO_ICMP) {

+    Print (L" class/code:");

+    if (Selector->LocalPort != 0) {

+      Print (L"%d", (UINTN) (UINT8) Selector->LocalPort);

+    } else {

+      Print (L"any");

+    }

+

+    Print (L"/");

+    if (Selector->RemotePort != 0) {

+      Print (L"%d", (UINTN) (UINT8) Selector->RemotePort);

+    } else {

+      Print (L"any");

+    }

+  }

+}

+

+/**

+  Print EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA content.

+

+  @param[in] Selector      The pointer to the EFI_IPSEC_SPD_SELECTOR structure.

+  @param[in] Data          The pointer to the EFI_IPSEC_SPD_DATA structure.

+  @param[in] EntryIndex    The pointer to the Index in SPD Database.

+

+  @retval EFI_SUCCESS    Dump SPD information successfully.

+**/

+EFI_STATUS

+DumpSpdEntry (

+  IN EFI_IPSEC_SPD_SELECTOR    *Selector,

+  IN EFI_IPSEC_SPD_DATA        *Data,

+  IN UINTN                     *EntryIndex

+  )

+{

+  BOOLEAN    HasPre;

+  CHAR16     DataName[128];

+  CHAR16     *String1;

+  CHAR16     *String2;

+  CHAR16     *String3;

+  UINT8      Index;

+

+  Print (L"%d.", (*EntryIndex)++);

+

+  //

+  // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400

+  // Protect  PF:0x34323423 Name:First Entry

+  // ext-sequence sequence-overflow fragcheck life:[B0,S1024,H3600]

+  // ESP algo1 algo2 Tunnel [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx set]

+  //

+

+  DumpSpdSelector (Selector);

+  Print (L"\n  ");

+

+  Print (L"%s ", MapIntegerToString (Data->Action, mMapIpSecAction));

+  Print (L"PF:%08x ", Data->PackageFlag);

+

+  Index = 0;

+  while (Data->Name[Index] != 0) {

+    DataName[Index] = (CHAR16) Data->Name[Index];

+    Index++;

+    ASSERT (Index < 128);

+  }

+  DataName[Index] = L'\0';

+

+  Print (L"Name:%s", DataName);

+

+  if (Data->Action == EfiIPsecActionProtect) {

+    Print (L"\n  ");

+    if (Data->ProcessingPolicy->ExtSeqNum) {

+      Print (L"ext-sequence ");

+    }

+

+    if (Data->ProcessingPolicy->SeqOverflow) {

+      Print (L"sequence-overflow ");

+    }

+

+    if (Data->ProcessingPolicy->FragCheck) {

+      Print (L"fragment-check ");

+    }

+

+    HasPre = FALSE;

+    if (Data->ProcessingPolicy->SaLifetime.ByteCount != 0) {

+      Print (HasPre ? L"," : L"life:[");

+      Print (L"%lxB", Data->ProcessingPolicy->SaLifetime.ByteCount);

+      HasPre = TRUE;

+    }

+

+    if (Data->ProcessingPolicy->SaLifetime.SoftLifetime != 0) {

+      Print (HasPre ? L"," : L"life:[");

+      Print (L"%lxs", Data->ProcessingPolicy->SaLifetime.SoftLifetime);

+      HasPre = TRUE;

+    }

+

+    if (Data->ProcessingPolicy->SaLifetime.HardLifetime != 0) {

+      Print (HasPre ? L"," : L"life:[");

+      Print (L"%lxS", Data->ProcessingPolicy->SaLifetime.HardLifetime);

+      HasPre = TRUE;

+    }

+

+    if (HasPre) {

+      Print (L"]");

+    }

+

+    if (HasPre || Data->ProcessingPolicy->ExtSeqNum ||

+        Data->ProcessingPolicy->SeqOverflow || Data->ProcessingPolicy->FragCheck) {

+      Print (L"\n  ");

+    }

+

+    String1 = MapIntegerToString (Data->ProcessingPolicy->Proto, mMapIpSecProtocol);

+    String2 = MapIntegerToString (Data->ProcessingPolicy->AuthAlgoId, mMapAuthAlgo);

+    String3 = MapIntegerToString (Data->ProcessingPolicy->EncAlgoId, mMapEncAlgo);

+    Print (

+      L"%s Auth:%s Encrypt:%s ",

+      String1,

+      String2,

+      String3

+      );

+

+    Print (L"%s ", MapIntegerToString (Data->ProcessingPolicy->Mode, mMapIpSecMode));

+    if (Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {

+      Print (L"[");

+      DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress);

+      Print (L" -> ");

+      DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);

+      Print (L" %s]", MapIntegerToString (Data->ProcessingPolicy->TunnelOption->DF, mMapDfOption));

+    }

+  }

+

+  Print (L"\n");

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Print EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA content.

+

+  @param[in] SaId          The pointer to the EFI_IPSEC_SA_ID structure.

+  @param[in] Data          The pointer to the EFI_IPSEC_SA_DATA structure.

+  @param[in] EntryIndex    The pointer to the Index in the SAD Database.

+

+  @retval EFI_SUCCESS    Dump SAD information successfully.

+**/

+EFI_STATUS

+DumpSadEntry (

+  IN EFI_IPSEC_SA_ID      *SaId,

+  IN EFI_IPSEC_SA_DATA    *Data,

+  IN UINTN                *EntryIndex

+  )

+{

+  BOOLEAN    HasPre;

+  CHAR16     *String1;

+  CHAR16     *String2;

+

+  //

+  // SPI:1234 ESP Destination:xxx.xxx.xxx.xxx

+  //  Mode:Transport SeqNum:134 AntiReplayWin:64 life:[0B,1023s,3400S] PathMTU:34

+  //  Auth:xxxx/password Encrypt:yyyy/password

+  //  xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400

+  //

+

+  Print (L"%d.", (*EntryIndex)++);

+  Print (L"0x%x %s ", (UINTN) SaId->Spi, MapIntegerToString (SaId->Proto, mMapIpSecProtocol));

+  Print (L"Destination:");

+  DumpIpAddress (&SaId->DestAddress);

+  Print (L"\n");

+

+  Print (

+    L"  Mode:%s SeqNum:%lx AntiReplayWin:%d ",

+    MapIntegerToString (Data->Mode, mMapIpSecMode),

+    Data->SNCount,

+    (UINTN) Data->AntiReplayWindows

+    );

+

+  HasPre = FALSE;

+  if (Data->SaLifetime.ByteCount != 0) {

+    Print (HasPre ? L"," : L"life:[");

+    Print (L"%lxB", Data->SaLifetime.ByteCount);

+    HasPre = TRUE;

+  }

+

+  if (Data->SaLifetime.SoftLifetime != 0) {

+    Print (HasPre ? L"," : L"life:[");

+    Print (L"%lxs", Data->SaLifetime.SoftLifetime);

+    HasPre = TRUE;

+  }

+

+  if (Data->SaLifetime.HardLifetime != 0) {

+    Print (HasPre ? L"," : L"life:[");

+    Print (L"%lxS", Data->SaLifetime.HardLifetime);

+    HasPre = TRUE;

+  }

+

+  if (HasPre) {

+    Print (L"] ");

+  }

+

+  Print (L"PathMTU:%d\n", (UINTN) Data->PathMTU);

+

+  if (SaId->Proto == EfiIPsecAH) {

+    Print (

+      L"  Auth:%s/%s\n",

+      MapIntegerToString (Data->AlgoInfo.AhAlgoInfo.AuthAlgoId, mMapAuthAlgo),

+      Data->AlgoInfo.AhAlgoInfo.AuthKey

+      );

+  } else {

+    String1 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, mMapAuthAlgo);

+    String2 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.EncAlgoId, mMapEncAlgo);

+    Print (

+      L"  Auth:%s/%s Encrypt:%s/%s\n",

+      String1,

+      Data->AlgoInfo.EspAlgoInfo.AuthKey,

+      String2,

+      Data->AlgoInfo.EspAlgoInfo.EncKey

+      );

+  }

+

+  if (Data->SpdSelector != NULL) {

+    Print (L"  ");

+    DumpSpdSelector (Data->SpdSelector);

+    Print (L"\n");

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Print EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA content.

+

+  @param[in] PadId         The pointer to the EFI_IPSEC_PAD_ID structure.

+  @param[in] Data          The pointer to the EFI_IPSEC_PAD_DATA structure.

+  @param[in] EntryIndex    The pointer to the Index in the PAD Database.

+

+  @retval EFI_SUCCESS    Dump PAD information successfully.

+**/

+EFI_STATUS

+DumpPadEntry (

+  IN EFI_IPSEC_PAD_ID      *PadId,

+  IN EFI_IPSEC_PAD_DATA    *Data,

+  IN UINTN                 *EntryIndex

+  )

+{

+  CHAR16    *String1;

+  CHAR16    *String2;

+

+  //

+  // ADDR:10.23.17.34/15

+  // IDEv1 PreSharedSecret IKE-ID

+  // password

+  //

+

+  Print (L"%d.", (*EntryIndex)++);

+

+  if (PadId->PeerIdValid) {

+    Print (L"ID:%s", PadId->Id.PeerId);

+  } else {

+    Print (L"ADDR:");

+    DumpAddressInfo (&PadId->Id.IpAddress);

+  }

+

+  Print (L"\n");

+

+  String1 = MapIntegerToString (Data->AuthProtocol, mMapAuthProto);

+  String2 = MapIntegerToString (Data->AuthMethod, mMapAuthMethod);

+  Print (

+    L"  %s %s",

+    String1,

+    String2

+    );

+

+  if (Data->IkeIdFlag) {

+    Print (L"IKE-ID");

+  }

+

+  Print (L"\n");

+

+  if (Data->AuthData != NULL) {

+    DumpAsciiString (Data->AuthData, Data->AuthDataSize);

+    Print (L"\n");

+  }

+

+  if (Data->RevocationData != NULL) {

+    Print (L"  %s\n", Data->RevocationData);

+  }

+

+  return EFI_SUCCESS;

+

+}

+

+VISIT_POLICY_ENTRY  mDumpPolicyEntry[] = {

+  (VISIT_POLICY_ENTRY) DumpSpdEntry,

+  (VISIT_POLICY_ENTRY) DumpSadEntry,

+  (VISIT_POLICY_ENTRY) DumpPadEntry

+};

+

+/**

+  Print all entry information in the database according to datatype.

+

+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS    Dump all information successfully.

+  @retval Others         Some mistaken case.

+**/

+EFI_STATUS

+ListPolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN LIST_ENTRY                    *ParamPackage

+  )

+{

+  UINTN  EntryIndex;

+

+  EntryIndex = 0;

+  return ForeachPolicyEntry (DataType, mDumpPolicyEntry[DataType], &EntryIndex);

+}

+

diff --git a/NetworkPkg/Application/IpsecConfig/Dump.h b/NetworkPkg/Application/IpsecConfig/Dump.h
new file mode 100644
index 0000000..3c475dd
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Dump.h
@@ -0,0 +1,34 @@
+/** @file

+  The function declaration of dump policy entry function in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _DUMP_H_

+#define _DUMP_H_

+

+/**

+  Print all entry information in the database according to datatype.

+

+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS    Dump all information successfully.

+  @retval Others         Some mistaken case.

+**/

+EFI_STATUS

+ListPolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN LIST_ENTRY                    *ParamPackage

+  );

+

+#endif

diff --git a/NetworkPkg/Application/IpsecConfig/ForEach.c b/NetworkPkg/Application/IpsecConfig/ForEach.c
new file mode 100644
index 0000000..7b3b9b1
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/ForEach.c
@@ -0,0 +1,115 @@
+/** @file

+  The implementation to go through each entry in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecConfig.h"

+#include "ForEach.h"

+

+

+/**

+  Enumerate all entries in the database to execute specified operations according to datatype.

+

+  @param[in] DataType    The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] Routine     The pointer to the function of a specified operation.

+  @param[in] Context     The pointer to the context of a function.

+

+  @retval EFI_SUCCESS    Execute specified operation successfully.

+**/

+EFI_STATUS

+ForeachPolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN VISIT_POLICY_ENTRY            Routine,

+  IN VOID                          *Context

+  )

+{

+  EFI_STATUS                   GetNextStatus;

+  EFI_STATUS                   GetDataStatus;

+  EFI_IPSEC_CONFIG_SELECTOR    *Selector;

+  VOID                         *Data;

+  UINTN                        SelectorSize;

+  UINTN                        DataSize;

+  BOOLEAN                      FirstGetNext;

+

+  FirstGetNext = TRUE;

+  SelectorSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR);

+  Selector     = AllocateZeroPool (SelectorSize);

+

+  DataSize     = 0;

+  Data         = NULL;

+

+  while (TRUE) {

+    GetNextStatus = mIpSecConfig->GetNextSelector (

+                                    mIpSecConfig,

+                                    DataType,

+                                    &SelectorSize,

+                                    Selector

+                                    );

+    if (GetNextStatus == EFI_BUFFER_TOO_SMALL) {

+      gBS->FreePool (Selector);

+      Selector = FirstGetNext ? AllocateZeroPool (SelectorSize) : AllocatePool (SelectorSize);

+

+      GetNextStatus = mIpSecConfig->GetNextSelector (

+                                      mIpSecConfig,

+                                      DataType,

+                                      &SelectorSize,

+                                      Selector

+                                      );

+    }

+

+    if (EFI_ERROR (GetNextStatus)) {

+      break;

+    }

+

+    FirstGetNext = FALSE;

+

+    GetDataStatus = mIpSecConfig->GetData (

+                                    mIpSecConfig,

+                                    DataType,

+                                    Selector,

+                                    &DataSize,

+                                    Data

+                                    );

+    if (GetDataStatus == EFI_BUFFER_TOO_SMALL) {

+      if (Data != NULL) {

+        gBS->FreePool (Data);

+      }

+

+      Data = AllocateZeroPool (DataSize);

+      GetDataStatus = mIpSecConfig->GetData (

+                                      mIpSecConfig,

+                                      DataType,

+                                      Selector,

+                                      &DataSize,

+                                      Data

+                                      );

+    }

+

+    ASSERT_EFI_ERROR (GetDataStatus);

+

+    if (EFI_ERROR (Routine (Selector, Data, Context))) {

+      break;

+    }

+  }

+

+  if (Data != NULL) {

+    gBS->FreePool (Data);

+  }

+

+  if (Selector != NULL) {

+    gBS->FreePool (Selector);

+  }

+

+  return EFI_SUCCESS;

+}

+

diff --git a/NetworkPkg/Application/IpsecConfig/ForEach.h b/NetworkPkg/Application/IpsecConfig/ForEach.h
new file mode 100644
index 0000000..fc30930
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/ForEach.h
@@ -0,0 +1,54 @@
+/** @file

+  The internal structure and function declaration of the implementation

+  to go through each entry in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _FOREACH_H_

+#define _FOREACH_H_

+

+/**

+  The prototype for the DumpSpdEntry()/DumpSadEntry()/DumpPadEntry().

+  Print EFI_IPSEC_CONFIG_SELECTOR and corresponding content.

+

+  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.

+  @param[in] Data        The pointer to the corresponding data.

+  @param[in] Context     The pointer to the Index in SPD/SAD/PAD Database.

+

+  @retval EFI_SUCCESS    Dump SPD/SAD/PAD information successfully.

+**/

+typedef

+EFI_STATUS

+(*VISIT_POLICY_ENTRY) (

+  IN EFI_IPSEC_CONFIG_SELECTOR    *Selector,

+  IN VOID                         *Data,

+  IN VOID                         *Context

+  );

+

+/**

+  Enumerate all entry in the database to execute a specified operation according to datatype.

+

+  @param[in] DataType    The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] Routine     The pointer to function of a specified operation.

+  @param[in] Context     The pointer to the context of a function.

+

+  @retval EFI_SUCCESS    Execute specified operation successfully.

+**/

+EFI_STATUS

+ForeachPolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN VISIT_POLICY_ENTRY            Routine,

+  IN VOID                          *Context

+  );

+

+#endif

diff --git a/NetworkPkg/Application/IpsecConfig/Helper.c b/NetworkPkg/Application/IpsecConfig/Helper.c
new file mode 100644
index 0000000..5013ad9
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Helper.c
@@ -0,0 +1,419 @@
+/** @file

+  The assistant function implementation for IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecConfig.h"

+#include "Helper.h"

+

+/**

+  Helper function called to change an input parameter in the string format to a number.

+

+  @param[in]      FlagStr         The pointer to the flag string.

+  @param[in]      Maximum         Greatest value number.

+  @param[in, out] ValuePtr        The pointer to the input parameter in string format.

+  @param[in]      ByteCount       The valid byte count

+  @param[in]      Map             The pointer to the STR2INT table.

+  @param[in]      ParamPackage    The pointer to the ParamPackage list.

+  @param[in]      FormatMask      The bit mask.

+                                  BIT 0 set indicates the value of a flag might be a number.

+                                  BIT 1 set indicates the value of a flag might be a string that needs to be looked up.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_NOT_FOUND            The input parameter can't be found.

+  @retval EFI_INVALID_PARAMETER    The input parameter is an invalid input.

+**/

+EFI_STATUS

+GetNumber (

+  IN     CHAR16        *FlagStr,

+  IN     UINT64        Maximum,

+  IN OUT VOID          *ValuePtr,

+  IN     UINTN         ByteCount,

+  IN     STR2INT       *Map,

+  IN     LIST_ENTRY    *ParamPackage,

+  IN     UINT32        FormatMask

+  )

+{

+  EFI_STATUS      Status;

+  UINT64          Value64;

+  BOOLEAN         Converted;

+  UINTN           Index;

+  CONST CHAR16    *ValueStr;

+

+  ASSERT (FormatMask & (FORMAT_NUMBER | FORMAT_STRING));

+

+  Converted = FALSE;

+  Value64   = 0;

+  ValueStr  = ShellCommandLineGetValue (ParamPackage, FlagStr);

+

+  if (ValueStr == NULL) {

+    return EFI_NOT_FOUND;

+  } else {

+    //

+    // Try to convert to integer directly if MaybeNumber is TRUE.

+    //

+    if ((FormatMask & FORMAT_NUMBER) != 0) {

+      Value64 = StrToUInteger (ValueStr, &Status);

+      if (!EFI_ERROR (Status)) {

+        //

+        // Convert successfully.

+        //

+        if (Value64 > Maximum) {

+          //

+          // But the result is invalid

+          //

+          ShellPrintHiiEx (

+            -1,

+            -1,

+            NULL,

+            STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+            mHiiHandle,

+            mAppName,

+            FlagStr,

+            ValueStr

+            );

+          return EFI_INVALID_PARAMETER;

+        }

+

+        Converted = TRUE;

+      }

+    }

+

+    if (!Converted && ((FormatMask & FORMAT_STRING) != 0)) {

+      //

+      // Convert falied, so use String->Integer map.

+      //

+      Value64 = MapStringToInteger (ValueStr, Map);

+      if (Value64 == (UINT32) -1) {

+        //

+        // Cannot find the string in the map.

+        //

+        ShellPrintHiiEx (

+          -1,

+          -1,

+          NULL,

+          STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+          mHiiHandle,

+          mAppName,

+          FlagStr,

+          ValueStr

+          );

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ACCEPT_PARAMETERS), mHiiHandle);

+        for (Index = 0; Map[Index].String != NULL; Index++) {

+          Print (L" %s", Map[Index].String);

+        }

+

+        Print (L"\n");

+        return EFI_INVALID_PARAMETER;

+      }

+    }

+

+    CopyMem (ValuePtr, &Value64, ByteCount);

+    return EFI_SUCCESS;

+  }

+}

+

+/**

+  Helper function called to convert a string containing an Ipv4 or Ipv6 Internet Protocol address

+  into a proper address for the EFI_IP_ADDRESS structure.

+

+  @param[in]  Ptr    The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.

+  @param[out] Ip     The pointer to the EFI_IP_ADDRESS structure to contain the result.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid parameter.

+**/

+EFI_STATUS

+EfiInetAddr2 (

+  IN  CHAR16            *Ptr,

+  OUT EFI_IP_ADDRESS    *Ip

+  )

+{

+  EFI_STATUS    Status;

+

+  if ((Ptr == NULL) || (Ip == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Parse the input address as Ipv4 Address first.

+  //

+  Status = NetLibStrToIp4 (Ptr, &Ip->v4);

+  if (!EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = NetLibStrToIp6 (Ptr, &Ip->v6);

+  return Status;

+}

+

+/**

+  Helper function called to calculate the prefix length associated with the string

+  containing an Ipv4 or Ipv6 Internet Protocol address.

+

+  @param[in]  Ptr     The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.

+  @param[out] Addr    The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid parameter.

+  @retval Others                   Other mistake case.

+**/

+EFI_STATUS

+EfiInetAddrRange (

+  IN  CHAR16                 *Ptr,

+  OUT EFI_IP_ADDRESS_INFO    *Addr

+  )

+{

+  EFI_STATUS    Status;

+

+  if ((Ptr == NULL) || (Addr == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = NetLibStrToIp4 (Ptr, &Addr->Address.v4);

+  if (!EFI_ERROR (Status)) {

+    if ((UINT32)(*Addr->Address.v4.Addr) == 0) {

+      Addr->PrefixLength = 0;

+    } else {

+      Addr->PrefixLength = 32;

+    }

+    return Status;

+  }

+

+  Status = NetLibStrToIp6andPrefix (Ptr, &Addr->Address.v6, &Addr->PrefixLength);

+  if (!EFI_ERROR (Status) && (Addr->PrefixLength == 0xFF)) {

+    Addr->PrefixLength = 128;

+  }

+

+  return Status;

+}

+

+/**

+  Helper function called to calculate the port range associated with the string.

+

+  @param[in]  Ptr          The pointer to the string containing a port and range.

+  @param[out] Port         The pointer to the Port to contain the result.

+  @param[out] PortRange    The pointer to the PortRange to contain the result.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid parameter.

+  @retval Others                   Other mistake case.

+**/

+EFI_STATUS

+EfiInetPortRange (

+  IN  CHAR16    *Ptr,

+  OUT UINT16    *Port,

+  OUT UINT16    *PortRange

+  )

+{

+  CHAR16        *BreakPtr;

+  CHAR16        Ch;

+  EFI_STATUS    Status;

+

+  for (BreakPtr = Ptr; (*BreakPtr != L'\0') && (*BreakPtr != L':'); BreakPtr++) {

+    ;

+  }

+

+  Ch        = *BreakPtr;

+  *BreakPtr = L'\0';

+  *Port     = (UINT16) StrToUInteger (Ptr, &Status);

+  *BreakPtr = Ch;

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  *PortRange = 0;

+  if (*BreakPtr == L':') {

+    BreakPtr++;

+    *PortRange = (UINT16) StrToUInteger (BreakPtr, &Status);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    if (*PortRange < *Port) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    *PortRange = (UINT16) (*PortRange - *Port);

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Helper function called to transfer a string to an unsigned integer.

+

+  @param[in]  Str       The pointer to the string.

+  @param[out] Status    The operation status.

+

+  @return The integer value of converted Str.

+**/

+UINT64

+StrToUInteger (

+  IN  CONST CHAR16    *Str,

+  OUT EFI_STATUS      *Status

+  )

+{

+  UINT64    Value;

+  UINT64    NewValue;

+  CHAR16    *StrTail;

+  CHAR16    Char;

+  UINTN     Base;

+  UINTN     Len;

+

+  Base    = 10;

+  Value   = 0;

+  *Status = EFI_ABORTED;

+

+  //

+  // Skip leading white space.

+  //

+  while ((*Str != 0) && (*Str == ' ')) {

+    Str++;

+  }

+  //

+  // For NULL Str, just return.

+  //

+  if (*Str == 0) {

+    return 0;

+  }

+  //

+  // Skip white space in tail.

+  //

+  Len     = StrLen (Str);

+  StrTail = (CHAR16 *) (Str + Len - 1);

+  while (*StrTail == ' ') {

+    *StrTail = 0;

+    StrTail--;

+  }

+

+  Len = StrTail - Str + 1;

+

+  //

+  // Check hex prefix '0x'.

+  //

+  if ((Len >= 2) && (*Str == '0') && ((*(Str + 1) == 'x') || (*(Str + 1) == 'X'))) {

+    Str += 2;

+    Len -= 2;

+    Base = 16;

+  }

+

+  if (Len == 0) {

+    return 0;

+  }

+  //

+  // Convert the string to value.

+  //

+  for (; Str <= StrTail; Str++) {

+

+    Char = *Str;

+

+    if (Base == 16) {

+      if (RShiftU64 (Value, 60) != 0) {

+        //

+        // Overflow here x16.

+        //

+        return 0;

+      }

+

+      NewValue = LShiftU64 (Value, 4);

+    } else {

+      if (RShiftU64 (Value, 61) != 0) {

+        //

+        // Overflow here x8.

+        //

+        return 0;

+      }

+

+      NewValue  = LShiftU64 (Value, 3);

+      Value     = LShiftU64 (Value, 1);

+      NewValue += Value;

+      if (NewValue < Value) {

+        //

+        // Overflow here.

+        //

+        return 0;

+      }

+    }

+

+    Value = NewValue;

+

+    if ((Base == 16) && (Char >= 'a') && (Char <= 'f')) {

+      Char = (CHAR16) (Char - 'a' + 'A');

+    }

+

+    if ((Base == 16) && (Char >= 'A') && (Char <= 'F')) {

+      Value += (Char - 'A') + 10;

+    } else if ((Char >= '0') && (Char <= '9')) {

+      Value += (Char - '0');

+    } else {

+      //

+      // Unexpected Char encountered.

+      //

+      return 0;

+    }

+  }

+

+  *Status = EFI_SUCCESS;

+  return Value;

+}

+

+/**

+  Helper function called to transfer a string to an unsigned integer according to the map table.

+

+  @param[in] Str    The pointer to the string.

+  @param[in] Map    The pointer to the map table.

+

+  @return The integer value of converted Str. If not found, then return -1.

+**/

+UINT32

+MapStringToInteger (

+  IN CONST CHAR16    *Str,

+  IN STR2INT         *Map

+  )

+{

+  STR2INT       *Item;

+

+  for (Item = Map; Item->String != NULL; Item++) {

+    if (StrCmp (Item->String, Str) == 0) {

+      return Item->Integer;

+    }

+  }

+

+  return (UINT32) -1;

+}

+

+/**

+  Helper function called to transfer an unsigned integer to a string according to the map table.

+

+  @param[in] Integer    The pointer to the string.

+  @param[in] Map        The pointer to the map table.

+

+  @return The converted Str. If not found, then return NULL.

+**/

+CHAR16 *

+MapIntegerToString (

+  IN UINT32     Integer,

+  IN STR2INT    *Map

+  )

+{

+  STR2INT    *Item;

+

+  for (Item = Map; Item->String != NULL; Item++) {

+    if (Integer == Item->Integer) {

+      return Item->String;

+    }

+  }

+

+  return NULL;

+}

diff --git a/NetworkPkg/Application/IpsecConfig/Helper.h b/NetworkPkg/Application/IpsecConfig/Helper.h
new file mode 100644
index 0000000..d893145
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Helper.h
@@ -0,0 +1,143 @@
+/** @file

+  The assistant function declaration for IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _HELPER_H_

+#define _HELPER_H_

+

+#define  FORMAT_NUMBER 0x1

+#define  FORMAT_STRING 0x2

+

+/**

+  Helper function called to change input parameter in string format to number.

+

+  @param[in]      FlagStr         The pointer to the flag string.

+  @param[in]      Maximum         most value number.

+  @param[in, out] ValuePtr        The pointer to the input parameter in string format.

+  @param[in]      ByteCount       The valid byte count

+  @param[in]      Map             The pointer to the STR2INT table.

+  @param[in]      ParamPackage    The pointer to the ParamPackage list.

+  @param[in]      FormatMask      The bit mask.

+                                  BIT 0 set indicates the value of flag might be number.

+                                  BIT 1 set indicates the value of flag might be a string that needs to be looked up.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_NOT_FOUND            The input parameter can't be found.

+  @retval EFI_INVALID_PARAMETER    The input parameter is an invalid input.

+**/

+EFI_STATUS

+GetNumber (

+  IN     CHAR16        *FlagStr,

+  IN     UINT64        Maximum,

+  IN OUT VOID          *ValuePtr,

+  IN     UINTN         ByteCount,

+  IN     STR2INT       *Map,

+  IN     LIST_ENTRY    *ParamPackage,

+  IN     UINT32        FormatMask

+  );

+

+/**

+  Helper function called to convert a string containing an (Ipv4) Internet Protocol dotted address

+  into a proper address for the EFI_IP_ADDRESS structure.

+

+  @param[in]  Ptr    The pointer to the string containing an (Ipv4) Internet Protocol dotted address.

+  @param[out] Ip     The pointer to the Ip address structure to contain the result.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid parameter.

+**/

+EFI_STATUS

+EfiInetAddr2 (

+  IN  CHAR16            *Ptr,

+  OUT EFI_IP_ADDRESS    *Ip

+  );

+

+/**

+  Helper function called to calculate the prefix length associated with the string

+  containing an Ipv4 or Ipv6 Internet Protocol address.

+

+  @param[in]  Ptr     The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.

+  @param[out] Addr    The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid parameter.

+  @retval Others                   Other mistake case.

+**/

+EFI_STATUS

+EfiInetAddrRange (

+  IN  CHAR16                 *Ptr,

+  OUT EFI_IP_ADDRESS_INFO    *Addr

+  );

+

+/**

+  Helper function called to calculate the port range associated with the string.

+

+  @param[in]  Ptr          The pointer to the string containing a port and range.

+  @param[out] Port         The pointer to the Port to contain the result.

+  @param[out] PortRange    The pointer to the PortRange to contain the result.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid parameter.

+  @retval Others                   Other mistake case.

+**/

+EFI_STATUS

+EfiInetPortRange (

+  IN  CHAR16    *Ptr,

+  OUT UINT16    *Port,

+  OUT UINT16    *PortRange

+  );

+

+/**

+  Helper function called to transfer a string to an unsigned integer.

+

+  @param[in]  Str       The pointer to the string.

+  @param[out] Status    The operation status.

+

+  @return The integer value of a converted str.

+**/

+UINT64

+StrToUInteger (

+  IN  CONST CHAR16    *Str,

+  OUT EFI_STATUS      *Status

+  );

+

+/**

+  Helper function called to transfer a string to an unsigned integer according to the map table.

+

+  @param[in] Str    The pointer to the string.

+  @param[in] Map    The pointer to the map table.

+

+  @return The integer value of converted str. If not found, then return -1.

+**/

+UINT32

+MapStringToInteger (

+  IN CONST CHAR16    *Str,

+  IN STR2INT         *Map

+  );

+

+/**

+  Helper function called to transfer an unsigned integer to a string according to the map table.

+

+  @param[in] Integer    The pointer to the string.

+  @param[in] Map        The pointer to the map table.

+

+  @return The converted str. If not found, then return NULL.

+**/

+CHAR16 *

+MapIntegerToString (

+  IN UINT32     Integer,

+  IN STR2INT    *Map

+  );

+

+#endif

diff --git a/NetworkPkg/Application/IpsecConfig/Indexer.c b/NetworkPkg/Application/IpsecConfig/Indexer.c
new file mode 100644
index 0000000..1762bbe
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Indexer.c
@@ -0,0 +1,248 @@
+/** @file

+  The implementation of construct ENTRY_INDEXER in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecConfig.h"

+#include "Indexer.h"

+#include "Helper.h"

+

+/**

+  Fill in SPD_ENTRY_INDEXER through ParamPackage list.

+

+  @param[in, out] Indexer         The pointer to the SPD_ENTRY_INDEXER structure.

+  @param[in]      ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS    Filled in SPD_ENTRY_INDEXER successfully.

+**/

+EFI_STATUS

+ConstructSpdIndexer (

+  IN OUT SPD_ENTRY_INDEXER    *Indexer,

+  IN     LIST_ENTRY           *ParamPackage

+  )

+{

+  EFI_STATUS      Status;

+  UINT64          Value64;

+  CONST CHAR16    *ValueStr;

+

+  ValueStr = NULL;

+

+  if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");

+  } else {

+    ASSERT (FALSE);

+  }

+

+  ASSERT (ValueStr != NULL);

+

+  Value64 = StrToUInteger (ValueStr, &Status);

+  if (!EFI_ERROR (Status)) {

+    Indexer->Index = (UINTN) Value64;

+    Indexer->Name  = NULL;

+  } else {

+    UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) Indexer->Name);

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Fill in SAD_ENTRY_INDEXER through ParamPackage list.

+

+  @param[in, out] Indexer         The pointer to the SAD_ENTRY_INDEXER structure.

+  @param[in]      ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS              Filled in SPD_ENTRY_INDEXER successfully.

+  @retval EFI_INVALID_PARAMETER    The mistaken user input in ParamPackage list.

+**/

+EFI_STATUS

+ConstructSadIndexer (

+  IN OUT SAD_ENTRY_INDEXER    *Indexer,

+  IN     LIST_ENTRY           *ParamPackage

+  )

+{

+  EFI_STATUS      Status;

+  EFI_STATUS      Status1;

+  UINT64          Value64;

+  CONST CHAR16    *ValueStr;

+

+  ValueStr = NULL;

+

+  if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");

+  } else {

+    ASSERT (FALSE);

+  }

+

+  ASSERT (ValueStr != NULL);

+

+  Value64 = StrToUInteger (ValueStr, &Status);

+  if (!EFI_ERROR (Status)) {

+    Indexer->Index = (UINTN) Value64;

+    ZeroMem (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID));

+  } else {

+    if ((!ShellCommandLineGetFlag (ParamPackage, L"--lookup-spi")) ||

+        (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-ipsec-proto")) ||

+        (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-dest"))) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+        mHiiHandle,

+        mAppName,

+        L"--lookup-spi --lookup-ipsec-proto --lookup-dest"

+        );

+      return EFI_INVALID_PARAMETER;

+    }

+

+    Status = GetNumber (

+              L"--lookup-spi",

+              (UINT32) -1,

+              &Indexer->SaId.Spi,

+              sizeof (UINT32),

+              NULL,

+              ParamPackage,

+              FORMAT_NUMBER

+              );

+    Status1 = GetNumber (

+                L"--lookup-ipsec-proto",

+                0,

+                &Indexer->SaId.Proto,

+                sizeof (EFI_IPSEC_PROTOCOL_TYPE),

+                mMapIpSecProtocol,

+                ParamPackage,

+                FORMAT_STRING

+                );

+

+    if (EFI_ERROR (Status) || EFI_ERROR (Status1)) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-dest");

+    ASSERT (ValueStr != NULL);

+

+    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &Indexer->SaId.DestAddress);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+        mHiiHandle,

+        mAppName,

+        L"--lookup-dest",

+        ValueStr

+        );

+      return EFI_INVALID_PARAMETER;

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Fill in PAD_ENTRY_INDEXER through ParamPackage list.

+

+  @param[in, out] Indexer         The pointer to the PAD_ENTRY_INDEXER structure.

+  @param[in]      ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS              Filled in PAD_ENTRY_INDEXER successfully.

+  @retval EFI_INVALID_PARAMETER    The mistaken user input in ParamPackage list.

+**/

+EFI_STATUS

+ConstructPadIndexer (

+  IN OUT PAD_ENTRY_INDEXER    *Indexer,

+  IN     LIST_ENTRY           *ParamPackage

+  )

+{

+  EFI_STATUS      Status;

+  UINT64          Value64;

+  CONST CHAR16    *ValueStr;

+

+  ValueStr = NULL;

+

+  if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {

+    ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");

+  } else {

+    ASSERT (FALSE);

+  }

+

+  ASSERT (ValueStr != NULL);

+

+  Value64 = StrToUInteger (ValueStr, &Status);

+

+  if (!EFI_ERROR (Status)) {

+    Indexer->Index = (UINTN) Value64;

+    ZeroMem (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID));

+  } else {

+

+    if (ShellCommandLineGetFlag (ParamPackage, L"--lookup-peer-address")) {

+      ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-address");

+      ASSERT (ValueStr != NULL);

+

+      Indexer->PadId.PeerIdValid = FALSE;

+      Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &Indexer->PadId.Id.IpAddress);

+      if (EFI_ERROR (Status)) {

+        ShellPrintHiiEx (

+          -1,

+          -1,

+          NULL,

+          STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+          mHiiHandle,

+          mAppName,

+          L"--lookup-peer-address",

+          ValueStr

+          );

+        return EFI_INVALID_PARAMETER;

+      }

+    } else {

+      ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-id");

+      if (ValueStr == NULL) {

+        ShellPrintHiiEx (

+          -1,

+          -1,

+          NULL,

+          STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+          mHiiHandle,

+          mAppName,

+          L"--lookup-peer-address --lookup-peer-id"

+          );

+        return EFI_INVALID_PARAMETER;

+      }

+

+      Indexer->PadId.PeerIdValid = TRUE;

+      StrnCpy ((CHAR16 *) Indexer->PadId.Id.PeerId, ValueStr, ARRAY_SIZE (Indexer->PadId.Id.PeerId) - 1);

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[] = {

+  (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSpdIndexer,

+  (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSadIndexer,

+  (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructPadIndexer

+};

diff --git a/NetworkPkg/Application/IpsecConfig/Indexer.h b/NetworkPkg/Application/IpsecConfig/Indexer.h
new file mode 100644
index 0000000..078f38a
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Indexer.h
@@ -0,0 +1,58 @@
+/** @file

+  The internal structure and function declaration to construct ENTRY_INDEXER in

+  IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _INDEXER_H_

+#define _INDEXER_H_

+

+typedef struct {

+  UINT8    *Name;

+  UINTN    Index;    // Used only if Name is NULL.

+} SPD_ENTRY_INDEXER;

+

+typedef struct {

+  EFI_IPSEC_SA_ID    SaId;

+  UINTN              Index;

+} SAD_ENTRY_INDEXER;

+

+typedef struct {

+  EFI_IPSEC_PAD_ID    PadId;

+  UINTN               Index;

+} PAD_ENTRY_INDEXER;

+

+typedef union {

+  SPD_ENTRY_INDEXER    Spd;

+  SAD_ENTRY_INDEXER    Sad;

+  PAD_ENTRY_INDEXER    Pad;

+} POLICY_ENTRY_INDEXER;

+

+/**

+  The prototype for the ConstructSpdIndexer()/ConstructSadIndexer()/ConstructPadIndexer().

+  Fill in SPD_ENTRY_INDEXER/SAD_ENTRY_INDEXER/PAD_ENTRY_INDEXER through ParamPackage list.

+

+  @param[in, out] Indexer         The pointer to the POLICY_ENTRY_INDEXER union.

+  @param[in]      ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS    Filled in POLICY_ENTRY_INDEXER successfully.

+**/

+typedef

+EFI_STATUS

+(* CONSTRUCT_POLICY_ENTRY_INDEXER) (

+  IN POLICY_ENTRY_INDEXER    *Indexer,

+  IN LIST_ENTRY              *ParamPackage

+);

+

+extern CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[];

+#endif

diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.c b/NetworkPkg/Application/IpsecConfig/IpSecConfig.c
new file mode 100644
index 0000000..8006d84
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/IpSecConfig.c
@@ -0,0 +1,809 @@
+/** @file

+  The main process for IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include <Library/UefiRuntimeServicesTableLib.h>

+#include <Library/HiiLib.h>

+

+#include <Protocol/IpSec.h>

+

+#include "IpSecConfig.h"

+#include "Dump.h"

+#include "Indexer.h"

+#include "PolicyEntryOperation.h"

+#include "Delete.h"

+#include "Helper.h"

+

+//

+// Used for ShellCommandLineParseEx only

+// and to ensure user inputs are in valid format

+//

+SHELL_PARAM_ITEM    mIpSecConfigParamList[] = {

+  { L"-p",                    TypeValue },

+  { L"-a",                    TypeValue },

+  { L"-i",                    TypeValue },

+  { L"-e",                    TypeValue },

+  { L"-d",                    TypeValue },

+  { L"-f",                    TypeFlag },

+  { L"-l",                    TypeFlag },

+  { L"-enable",               TypeFlag },

+  { L"-disable",              TypeFlag },

+  { L"-status",               TypeFlag },

+  { L"-?",                    TypeFlag },

+

+  //

+  // SPD Selector

+  //

+  { L"--local",               TypeValue },

+  { L"--remote",              TypeValue },

+  { L"--proto",               TypeValue },

+  { L"--local-port",          TypeValue },

+  { L"--remote-port",         TypeValue },

+  { L"--icmp-type",           TypeValue },

+  { L"--icmp-code",           TypeValue },

+

+  //

+  // SPD Data

+  //

+  { L"--name",                TypeValue },

+  { L"--packet-flag",         TypeValue },

+  { L"--action",              TypeValue },

+  { L"--lifebyte",            TypeValue },

+  { L"--lifetime-soft",       TypeValue },

+  { L"--lifetime",            TypeValue },

+  { L"--mode",                TypeValue },

+  { L"--tunnel-local",        TypeValue },

+  { L"--tunnel-remote",       TypeValue },

+  { L"--dont-fragment",       TypeValue },

+  { L"--ipsec-proto",         TypeValue },

+  { L"--auth-algo",           TypeValue },

+  { L"--encrypt-algo",        TypeValue },

+

+  { L"--ext-sequence",        TypeFlag  },

+  { L"--sequence-overflow",   TypeFlag  },

+  { L"--fragment-check",      TypeFlag  },

+  { L"--ext-sequence-",       TypeFlag  },

+  { L"--sequence-overflow-",  TypeFlag  },

+  { L"--fragment-check-",     TypeFlag  },

+

+  //

+  // SA ID

+  // --ipsec-proto

+  //

+  { L"--spi",                 TypeValue },

+  { L"--dest",                TypeValue },

+  { L"--lookup-spi",          TypeValue },

+  { L"--lookup-ipsec-proto",  TypeValue },

+  { L"--lookup-dest",         TypeValue },

+

+  //

+  // SA DATA

+  // --mode

+  // --auth-algo

+  // --encrypt-algo

+  //

+  { L"--sequence-number",     TypeValue },

+  { L"--antireplay-window",   TypeValue },

+  { L"--auth-key",            TypeValue },

+  { L"--encrypt-key",         TypeValue },

+  { L"--path-mtu",            TypeValue },

+

+  //

+  // PAD ID

+  //

+  { L"--peer-id",             TypeValue },

+  { L"--peer-address",        TypeValue },

+  { L"--auth-proto",          TypeValue },

+  { L"--auth-method",         TypeValue },

+  { L"--ike-id",              TypeValue },

+  { L"--ike-id-",             TypeValue },

+  { L"--auth-data",           TypeValue },

+  { L"--revocation-data",     TypeValue },

+  { L"--lookup-peer-id",      TypeValue },

+  { L"--lookup-peer-address", TypeValue },

+

+  { NULL,                     TypeMax   },

+};

+

+//

+// -P

+//

+STR2INT mMapPolicy[] = {

+  { L"SPD",       IPsecConfigDataTypeSpd },

+  { L"SAD",       IPsecConfigDataTypeSad },

+  { L"PAD",       IPsecConfigDataTypePad },

+  { NULL,         0 },

+};

+

+//

+// --proto

+//

+STR2INT mMapIpProtocol[] = {

+  { L"TCP",       EFI_IP4_PROTO_TCP },

+  { L"UDP",       EFI_IP4_PROTO_UDP },

+  { L"ICMP",      EFI_IP4_PROTO_ICMP },

+  { NULL,         0 },

+};

+

+//

+// --action

+//

+STR2INT mMapIpSecAction[] = {

+  { L"Bypass",    EfiIPsecActionBypass },

+  { L"Discard",   EfiIPsecActionDiscard },

+  { L"Protect",   EfiIPsecActionProtect },

+  { NULL,         0 },

+};

+

+//

+// --mode

+//

+STR2INT mMapIpSecMode[] = {

+  { L"Transport", EfiIPsecTransport },

+  { L"Tunnel",    EfiIPsecTunnel },

+  { NULL,         0 },

+};

+

+//

+// --dont-fragment

+//

+STR2INT mMapDfOption[] = {

+  { L"clear",     EfiIPsecTunnelClearDf },

+  { L"set",       EfiIPsecTunnelSetDf },

+  { L"copy",      EfiIPsecTunnelCopyDf },

+  { NULL,         0 },

+};

+

+//

+// --ipsec-proto

+//

+STR2INT mMapIpSecProtocol[] = {

+  { L"AH",        EfiIPsecAH },

+  { L"ESP",       EfiIPsecESP },

+  { NULL,         0 },

+};

+

+//

+// --auth-algo

+//

+STR2INT mMapAuthAlgo[] = {

+  { L"NONE",         EFI_IPSEC_AALG_NONE },

+  { L"MD5HMAC",      EFI_IPSEC_AALG_MD5HMAC },

+  { L"SHA1HMAC",     EFI_IPSEC_AALG_SHA1HMAC },

+  { L"SHA2-256HMAC", EFI_IPSEC_AALG_SHA2_256HMAC },

+  { L"SHA2-384HMAC", EFI_IPSEC_AALG_SHA2_384HMAC },

+  { L"SHA2-512HMAC", EFI_IPSEC_AALG_SHA2_512HMAC },

+  { L"AES-XCBC-MAC", EFI_IPSEC_AALG_AES_XCBC_MAC },

+  { L"NULL",         EFI_IPSEC_AALG_NULL },

+  { NULL,            0 },

+};

+

+//

+// --encrypt-algo

+//

+STR2INT mMapEncAlgo[] = {

+  { L"NONE",         EFI_IPSEC_EALG_NONE },

+  { L"DESCBC",       EFI_IPSEC_EALG_DESCBC },

+  { L"3DESCBC",      EFI_IPSEC_EALG_3DESCBC },

+  { L"CASTCBC",      EFI_IPSEC_EALG_CASTCBC },

+  { L"BLOWFISHCBC",  EFI_IPSEC_EALG_BLOWFISHCBC },

+  { L"NULL",         EFI_IPSEC_EALG_NULL },

+  { L"AESCBC",       EFI_IPSEC_EALG_AESCBC },

+  { L"AESCTR",       EFI_IPSEC_EALG_AESCTR },

+  { L"AES-CCM-ICV8", EFI_IPSEC_EALG_AES_CCM_ICV8 },

+  { L"AES-CCM-ICV12",EFI_IPSEC_EALG_AES_CCM_ICV12 },

+  { L"AES-CCM-ICV16",EFI_IPSEC_EALG_AES_CCM_ICV16 },

+  { L"AES-GCM-ICV8", EFI_IPSEC_EALG_AES_GCM_ICV8 },

+  { L"AES-GCM-ICV12",EFI_IPSEC_EALG_AES_GCM_ICV12 },

+  { L"AES-GCM-ICV16",EFI_IPSEC_EALG_AES_GCM_ICV16 },

+  { NULL,            0 },

+};

+

+//

+// --auth-proto

+//

+STR2INT mMapAuthProto[] = {

+  { L"IKEv1",        EfiIPsecAuthProtocolIKEv1 },

+  { L"IKEv2",        EfiIPsecAuthProtocolIKEv2 },

+  { NULL,            0 },

+};

+

+//

+// --auth-method

+//

+STR2INT mMapAuthMethod[] = {

+  { L"PreSharedSecret", EfiIPsecAuthMethodPreSharedSecret },

+  { L"Certificates",    EfiIPsecAuthMethodCertificates },

+  { NULL,               0 },

+};

+

+EFI_IPSEC_PROTOCOL           *mIpSec;

+EFI_IPSEC_CONFIG_PROTOCOL    *mIpSecConfig;

+EFI_HII_HANDLE               mHiiHandle;

+EFI_GUID                     mEfiIpSecConfigGuid = EFI_IPSEC_CONFIG_GUID;

+CHAR16                       mAppName[]          = L"IpSecConfig";

+

+//

+// Used for IpSecConfigRetriveCheckListByName only to check the validation of user input

+//

+VAR_CHECK_ITEM    mIpSecConfigVarCheckList[] = {

+  { L"-enable",              BIT(1)|BIT(0),  BIT(1),  BIT(2)|BIT(1)|BIT(0), 0 },

+  { L"-disable",             BIT(1)|BIT(0),  BIT(1),  BIT(2)|BIT(1)|BIT(0), 0 },

+  { L"-status",              BIT(1)|BIT(0),  BIT(1),  BIT(2)|BIT(1)|BIT(0), 0 },

+  { L"-p",                   BIT(1),         0,       BIT(2)|BIT(1)|BIT(0), 0 },

+

+  { L"-a",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },

+  { L"-i",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },

+  { L"-d",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },

+  { L"-e",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },

+  { L"-l",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },

+  { L"-f",                   BIT(0),         0,       BIT(2)|BIT(1)|BIT(0), 0 },

+

+  { L"-?",                   BIT(0),         BIT(0),  BIT(2)|BIT(1)|BIT(0), 0 },

+

+  //

+  // SPD Selector

+  //

+  { L"--local",              0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--remote",             0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--proto",              0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--local-port",         0,              0,       BIT(2)|BIT(1),        BIT(0) },

+  { L"--remote-port",        0,              0,       BIT(2)|BIT(1),        BIT(0) },

+  { L"--icmp-type",          0,              0,       BIT(2)|BIT(1),        BIT(1) },

+  { L"--icmp-code",          0,              0,       BIT(2)|BIT(1),        BIT(1) },

+

+  //

+  // SPD Data

+  //

+  { L"--name",               0,              0,       BIT(2),               0 },

+  { L"--packet-flag",        0,              0,       BIT(2),               0 },

+  { L"--action",             0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--lifebyte",           0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--lifetime-soft",      0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--lifetime",           0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--mode",               0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--tunnel-local",       0,              0,       BIT(2),               0 },

+  { L"--tunnel-remote",      0,              0,       BIT(2),               0 },

+  { L"--dont-fragment",      0,              0,       BIT(2),               0 },

+  { L"--ipsec-proto",        0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--auth-algo",          0,              0,       BIT(2)|BIT(1),        0 },

+  { L"--encrypt-algo",       0,              0,       BIT(2)|BIT(1),        0 },

+

+  { L"--ext-sequence",       0,              0,       BIT(2),               BIT(2) },

+  { L"--sequence-overflow",  0,              0,       BIT(2),               BIT(2) },

+  { L"--fragment-check",     0,              0,       BIT(2),               BIT(2) },

+  { L"--ext-sequence-",      0,              0,       BIT(2),               BIT(3) },

+  { L"--sequence-overflow-", 0,              0,       BIT(2),               BIT(3) },

+  { L"--fragment-check-",    0,              0,       BIT(2),               BIT(3) },

+

+  //

+  // SA ID

+  // --ipsec-proto

+  //

+  { L"--spi",                0,              0,       BIT(1),               0 },

+  { L"--dest",               0,              0,       BIT(1),               0 },

+  { L"--lookup-spi",         0,              0,       BIT(1),               0 },

+  { L"--lookup-ipsec-proto", 0,              0,       BIT(1),               0 },

+  { L"--lookup-dest",        0,              0,       BIT(1),               0 },

+

+  //

+  // SA DATA

+  // --mode

+  // --auth-algo

+  // --encrypt-algo

+  //

+  { L"--sequence-number",    0,              0,       BIT(1),               0 },

+  { L"--antireplay-window",  0,              0,       BIT(1),               0 },

+  { L"--auth-key",           0,              0,       BIT(1),               0 },

+  { L"--encrypt-key",        0,              0,       BIT(1),               0 },

+  { L"--path-mtu",           0,              0,       BIT(1),               0 },

+

+  //

+  // The example to add a PAD:

+  // "-A --peer-id Mike [--peer-address 10.23.2.2] --auth-proto IKE1/IKE2

+  //     --auth-method PreSharedSeceret/Certificate --ike-id

+  //     --auth-data 343343 --revocation-data 2342432"

+  // The example to delete a PAD:

+  // "-D * --lookup-peer-id Mike [--lookup-peer-address 10.23.2.2]"

+  // "-D 1"

+  // The example to edit a PAD:

+  // "-E * --lookup-peer-id Mike --auth-method Certificate"

+

+  //

+  // PAD ID

+  //

+  { L"--peer-id",            0,              0,       BIT(0),               BIT(4) },

+  { L"--peer-address",       0,              0,       BIT(0),               BIT(5) },

+  { L"--auth-proto",         0,              0,       BIT(0),               0 },

+  { L"--auth-method",        0,              0,       BIT(0),               0 },

+  { L"--IKE-ID",             0,              0,       BIT(0),               BIT(6) },

+  { L"--IKE-ID-",            0,              0,       BIT(0),               BIT(7) },

+  { L"--auth-data",          0,              0,       BIT(0),               0 },

+  { L"--revocation-data",    0,              0,       BIT(0),               0 },

+  { L"--lookup-peer-id",     0,              0,       BIT(0),               BIT(4) },

+  { L"--lookup-peer-address",0,              0,       BIT(0),               BIT(5) },

+

+  { NULL,                    0,              0,       0,                    0 },

+};

+

+/**

+  The function to allocate the proper sized buffer for various

+  EFI interfaces.

+

+  @param[in, out] Status        Current status.

+  @param[in, out] Buffer        Current allocated buffer, or NULL.

+  @param[in]      BufferSize    Current buffer size needed

+

+  @retval TRUE     If the buffer was reallocated and the caller should try the API again.

+  @retval FALSE    If the buffer was not reallocated successfully.

+**/

+BOOLEAN

+GrowBuffer (

+  IN OUT EFI_STATUS    *Status,

+  IN OUT VOID          **Buffer,

+  IN     UINTN         BufferSize

+  )

+{

+  BOOLEAN    TryAgain;

+

+  ASSERT (Status != NULL);

+  ASSERT (Buffer != NULL);

+

+  //

+  // If this is an initial request, buffer will be null with a new buffer size.

+  //

+  if ((NULL == *Buffer) && (BufferSize != 0)) {

+    *Status = EFI_BUFFER_TOO_SMALL;

+  }

+

+  //

+  // If the status code is "buffer too small", resize the buffer.

+  //

+  TryAgain = FALSE;

+  if (*Status == EFI_BUFFER_TOO_SMALL) {

+

+    if (*Buffer != NULL) {

+      FreePool (*Buffer);

+    }

+

+    *Buffer = AllocateZeroPool (BufferSize);

+

+    if (*Buffer != NULL) {

+      TryAgain = TRUE;

+    } else {

+      *Status = EFI_OUT_OF_RESOURCES;

+    }

+  }

+

+  //

+  // If there's an error, free the buffer.

+  //

+  if (!TryAgain && EFI_ERROR (*Status) && (*Buffer != NULL)) {

+    FreePool (*Buffer);

+    *Buffer = NULL;

+  }

+

+  return TryAgain;

+}

+

+/**

+  Function returns an array of handles that support the requested protocol

+  in a buffer allocated from a pool.

+

+  @param[in]      SearchType    Specifies which handle(s) are to be returned.

+  @param[in]      Protocol      Provides the protocol to search by.

+                                This parameter is only valid for SearchType ByProtocol.

+

+  @param[in]      SearchKey     Supplies the search key depending on the SearchType.

+  @param[in, out] NoHandles     The number of handles returned in Buffer.

+  @param[out]     Buffer        A pointer to the buffer to return the requested array of

+                                handles that support Protocol.

+

+  @retval EFI_SUCCESS    The resulting array of handles was returned.

+  @retval Others         Other mistake case.

+**/

+EFI_STATUS

+LocateHandle (

+  IN     EFI_LOCATE_SEARCH_TYPE    SearchType,

+  IN     EFI_GUID                  *Protocol  OPTIONAL,

+  IN     VOID                      *SearchKey OPTIONAL,

+  IN OUT UINTN                     *NoHandles,

+     OUT EFI_HANDLE                **Buffer

+  )

+{

+  EFI_STATUS    Status;

+  UINTN         BufferSize;

+

+  ASSERT (NoHandles != NULL);

+  ASSERT (Buffer != NULL);

+

+  //

+  // Initialize for GrowBuffer loop.

+  //

+  Status      = EFI_SUCCESS;

+  *Buffer     = NULL;

+  BufferSize  = 50 * sizeof (EFI_HANDLE);

+

+  //

+  // Call the real function.

+  //

+  while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {

+    Status = gBS->LocateHandle (

+                    SearchType,

+                    Protocol,

+                    SearchKey,

+                    &BufferSize,

+                    *Buffer

+                    );

+  }

+

+  *NoHandles = BufferSize / sizeof (EFI_HANDLE);

+  if (EFI_ERROR (Status)) {

+    *NoHandles = 0;

+  }

+

+  return Status;

+}

+

+/**

+  Find the first instance of this protocol in the system and return its interface.

+

+  @param[in]  ProtocolGuid    The guid of the protocol.

+  @param[out] Interface       The pointer to the first instance of the protocol.

+

+  @retval EFI_SUCCESS    A protocol instance matching ProtocolGuid was found.

+  @retval Others         A protocol instance matching ProtocolGuid was not found.

+**/

+EFI_STATUS

+LocateProtocol (

+  IN  EFI_GUID    *ProtocolGuid,

+  OUT VOID        **Interface

+  )

+

+{

+  EFI_STATUS    Status;

+  UINTN         NumberHandles;

+  UINTN         Index;

+  EFI_HANDLE    *Handles;

+

+  *Interface    = NULL;

+  Handles       = NULL;

+  NumberHandles = 0;

+

+  Status        = LocateHandle (ByProtocol, ProtocolGuid, NULL, &NumberHandles, &Handles);

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_INFO, "LibLocateProtocol: Handle not found\n"));

+    return Status;

+  }

+

+  for (Index = 0; Index < NumberHandles; Index++) {

+    ASSERT (Handles != NULL);

+    Status = gBS->HandleProtocol (

+                    Handles[Index],

+                    ProtocolGuid,

+                    Interface

+                    );

+

+    if (!EFI_ERROR (Status)) {

+      break;

+    }

+  }

+

+  if (Handles != NULL) {

+    FreePool (Handles);

+  }

+

+  return Status;

+}

+

+/**

+  Helper function called to check the conflicted flags.

+

+  @param[in] CheckList       The pointer to the VAR_CHECK_ITEM table.

+  @param[in] ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS              No conflicted flags.

+  @retval EFI_INVALID_PARAMETER    The input parameter is erroroneous or there are some conflicted flags.

+**/

+EFI_STATUS

+IpSecConfigRetriveCheckListByName (

+  IN VAR_CHECK_ITEM    *CheckList,

+  IN LIST_ENTRY        *ParamPackage

+)

+{

+

+  LIST_ENTRY        *Node;

+  VAR_CHECK_ITEM    *Item;

+  UINT32            Attribute1;

+  UINT32            Attribute2;

+  UINT32            Attribute3;

+  UINT32            Attribute4;

+  UINT32            Index;

+

+  Attribute1 = 0;

+  Attribute2 = 0;

+  Attribute3 = 0;

+  Attribute4 = 0;

+  Index      = 0;

+  Item       = mIpSecConfigVarCheckList;

+

+  if ((ParamPackage == NULL) || (CheckList == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Enumerate through the list of parameters that are input by user.

+  //

+  for (Node = GetFirstNode (ParamPackage); !IsNull (ParamPackage, Node); Node = GetNextNode (ParamPackage, Node)) {

+    if (((SHELL_PARAM_PACKAGE *) Node)->Name != NULL) {

+      //

+	  // Enumerate the check list that defines the conflicted attributes of each flag.

+      //

+      for (; Item->VarName != NULL; Item++) {

+        if (StrCmp (((SHELL_PARAM_PACKAGE *) Node)->Name, Item->VarName) == 0) {

+          Index++;

+          if (Index == 1) {

+            Attribute1 = Item->Attribute1;

+            Attribute2 = Item->Attribute2;

+            Attribute3 = Item->Attribute3;

+            Attribute4 = Item->Attribute4;

+          } else {

+            Attribute1 &= Item->Attribute1;

+            Attribute2 |= Item->Attribute2;

+            Attribute3 &= Item->Attribute3;

+            Attribute4 |= Item->Attribute4;

+            if (Attribute1 != 0) {

+              return EFI_INVALID_PARAMETER;

+            }

+

+            if (Attribute2 != 0) {

+              if ((Index == 2) && (StrCmp (Item->VarName, L"-p") == 0)) {

+                continue;

+              }

+

+              return EFI_INVALID_PARAMETER;

+            }

+

+            if (Attribute3 == 0) {

+              return EFI_INVALID_PARAMETER;

+            }

+            if (((Attribute4 & 0xFF) == 0x03) || ((Attribute4 & 0xFF) == 0x0C) ||

+                ((Attribute4 & 0xFF) == 0x30) || ((Attribute4 & 0xFF) == 0xC0)) {

+              return EFI_INVALID_PARAMETER;

+            }

+          }

+          break;

+        }

+      }

+

+      Item = mIpSecConfigVarCheckList;

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This is the declaration of an EFI image entry point. This entry point is

+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including

+  both device drivers and bus drivers.

+

+  The entry point for IpSecConfig application that parse the command line input and call an IpSecConfig process.

+

+  @param[in] ImageHandle    The image handle of this application.

+  @param[in] SystemTable    The pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS    The operation completed successfully.

+

+**/

+EFI_STATUS

+EFIAPI

+InitializeIpSecConfig (

+  IN EFI_HANDLE          ImageHandle,

+  IN EFI_SYSTEM_TABLE    *SystemTable

+  )

+{

+  EFI_STATUS                    Status;

+  EFI_IPSEC_CONFIG_DATA_TYPE    DataType;

+  UINT8                         Value;

+  LIST_ENTRY                    *ParamPackage;

+  CONST CHAR16                  *ValueStr;

+  CHAR16                        *ProblemParam;

+  UINTN                         NonOptionCount;

+

+  //

+  // Register our string package with HII and return the handle to it.

+  //

+  mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IpSecConfigStrings, NULL);

+  ASSERT (mHiiHandle != NULL);

+

+  Status = ShellCommandLineParseEx (mIpSecConfigParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);

+  if (EFI_ERROR (Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, ProblemParam);

+    goto Done;

+  }

+

+  Status = IpSecConfigRetriveCheckListByName (mIpSecConfigVarCheckList, ParamPackage);

+  if (EFI_ERROR (Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_MISTAKEN_OPTIONS), mHiiHandle);

+    goto Done;

+  }

+

+  Status = LocateProtocol (&gEfiIpSecConfigProtocolGuid, (VOID **) &mIpSecConfig);

+  if (EFI_ERROR (Status) || mIpSecConfig == NULL) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName);

+    goto Done;

+  }

+

+  Status = LocateProtocol (&gEfiIpSecProtocolGuid, (VOID **) &mIpSec);

+  if (EFI_ERROR (Status) || mIpSec == NULL) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName);

+    goto Done;

+  }

+

+  //

+  // Enable IPsec.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"-enable")) {

+    if (!(mIpSec->DisabledFlag)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_ENABLE), mHiiHandle, mAppName);

+    } else {

+      //

+      // Set enable flag.

+      //

+      Value  = IPSEC_STATUS_ENABLED;

+      Status = gRT->SetVariable (

+                      IPSECCONFIG_STATUS_NAME,

+                      &gEfiIpSecConfigProtocolGuid,

+                      EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,

+                      sizeof (Value),

+                      &Value

+                      );

+      if (!EFI_ERROR (Status)) {

+        mIpSec->DisabledFlag = FALSE;

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_SUCCESS), mHiiHandle, mAppName);

+      } else {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_FAILED), mHiiHandle, mAppName);

+      }

+    }

+

+    goto Done;

+  }

+

+  //

+  // Disable IPsec.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"-disable")) {

+    if (mIpSec->DisabledFlag) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_DISABLE), mHiiHandle, mAppName);

+    } else {

+      //

+      // Set disable flag; however, leave it to be disabled in the callback function of DisabledEvent.

+      //

+      gBS->SignalEvent (mIpSec->DisabledEvent);

+      if (mIpSec->DisabledFlag) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_SUCCESS), mHiiHandle, mAppName);

+      } else {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_FAILED), mHiiHandle, mAppName);

+      }

+    }

+

+    goto Done;

+  }

+

+  //

+  //IPsec Status.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"-status")) {

+    if (mIpSec->DisabledFlag) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_DISABLE), mHiiHandle, mAppName);

+    } else {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_ENABLE), mHiiHandle, mAppName);

+    }

+

+    goto Done;

+  }

+

+  //

+  // Try to get policy database type.

+  //

+  DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) -1;

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-p");

+  if (ValueStr != NULL) {

+    DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) MapStringToInteger (ValueStr, mMapPolicy);

+    if (DataType == -1) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle, mAppName, ValueStr);

+      goto Done;

+    }

+  }

+

+  if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {

+    switch (DataType) {

+      case (EFI_IPSEC_CONFIG_DATA_TYPE) -1:

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_HELP), mHiiHandle);

+        break;

+

+      case IPsecConfigDataTypeSpd:

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SPD_HELP), mHiiHandle);

+        break;

+

+      case IPsecConfigDataTypeSad:

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SAD_HELP), mHiiHandle);

+        break;

+

+      case IPsecConfigDataTypePad:

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PAD_HELP), mHiiHandle);

+        break;

+

+      default:

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle);

+        break;

+    }

+

+    goto Done;

+  }

+

+  NonOptionCount = ShellCommandLineGetCount ();

+  if ((NonOptionCount - 1) > 0) {

+    ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32) (NonOptionCount - 1));

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_REDUNDANCY_MANY), mHiiHandle, mAppName, ValueStr);

+    goto Done;

+  }

+

+  if (DataType == -1) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_DB), mHiiHandle, mAppName);

+    goto Done;

+  }

+

+  if (ShellCommandLineGetFlag (ParamPackage, L"-a")) {

+    Status = AddOrInsertPolicyEntry (DataType, ParamPackage);

+    if (EFI_ERROR (Status)) {

+      goto Done;

+    }

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {

+    Status = AddOrInsertPolicyEntry (DataType, ParamPackage);

+    if (EFI_ERROR (Status)) {

+      goto Done;

+    }

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {

+    Status = EditPolicyEntry (DataType, ParamPackage);

+    if (EFI_ERROR (Status)) {

+      goto Done;

+    }

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {

+    Status = FlushOrDeletePolicyEntry (DataType, ParamPackage);

+    if (EFI_ERROR (Status)) {

+      goto Done;

+    }

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-f")) {

+    Status = FlushOrDeletePolicyEntry (DataType, ParamPackage);

+    if (EFI_ERROR (Status)) {

+      goto Done;

+    }

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"-l")) {

+    Status = ListPolicyEntry (DataType, ParamPackage);

+    if (EFI_ERROR (Status)) {

+      goto Done;

+    }

+  } else {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, mAppName);

+    goto Done;

+  }

+

+Done:

+  ShellCommandLineFreeVarList (ParamPackage);

+  HiiRemovePackages (mHiiHandle);

+

+  return EFI_SUCCESS;

+}

diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.h b/NetworkPkg/Application/IpsecConfig/IpSecConfig.h
new file mode 100644
index 0000000..d1a7681
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/IpSecConfig.h
@@ -0,0 +1,123 @@
+/** @file

+  The internal structure and function declaration in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _IPSEC_CONFIG_H_

+#define _IPSEC_CONFIG_H_

+

+#include <Library/BaseMemoryLib.h>

+#include <Library/UefiLib.h>

+#include <Library/ShellLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/NetLib.h>

+

+#include <Protocol/IpSecConfig.h>

+

+#define EFI_IPSEC_CONFIG_GUID \

+  { \

+    0x9db0c3ac, 0xd9d2, 0x4f96, {0x9e, 0xd7, 0x6d, 0xa6, 0x12, 0xa4, 0xf3, 0x27} \

+  }

+

+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))

+

+#define IPSECCONFIG_STATUS_NAME    L"IpSecStatus"

+

+#define BIT(x)   (UINT32) (1 << (x))

+

+#define IPSEC_STATUS_DISABLED    0x0

+#define IPSEC_STATUS_ENABLED     0x1

+

+#define EFI_IP4_PROTO_ICMP       0x1

+#define EFI_IP4_PROTO_TCP        0x6

+#define EFI_IP4_PROTO_UDP        0x11

+

+#define EFI_IPSEC_ANY_PROTOCOL    0xFFFF

+#define EFI_IPSEC_ANY_PORT        0

+

+typedef struct _VAR_CHECK_ITEM {

+  CHAR16      *VarName;

+  UINT32      Attribute1;

+  UINT32      Attribute2;

+  UINT32      Attribute3;

+  UINT32      Attribute4;

+} VAR_CHECK_ITEM;

+

+typedef struct _SHELL_PARAM_PACKAGE{

+  LIST_ENTRY     Link;

+  CHAR16         *Name;

+  ParamType      Type;

+  CHAR16         *Value;

+  UINTN          OriginalPosition;

+} SHELL_PARAM_PACKAGE;

+

+typedef struct _STR2INT {

+  CHAR16        *String;

+  UINT32        Integer;

+} STR2INT;

+

+extern EFI_IPSEC_CONFIG_PROTOCOL    *mIpSecConfig;

+extern EFI_HII_HANDLE               mHiiHandle;

+extern CHAR16                       mAppName[];

+

+//

+// -P

+//

+extern STR2INT mMapPolicy[];

+

+//

+// --proto

+//

+extern STR2INT mMapIpProtocol[];

+

+//

+// --action

+//

+extern STR2INT mMapIpSecAction[];

+

+//

+// --mode

+//

+extern STR2INT mMapIpSecMode[];

+

+//

+// --dont-fragment

+//

+extern STR2INT mMapDfOption[];

+

+//

+// --ipsec-proto

+//

+extern STR2INT mMapIpSecProtocol[];

+//

+// --auth-algo

+//

+extern STR2INT mMapAuthAlgo[];

+

+//

+// --encrypt-algo

+//

+extern STR2INT mMapEncAlgo[];

+//

+// --auth-proto

+//

+extern STR2INT mMapAuthProto[];

+

+//

+// --auth-method

+//

+extern STR2INT mMapAuthMethod[];

+

+#endif

diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf b/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf
new file mode 100644
index 0000000..1e0d4f4
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf
@@ -0,0 +1,61 @@
+## @file

+#  Component description file for IpSecConfig6 application.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010006

+  BASE_NAME                      = IpSecConfig

+  FILE_GUID                      = 0922E604-F5EC-42ef-980D-A35E9A2B1844

+  MODULE_TYPE                    = UEFI_APPLICATION

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = InitializeIpSecConfig

+

+[Sources]

+  IpSecConfigStrings.uni

+  IpSecConfig.c

+  IpSecConfig.h

+  Dump.c

+  Dump.h

+  Indexer.c

+  Indexer.h

+  Match.c

+  Match.h

+  Delete.h

+  Delete.c

+  Helper.c

+  Helper.h

+  ForEach.c

+  ForEach.h

+  PolicyEntryOperation.c

+  PolicyEntryOperation.h

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+  ShellPkg/ShellPkg.dec

+

+[LibraryClasses]

+  UefiBootServicesTableLib

+  UefiApplicationEntryPoint

+  BaseMemoryLib

+  ShellLib

+  MemoryAllocationLib

+  DebugLib

+  HiiLib

+  NetLib

+  UefiLib

+

+[Protocols]

+  gEfiIpSecProtocolGuid                         ##CONSUMS

+  gEfiIpSecConfigProtocolGuid                   ##CONSUMS

diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni b/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni
new file mode 100644
index 0000000..fb0e27d
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni
Binary files differ
diff --git a/NetworkPkg/Application/IpsecConfig/Match.c b/NetworkPkg/Application/IpsecConfig/Match.c
new file mode 100644
index 0000000..d6595ee
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Match.c
@@ -0,0 +1,163 @@
+/** @file

+  The implementation of match policy entry function in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecConfig.h"

+#include "Indexer.h"

+#include "Match.h"

+

+/**

+  Private function to validate a buffer that should be filled with zero.

+

+  @param[in] Memory    The pointer to the buffer.

+  @param[in] Size      The size of the buffer.

+

+  @retval TRUE     The memory is filled with zero.

+  @retval FALSE    The memory isn't filled with zero.

+**/

+BOOLEAN

+IsMemoryZero (

+  IN VOID     *Memory,

+  IN UINTN    Size

+  )

+{

+  UINTN    Index;

+

+  for (Index = 0; Index < Size; Index++) {

+    if (*((UINT8 *) Memory + Index) != 0) {

+      return FALSE;

+    }

+  }

+

+  return TRUE;

+}

+

+/**

+  Find the matching SPD with Indexer.

+

+  @param[in] Selector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.

+  @param[in] Data        The pointer to the EFI_IPSEC_SPD_DATA structure.

+  @param[in] Indexer     The pointer to the SPD_ENTRY_INDEXER structure.

+

+  @retval TRUE     The matched SPD is found.

+  @retval FALSE    The matched SPD is not found.

+**/

+BOOLEAN

+MatchSpdEntry (

+  IN EFI_IPSEC_SPD_SELECTOR    *Selector,

+  IN EFI_IPSEC_SPD_DATA        *Data,

+  IN SPD_ENTRY_INDEXER         *Indexer

+  )

+{

+  BOOLEAN    Match;

+

+  Match = FALSE;

+  if (Indexer->Name != NULL) {

+    if ((Data->Name != NULL) && (AsciiStrCmp ((CHAR8 *) Indexer->Name, (CHAR8 *) Data->Name) == 0)) {

+      Match = TRUE;

+    }

+  } else {

+    if (Indexer->Index == 0) {

+      Match = TRUE;

+    }

+

+    Indexer->Index--;

+  }

+

+  return Match;

+}

+

+/**

+  Find the matching SAD with Indexer.

+

+  @param[in] SaId       The pointer to the EFI_IPSEC_SA_ID structure.

+  @param[in] Data       The pointer to the EFI_IPSEC_SA_DATA structure.

+  @param[in] Indexer    The pointer to the SPD_ENTRY_INDEXER structure.

+

+  @retval TRUE     The matched SAD is found.

+  @retval FALSE    The matched SAD is not found.

+**/

+BOOLEAN

+MatchSadEntry (

+  IN EFI_IPSEC_SA_ID      *SaId,

+  IN EFI_IPSEC_SA_DATA    *Data,

+  IN SAD_ENTRY_INDEXER    *Indexer

+  )

+{

+  BOOLEAN    Match;

+

+  Match = FALSE;

+  if (!IsMemoryZero (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID))) {

+    Match = (BOOLEAN) (CompareMem (&Indexer->SaId, SaId, sizeof (EFI_IPSEC_SA_ID)) == 0);

+  } else {

+    if (Indexer->Index == 0) {

+      Match = TRUE;

+    }

+    Indexer->Index--;

+  }

+

+  return Match;

+}

+

+/**

+  Find the matching PAD with Indexer.

+

+  @param[in] PadId      The pointer to the EFI_IPSEC_PAD_ID structure.

+  @param[in] Data       The pointer to the EFI_IPSEC_PAD_DATA structure.

+  @param[in] Indexer    The pointer to the SPD_ENTRY_INDEXER structure.

+

+  @retval TRUE     The matched PAD is found.

+  @retval FALSE    The matched PAD is not found.

+**/

+BOOLEAN

+MatchPadEntry (

+  IN EFI_IPSEC_PAD_ID      *PadId,

+  IN EFI_IPSEC_PAD_DATA    *Data,

+  IN PAD_ENTRY_INDEXER     *Indexer

+  )

+{

+  BOOLEAN                       Match;

+

+  Match = FALSE;

+  if (!IsMemoryZero (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID))) {

+    Match = (BOOLEAN) ((Indexer->PadId.PeerIdValid == PadId->PeerIdValid) &&

+                       ((PadId->PeerIdValid &&

+                         (StrCmp (

+                            (CONST CHAR16 *) Indexer->PadId.Id.PeerId,

+                            (CONST CHAR16 *) PadId->Id.PeerId

+                            ) == 0)) ||

+                        ((!PadId->PeerIdValid) &&

+                         (Indexer->PadId.Id.IpAddress.PrefixLength == PadId->Id.IpAddress.PrefixLength) &&

+                         (CompareMem (

+                            &Indexer->PadId.Id.IpAddress.Address,

+                            &PadId->Id.IpAddress.Address,

+                            sizeof (EFI_IP_ADDRESS)

+                            ) == 0))));

+  } else {

+    if (Indexer->Index == 0) {

+      Match = TRUE;

+    }

+

+    Indexer->Index--;

+  }

+

+  return Match;

+}

+

+MATCH_POLICY_ENTRY mMatchPolicyEntry[] = {

+  (MATCH_POLICY_ENTRY) MatchSpdEntry,

+  (MATCH_POLICY_ENTRY) MatchSadEntry,

+  (MATCH_POLICY_ENTRY) MatchPadEntry

+};

+

diff --git a/NetworkPkg/Application/IpsecConfig/Match.h b/NetworkPkg/Application/IpsecConfig/Match.h
new file mode 100644
index 0000000..1d73c5c
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Match.h
@@ -0,0 +1,41 @@
+/** @file

+  The internal structure and function declaration of 

+  match policy entry function in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _MATCH_H_

+#define _MATCH_H_

+

+/**

+  The prototype for the MatchSpdEntry()/MatchSadEntry()/MatchPadEntry().

+  The functionality is to find the matching SPD/SAD/PAD with Indexer.

+

+  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.

+  @param[in] Data        The pointer to corresponding Data.

+  @param[in] Indexer     The pointer to the POLICY_ENTRY_INDEXER union.

+

+  @retval TRUE     The matched SPD/SAD/PAD is found.

+  @retval FALSE    The matched SPD/SAD/PAD is not found.

+**/

+typedef

+BOOLEAN

+(* MATCH_POLICY_ENTRY) (

+  IN EFI_IPSEC_CONFIG_SELECTOR    *Selector,

+  IN VOID                         *Data,

+  IN POLICY_ENTRY_INDEXER         *Indexer

+  );

+

+extern MATCH_POLICY_ENTRY mMatchPolicyEntry[];

+

+#endif

diff --git a/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c
new file mode 100644
index 0000000..ddfbb4c
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c
@@ -0,0 +1,2016 @@
+/** @file

+  The implementation of policy entry operation function in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecConfig.h"

+#include "Indexer.h"

+#include "Match.h"

+#include "Helper.h"

+#include "ForEach.h"

+#include "PolicyEntryOperation.h"

+

+/**

+  Fill in EFI_IPSEC_SPD_SELECTOR through ParamPackage list.

+

+  @param[out]     Selector        The pointer to the EFI_IPSEC_SPD_SELECTOR structure.

+  @param[in]      ParamPackage    The pointer to the ParamPackage list.

+  @param[in, out] ParamPackage    The pointer to the Mask.

+

+  @retval EFI_SUCCESS              Fill in EFI_IPSEC_SPD_SELECTOR successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

+

+**/

+EFI_STATUS

+CreateSpdSelector (

+     OUT EFI_IPSEC_SPD_SELECTOR    *Selector,

+  IN     LIST_ENTRY                *ParamPackage,

+  IN OUT UINT32                    *Mask

+  )

+{

+  EFI_STATUS      Status;

+  EFI_STATUS      ReturnStatus;

+  CONST CHAR16    *ValueStr;

+

+  Status       = EFI_SUCCESS;

+  ReturnStatus = EFI_SUCCESS;

+

+  //

+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local");

+  if (ValueStr != NULL) {

+    Selector->LocalAddressCount = 1;

+    Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->LocalAddress);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+        mHiiHandle,

+        mAppName,

+        L"--local",

+        ValueStr

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else {

+      *Mask |= LOCAL;

+    }

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote");

+  if (ValueStr != NULL) {

+    Selector->RemoteAddressCount = 1;

+    Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->RemoteAddress);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+        mHiiHandle,

+        mAppName,

+        L"--remote",

+        ValueStr

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else {

+      *Mask |= REMOTE;

+    }

+  }

+

+  Selector->NextLayerProtocol = EFI_IPSEC_ANY_PROTOCOL;

+

+  //

+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.

+  //

+  Status = GetNumber (

+             L"--proto",

+             (UINT16) -1,

+             &Selector->NextLayerProtocol,

+             sizeof (UINT16),

+             mMapIpProtocol,

+             ParamPackage,

+             FORMAT_NUMBER | FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= PROTO;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Selector->LocalPort  = EFI_IPSEC_ANY_PORT;

+  Selector->RemotePort = EFI_IPSEC_ANY_PORT;

+

+  //

+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local-port");

+  if (ValueStr != NULL) {

+    Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->LocalPort, &Selector->LocalPortRange);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+        mHiiHandle,

+        mAppName,

+        L"--local-port",

+        ValueStr

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else {

+      *Mask |= LOCAL_PORT;

+    }

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote-port");

+  if (ValueStr != NULL) {

+    Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->RemotePort, &Selector->RemotePortRange);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+        mHiiHandle,

+        mAppName,

+        L"--remote-port",

+        ValueStr

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else {

+      *Mask |= REMOTE_PORT;

+    }

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.

+  //

+  Status = GetNumber (

+             L"--icmp-type",

+             (UINT8) -1,

+             &Selector->LocalPort,

+             sizeof (UINT16),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= ICMP_TYPE;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.

+  //

+  Status = GetNumber (

+             L"--icmp-code",

+             (UINT8) -1,

+             &Selector->RemotePort,

+             sizeof (UINT16),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= ICMP_CODE;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  return ReturnStatus;

+}

+

+/**

+  Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA through ParamPackage list.

+

+  @param[out] Selector        The pointer to the EFI_IPSEC_SPD_SELECTOR structure.

+  @param[out] Data            The pointer to the EFI_IPSEC_SPD_DATA structure.

+  @param[in]  ParamPackage    The pointer to the ParamPackage list.

+  @param[out] Mask            The pointer to the Mask.

+  @param[in]  CreateNew       The switch to create new.

+

+  @retval EFI_SUCCESS              Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

+

+**/

+EFI_STATUS

+CreateSpdEntry (

+  OUT EFI_IPSEC_SPD_SELECTOR    **Selector,

+  OUT EFI_IPSEC_SPD_DATA        **Data,

+  IN  LIST_ENTRY                *ParamPackage,

+  OUT UINT32                    *Mask,

+  IN  BOOLEAN                   CreateNew

+  )

+{

+  EFI_STATUS      Status;

+  EFI_STATUS      ReturnStatus;

+  CONST CHAR16    *ValueStr;

+  UINTN           DataSize;

+

+  Status    = EFI_SUCCESS;

+  *Mask     = 0;

+

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

+  ASSERT (*Selector != NULL);

+

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

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

+

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

+

+  //

+  // SPD DATA

+  // NOTE: Allocate enough memory and add padding for different arch.

+  //

+  DataSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SPD_DATA));

+  DataSize  = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_PROCESS_POLICY));

+  DataSize += sizeof (EFI_IPSEC_TUNNEL_OPTION);

+

+  *Data = AllocateZeroPool (DataSize);

+  ASSERT (*Data != NULL);

+

+  (*Data)->ProcessingPolicy               = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (

+                                                                           (*Data + 1),

+                                                                           sizeof (UINTN)

+                                                                           );

+  (*Data)->ProcessingPolicy->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER (

+                                                                          ((*Data)->ProcessingPolicy + 1),

+                                                                          sizeof (UINTN)

+                                                                          );

+

+

+  //

+  // Convert user imput from string to integer, and fill in the Name in EFI_IPSEC_SPD_DATA.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--name");

+  if (ValueStr != NULL) {

+    UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) (*Data)->Name);

+    *Mask |= NAME;

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the PackageFlag in EFI_IPSEC_SPD_DATA.

+  //

+  Status = GetNumber (

+             L"--packet-flag",

+             (UINT8) -1,

+             &(*Data)->PackageFlag,

+             sizeof (UINT32),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= PACKET_FLAG;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the Action in EFI_IPSEC_SPD_DATA.

+  //

+  Status = GetNumber (

+             L"--action",

+             (UINT8) -1,

+             &(*Data)->Action,

+             sizeof (UINT32),

+             mMapIpSecAction,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= ACTION;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the ExtSeqNum in EFI_IPSEC_SPD_DATA.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence")) {

+    (*Data)->ProcessingPolicy->ExtSeqNum   = TRUE;

+    *Mask |= EXT_SEQUENCE;

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence-")) {

+    (*Data)->ProcessingPolicy->ExtSeqNum   = FALSE;

+    *Mask |= EXT_SEQUENCE;

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the SeqOverflow in EFI_IPSEC_SPD_DATA.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow")) {

+    (*Data)->ProcessingPolicy->SeqOverflow = TRUE;

+    *Mask |= SEQUENCE_OVERFLOW;

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow-")) {

+    (*Data)->ProcessingPolicy->SeqOverflow = FALSE;

+    *Mask |= SEQUENCE_OVERFLOW;

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the FragCheck in EFI_IPSEC_SPD_DATA.

+  //

+  if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check")) {

+    (*Data)->ProcessingPolicy->FragCheck   = TRUE;

+    *Mask |= FRAGMENT_CHECK;

+  } else if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check-")) {

+    (*Data)->ProcessingPolicy->FragCheck   = FALSE;

+    *Mask |= FRAGMENT_CHECK;

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the ProcessingPolicy in EFI_IPSEC_SPD_DATA.

+  //

+  Status = GetNumber (

+             L"--lifebyte",

+             (UINT64) -1,

+             &(*Data)->ProcessingPolicy->SaLifetime.ByteCount,

+             sizeof (UINT64),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= LIFEBYTE;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetNumber (

+             L"--lifetime",

+             (UINT64) -1,

+             &(*Data)->ProcessingPolicy->SaLifetime.HardLifetime,

+             sizeof (UINT64),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= LIFETIME;

+  }

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetNumber (

+             L"--lifetime-soft",

+             (UINT64) -1,

+             &(*Data)->ProcessingPolicy->SaLifetime.SoftLifetime,

+             sizeof (UINT64),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= LIFETIME_SOFT;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  (*Data)->ProcessingPolicy->Mode = EfiIPsecTransport;

+  Status = GetNumber (

+             L"--mode",

+             0,

+             &(*Data)->ProcessingPolicy->Mode,

+             sizeof (UINT32),

+             mMapIpSecMode,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= MODE;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-local");

+  if (ValueStr != NULL) {

+    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->LocalTunnelAddress);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+        mHiiHandle,

+        mAppName,

+        L"--tunnel-local",

+        ValueStr

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else {

+      *Mask |= TUNNEL_LOCAL;

+    }

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-remote");

+  if (ValueStr != NULL) {

+    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+        mHiiHandle,

+        mAppName,

+        L"--tunnel-remote",

+        ValueStr

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else {

+      *Mask |= TUNNEL_REMOTE;

+    }

+  }

+

+  (*Data)->ProcessingPolicy->TunnelOption->DF = EfiIPsecTunnelCopyDf;

+  Status = GetNumber (

+             L"--dont-fragment",

+             0,

+             &(*Data)->ProcessingPolicy->TunnelOption->DF,

+             sizeof (UINT32),

+             mMapDfOption,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= DONT_FRAGMENT;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  (*Data)->ProcessingPolicy->Proto = EfiIPsecESP;

+  Status = GetNumber (

+             L"--ipsec-proto",

+             0,

+             &(*Data)->ProcessingPolicy->Proto,

+             sizeof (UINT32),

+             mMapIpSecProtocol,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= IPSEC_PROTO;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetNumber (

+             L"--encrypt-algo",

+             0,

+             &(*Data)->ProcessingPolicy->EncAlgoId,

+             sizeof (UINT8),

+             mMapEncAlgo,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= ENCRYPT_ALGO;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetNumber (

+             L"--auth-algo",

+             0,

+             &(*Data)->ProcessingPolicy->AuthAlgoId,

+             sizeof (UINT8),

+             mMapAuthAlgo,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= AUTH_ALGO;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Cannot check Mode against EfiIPsecTunnel, because user may want to change tunnel_remote only so the Mode is not set.

+  //

+  if ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE | DONT_FRAGMENT)) == 0) {

+    (*Data)->ProcessingPolicy->TunnelOption = NULL;

+  }

+

+  if ((*Mask & (EXT_SEQUENCE | SEQUENCE_OVERFLOW | FRAGMENT_CHECK | LIFEBYTE |

+                LIFETIME_SOFT | LIFETIME | MODE | TUNNEL_LOCAL | TUNNEL_REMOTE |

+                DONT_FRAGMENT | IPSEC_PROTO | AUTH_ALGO | ENCRYPT_ALGO)) == 0) {

+    if ((*Data)->Action != EfiIPsecActionProtect) {

+      //

+      // User may not provide additional parameter for Protect action, so we cannot simply set ProcessingPolicy to NULL.

+      //

+      (*Data)->ProcessingPolicy = NULL;

+    }

+  }

+

+  if (CreateNew) {

+    if ((*Mask & (LOCAL | REMOTE | PROTO | ACTION)) != (LOCAL | REMOTE | PROTO | ACTION)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+        mHiiHandle,

+        mAppName,

+        L"--local --remote --proto --action"

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else if (((*Data)->Action == EfiIPsecActionProtect) &&

+               ((*Data)->ProcessingPolicy->Mode == EfiIPsecTunnel) &&

+               ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE))) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+        mHiiHandle,

+        mAppName,

+        L"--tunnel-local --tunnel-remote"

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    }

+  }

+

+  return ReturnStatus;

+}

+

+/**

+  Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA through ParamPackage list.

+

+  @param[out] SaId            The pointer to the EFI_IPSEC_SA_ID structure.

+  @param[out] Data            The pointer to the EFI_IPSEC_SA_DATA structure.

+  @param[in]  ParamPackage    The pointer to the ParamPackage list.

+  @param[out] Mask            The pointer to the Mask.

+  @param[in]  CreateNew       The switch to create new.

+

+  @retval EFI_SUCCESS              Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

+

+**/

+EFI_STATUS

+CreateSadEntry (

+  OUT EFI_IPSEC_SA_ID      **SaId,

+  OUT EFI_IPSEC_SA_DATA    **Data,

+  IN  LIST_ENTRY           *ParamPackage,

+  OUT UINT32               *Mask,

+  IN  BOOLEAN              CreateNew

+  )

+{

+  EFI_STATUS      Status;

+  EFI_STATUS      ReturnStatus;

+  UINTN           AuthKeyLength;

+  UINTN           EncKeyLength;

+  CONST CHAR16    *ValueStr;

+  UINTN           DataSize;

+

+  Status        = EFI_SUCCESS;

+  ReturnStatus  = EFI_SUCCESS;

+  *Mask         = 0;

+  AuthKeyLength = 0;

+  EncKeyLength  = 0;

+

+  *SaId = AllocateZeroPool (sizeof (EFI_IPSEC_SA_ID));

+  ASSERT (*SaId != NULL);

+

+  //

+  // Convert user imput from string to integer, and fill in the Spi in EFI_IPSEC_SA_ID.

+  //

+  Status = GetNumber (L"--spi", (UINT32) -1, &(*SaId)->Spi, sizeof (UINT32), NULL, ParamPackage, FORMAT_NUMBER);

+  if (!EFI_ERROR (Status)) {

+    *Mask |= SPI;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the Proto in EFI_IPSEC_SA_ID.

+  //

+  Status = GetNumber (

+             L"--ipsec-proto",

+             0,

+             &(*SaId)->Proto,

+             sizeof (EFI_IPSEC_PROTOCOL_TYPE),

+             mMapIpSecProtocol,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= IPSEC_PROTO;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in the DestAddress in EFI_IPSEC_SA_ID.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--dest");

+  if (ValueStr != NULL) {

+    Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*SaId)->DestAddress);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+        mHiiHandle,

+        mAppName,

+        L"--dest",

+        ValueStr

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else {

+      *Mask |= DEST;

+    }

+  }

+

+  //

+  // Convert user imput from string to integer, and fill in EFI_IPSEC_SA_DATA.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");

+  if (ValueStr != NULL) {

+    AuthKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");

+  if (ValueStr != NULL) {

+    EncKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);

+  }

+

+  //

+  // EFI_IPSEC_SA_DATA:

+  //   +------------

+  //   | EFI_IPSEC_SA_DATA

+  //   +-----------------------

+  //   | AuthKey

+  //   +-------------------------

+  //   | EncKey

+  //   +-------------------------

+  //   | SpdSelector

+  //

+  // Notes: To make sure the address alignment add padding after each data if needed.

+  //

+  DataSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA));

+  DataSize  = ALIGN_VARIABLE (DataSize + AuthKeyLength);

+  DataSize  = ALIGN_VARIABLE (DataSize + EncKeyLength);

+  DataSize  = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_SPD_SELECTOR));

+  DataSize  = ALIGN_VARIABLE (DataSize + sizeof (EFI_IP_ADDRESS_INFO));

+  DataSize += sizeof (EFI_IP_ADDRESS_INFO);

+

+

+

+  *Data = AllocateZeroPool (DataSize);

+  ASSERT (*Data != NULL);

+

+  (*Data)->ManualSet                    = TRUE;

+  (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER (((*Data) + 1), sizeof (UINTN));

+  (*Data)->AlgoInfo.EspAlgoInfo.EncKey  = (VOID *) ALIGN_POINTER (

+                                                     ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.AuthKey + AuthKeyLength),

+                                                     sizeof (UINTN)

+                                                     );

+  (*Data)->SpdSelector                  = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER (

+                                                                       ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.EncKey + EncKeyLength),

+                                                                       sizeof (UINTN)

+                                                                       );

+  (*Data)->SpdSelector->LocalAddress    = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (

+                                                                    ((UINT8 *) (*Data)->SpdSelector + sizeof (EFI_IPSEC_SPD_SELECTOR)),

+                                                                    sizeof (UINTN));

+  (*Data)->SpdSelector->RemoteAddress   = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (

+                                                                    (*Data)->SpdSelector->LocalAddress + 1,

+                                                                    sizeof (UINTN)

+                                                                    );

+

+  (*Data)->Mode = EfiIPsecTransport;

+  Status = GetNumber (

+             L"--mode",

+             0,

+             &(*Data)->Mode,

+             sizeof (EFI_IPSEC_MODE),

+             mMapIpSecMode,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= MODE;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // According to RFC 4303-3.3.3. The first packet sent using a given SA

+  // will contain a sequence number of 1.

+  //

+  (*Data)->SNCount = 1;

+  Status = GetNumber (

+             L"--sequence-number",

+             (UINT64) -1,

+             &(*Data)->SNCount,

+             sizeof (UINT64),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= SEQUENCE_NUMBER;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  (*Data)->AntiReplayWindows = 0;

+  Status = GetNumber (

+             L"--antireplay-window",

+             (UINT8) -1,

+             &(*Data)->AntiReplayWindows,

+             sizeof (UINT8),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= SEQUENCE_NUMBER;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetNumber (

+             L"--encrypt-algo",

+             0,

+             &(*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId,

+             sizeof (UINT8),

+             mMapEncAlgo,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= ENCRYPT_ALGO;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");

+  if (ValueStr != NULL ) {

+    (*Data)->AlgoInfo.EspAlgoInfo.EncKeyLength = EncKeyLength;

+    CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.EncKey, ValueStr, EncKeyLength);

+    *Mask |= ENCRYPT_KEY;

+  } else {

+    (*Data)->AlgoInfo.EspAlgoInfo.EncKey = NULL;

+  }

+

+  Status = GetNumber (

+             L"--auth-algo",

+             0,

+             &(*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId,

+             sizeof (UINT8),

+             mMapAuthAlgo,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= AUTH_ALGO;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");

+  if (ValueStr != NULL) {

+    (*Data)->AlgoInfo.EspAlgoInfo.AuthKeyLength = AuthKeyLength;

+    CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.AuthKey, ValueStr, AuthKeyLength);

+    *Mask |= AUTH_KEY;

+  } else {

+    (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = NULL;

+  }

+

+  Status = GetNumber (

+             L"--lifebyte",

+             (UINT64) -1,

+             &(*Data)->SaLifetime.ByteCount,

+             sizeof (UINT64),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= LIFEBYTE;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetNumber (

+             L"--lifetime",

+             (UINT64) -1,

+             &(*Data)->SaLifetime.HardLifetime,

+             sizeof (UINT64),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= LIFETIME;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetNumber (

+             L"--lifetime-soft",

+             (UINT64) -1,

+             &(*Data)->SaLifetime.SoftLifetime,

+             sizeof (UINT64),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= LIFETIME_SOFT;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetNumber (

+             L"--path-mtu",

+             (UINT32) -1,

+             &(*Data)->PathMTU,

+             sizeof (UINT32),

+             NULL,

+             ParamPackage,

+             FORMAT_NUMBER

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= PATH_MTU;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  ReturnStatus = CreateSpdSelector ((*Data)->SpdSelector, ParamPackage, Mask);

+

+  if (CreateNew) {

+    if ((*Mask & (SPI | IPSEC_PROTO | DEST)) != (SPI | IPSEC_PROTO | DEST)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+        mHiiHandle,

+        mAppName,

+        L"--spi --ipsec-proto --dest"

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else {

+      if ((*SaId)->Proto == EfiIPsecAH) {

+        if ((*Mask & AUTH_ALGO) == 0) {

+          ShellPrintHiiEx (

+            -1,

+            -1,

+            NULL,

+            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),

+            mHiiHandle,

+            mAppName,

+            L"--auth-algo"

+            );

+          ReturnStatus = EFI_INVALID_PARAMETER;

+        } else if ((*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId != EFI_IPSEC_AALG_NONE && (*Mask & AUTH_KEY) == 0) {

+          ShellPrintHiiEx (

+            -1,

+            -1,

+            NULL,

+            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),

+            mHiiHandle,

+            mAppName,

+            L"--auth-key"

+            );

+          ReturnStatus = EFI_INVALID_PARAMETER;

+        }

+      } else {

+        if ((*Mask & ENCRYPT_ALGO) == 0) {

+          ShellPrintHiiEx (

+            -1,

+            -1,

+            NULL,

+            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),

+            mHiiHandle,

+            mAppName,

+            L"--encrypt-algo"

+            );

+          ReturnStatus = EFI_INVALID_PARAMETER;

+        } else if ((*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (*Mask & ENCRYPT_KEY) == 0) {

+          ShellPrintHiiEx (

+            -1,

+            -1,

+            NULL,

+            STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),

+            mHiiHandle,

+            mAppName,

+            L"--encrypt-key"

+            );

+          ReturnStatus = EFI_INVALID_PARAMETER;

+        }

+      }

+    }

+  }

+

+  return ReturnStatus;

+}

+

+/**

+  Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA through ParamPackage list.

+

+  @param[out] PadId           The pointer to the EFI_IPSEC_PAD_ID structure.

+  @param[out] Data            The pointer to the EFI_IPSEC_PAD_DATA structure.

+  @param[in]  ParamPackage    The pointer to the ParamPackage list.

+  @param[out] Mask            The pointer to the Mask.

+  @param[in]  CreateNew       The switch to create new.

+

+  @retval EFI_SUCCESS              Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

+

+**/

+EFI_STATUS

+CreatePadEntry (

+  OUT EFI_IPSEC_PAD_ID      **PadId,

+  OUT EFI_IPSEC_PAD_DATA    **Data,

+  IN  LIST_ENTRY            *ParamPackage,

+  OUT UINT32                *Mask,

+  IN  BOOLEAN               CreateNew

+  )

+{

+  EFI_STATUS         Status;

+  EFI_STATUS         ReturnStatus;

+  EFI_FILE_HANDLE    FileHandle;

+  UINT64             FileSize;

+  UINTN              AuthDataLength;

+  UINTN              RevocationDataLength;

+  UINTN              DataLength;

+  UINTN              Index;

+  CONST CHAR16       *ValueStr;

+  UINTN              DataSize;

+

+  Status               = EFI_SUCCESS;

+  ReturnStatus         = EFI_SUCCESS;

+  *Mask                = 0;

+  AuthDataLength       = 0;

+  RevocationDataLength = 0;

+

+  *PadId = AllocateZeroPool (sizeof (EFI_IPSEC_PAD_ID));

+  ASSERT (*PadId != NULL);

+

+  //

+  // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_ID.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-address");

+  if (ValueStr != NULL) {

+    (*PadId)->PeerIdValid = FALSE;

+    Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &(*PadId)->Id.IpAddress);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),

+        mHiiHandle,

+        mAppName,

+        L"--peer-address",

+        ValueStr

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else {

+      *Mask |= PEER_ADDRESS;

+    }

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-id");

+  if (ValueStr != NULL) {

+    (*PadId)->PeerIdValid = TRUE;

+    StrnCpy ((CHAR16 *) (*PadId)->Id.PeerId, ValueStr, ARRAY_SIZE ((*PadId)->Id.PeerId) - 1);

+    *Mask |= PEER_ID;

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");

+  if (ValueStr != NULL) {

+    if (ValueStr[0] == L'@') {

+      //

+      // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"

+      //

+      Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);

+      if (EFI_ERROR (Status)) {

+        ShellPrintHiiEx (

+          -1,

+          -1,

+          NULL,

+          STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),

+          mHiiHandle,

+          mAppName,

+          &ValueStr[1]

+          );

+        ReturnStatus = EFI_INVALID_PARAMETER;

+      } else {

+        Status = ShellGetFileSize (FileHandle, &FileSize);

+        ShellCloseFile (&FileHandle);

+        if (EFI_ERROR (Status)) {

+          ShellPrintHiiEx (

+            -1,

+            -1,

+            NULL,

+            STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),

+            mHiiHandle,

+            mAppName,

+            &ValueStr[1]

+            );

+          ReturnStatus = EFI_INVALID_PARAMETER;

+        } else {

+          AuthDataLength = (UINTN) FileSize;

+        }

+      }

+    } else {

+      AuthDataLength = StrLen (ValueStr);

+    }

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");

+  if (ValueStr != NULL) {

+    RevocationDataLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);

+  }

+

+  //

+  // Allocate Buffer for Data. Add padding after each struct to make sure the alignment

+  // in different Arch.

+  //

+  DataSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));

+  DataSize  = ALIGN_VARIABLE (DataSize + AuthDataLength);

+  DataSize += RevocationDataLength;

+

+  *Data = AllocateZeroPool (DataSize);

+  ASSERT (*Data != NULL);

+

+  (*Data)->AuthData       = (VOID *) ALIGN_POINTER ((*Data + 1), sizeof (UINTN));

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

+  (*Data)->AuthProtocol   = EfiIPsecAuthProtocolIKEv1;

+

+  //

+  // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_DATA.

+  //

+  Status = GetNumber (

+             L"--auth-proto",

+             0,

+             &(*Data)->AuthProtocol,

+             sizeof (EFI_IPSEC_AUTH_PROTOCOL_TYPE),

+             mMapAuthProto,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= AUTH_PROTO;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  Status = GetNumber (

+             L"--auth-method",

+             0,

+             &(*Data)->AuthMethod,

+             sizeof (EFI_IPSEC_AUTH_METHOD),

+             mMapAuthMethod,

+             ParamPackage,

+             FORMAT_STRING

+             );

+  if (!EFI_ERROR (Status)) {

+    *Mask |= AUTH_METHOD;

+  }

+

+  if (Status == EFI_INVALID_PARAMETER) {

+    ReturnStatus = EFI_INVALID_PARAMETER;

+  }

+

+  if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id")) {

+    (*Data)->IkeIdFlag = TRUE;

+    *Mask |= IKE_ID;

+  }

+

+  if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id-")) {

+    (*Data)->IkeIdFlag = FALSE;

+    *Mask |= IKE_ID;

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");

+  if (ValueStr != NULL) {

+    if (ValueStr[0] == L'@') {

+      //

+      // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"

+      //

+

+      Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);

+      if (EFI_ERROR (Status)) {

+        ShellPrintHiiEx (

+          -1,

+          -1,

+          NULL,

+          STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),

+          mHiiHandle,

+          mAppName,

+          &ValueStr[1]

+          );

+        ReturnStatus = EFI_INVALID_PARAMETER;

+        (*Data)->AuthData = NULL;

+      } else {

+        DataLength = AuthDataLength;

+        Status = ShellReadFile (FileHandle, &DataLength, (*Data)->AuthData);

+        ShellCloseFile (&FileHandle);

+        if (EFI_ERROR (Status)) {

+          ShellPrintHiiEx (

+            -1,

+            -1,

+            NULL,

+            STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),

+            mHiiHandle,

+            mAppName,

+            &ValueStr[1]

+            );

+          ReturnStatus = EFI_INVALID_PARAMETER;

+          (*Data)->AuthData = NULL;

+        } else {

+          ASSERT (DataLength == AuthDataLength);

+          *Mask |= AUTH_DATA;

+        }

+      }

+    } else {

+      for (Index = 0; Index < AuthDataLength; Index++) {

+        ((CHAR8 *) (*Data)->AuthData)[Index] = (CHAR8) ValueStr[Index];

+      }

+      (*Data)->AuthDataSize = AuthDataLength;

+      *Mask |= AUTH_DATA;

+    }

+  }

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");

+  if (ValueStr != NULL) {

+    CopyMem ((*Data)->RevocationData, ValueStr, RevocationDataLength);

+    (*Data)->RevocationDataSize = RevocationDataLength;

+    *Mask |= REVOCATION_DATA;

+  } else {

+    (*Data)->RevocationData = NULL;

+  }

+

+  if (CreateNew) {

+    if ((*Mask & (PEER_ID | PEER_ADDRESS)) == 0) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+        mHiiHandle,

+        mAppName,

+        L"--peer-id --peer-address"

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    } else if ((*Mask & (AUTH_METHOD | AUTH_DATA)) != (AUTH_METHOD | AUTH_DATA)) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+        mHiiHandle,

+        mAppName,

+        L"--auth-method --auth-data"

+        );

+      ReturnStatus = EFI_INVALID_PARAMETER;

+    }

+  }

+

+  return ReturnStatus;

+}

+

+CREATE_POLICY_ENTRY mCreatePolicyEntry[] = {

+  (CREATE_POLICY_ENTRY) CreateSpdEntry,

+  (CREATE_POLICY_ENTRY) CreateSadEntry,

+  (CREATE_POLICY_ENTRY) CreatePadEntry

+};

+

+/**

+  Combine old SPD entry with new SPD entry.

+

+  @param[in, out] OldSelector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.

+  @param[in, out] OldData        The pointer to the EFI_IPSEC_SPD_DATA structure.

+  @param[in]      NewSelector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.

+  @param[in]      NewData        The pointer to the EFI_IPSEC_SPD_DATA structure.

+  @param[in]      Mask           The pointer to the Mask.

+  @param[out]     CreateNew      The switch to create new.

+

+  @retval EFI_SUCCESS              Combined successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

+

+**/

+EFI_STATUS

+CombineSpdEntry (

+  IN OUT EFI_IPSEC_SPD_SELECTOR    *OldSelector,

+  IN OUT EFI_IPSEC_SPD_DATA        *OldData,

+  IN     EFI_IPSEC_SPD_SELECTOR    *NewSelector,

+  IN     EFI_IPSEC_SPD_DATA        *NewData,

+  IN     UINT32                    Mask,

+     OUT BOOLEAN                   *CreateNew

+  )

+{

+

+  //

+  // Process Selector

+  //

+  *CreateNew = FALSE;

+  if ((Mask & LOCAL) == 0) {

+    NewSelector->LocalAddressCount = OldSelector->LocalAddressCount;

+    NewSelector->LocalAddress      = OldSelector->LocalAddress;

+  } else if ((NewSelector->LocalAddressCount != OldSelector->LocalAddressCount) ||

+             (CompareMem (NewSelector->LocalAddress, OldSelector->LocalAddress, NewSelector->LocalAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {

+    *CreateNew = TRUE;

+  }

+

+  if ((Mask & REMOTE) == 0) {

+    NewSelector->RemoteAddressCount = OldSelector->RemoteAddressCount;

+    NewSelector->RemoteAddress      = OldSelector->RemoteAddress;

+  } else if ((NewSelector->RemoteAddressCount != OldSelector->RemoteAddressCount) ||

+             (CompareMem (NewSelector->RemoteAddress, OldSelector->RemoteAddress, NewSelector->RemoteAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {

+    *CreateNew = TRUE;

+  }

+

+  if ((Mask & PROTO) == 0) {

+    NewSelector->NextLayerProtocol = OldSelector->NextLayerProtocol;

+  } else if (NewSelector->NextLayerProtocol != OldSelector->NextLayerProtocol) {

+    *CreateNew = TRUE;

+  }

+

+  switch (NewSelector->NextLayerProtocol) {

+    case EFI_IP4_PROTO_TCP:

+    case EFI_IP4_PROTO_UDP:

+      if ((Mask & LOCAL_PORT) == 0) {

+        NewSelector->LocalPort      = OldSelector->LocalPort;

+        NewSelector->LocalPortRange = OldSelector->LocalPortRange;

+      } else if ((NewSelector->LocalPort != OldSelector->LocalPort) ||

+        (NewSelector->LocalPortRange != OldSelector->LocalPortRange)) {

+        *CreateNew = TRUE;

+      }

+

+      if ((Mask & REMOTE_PORT) == 0) {

+        NewSelector->RemotePort      = OldSelector->RemotePort;

+        NewSelector->RemotePortRange = OldSelector->RemotePortRange;

+      } else if ((NewSelector->RemotePort != OldSelector->RemotePort) ||

+        (NewSelector->RemotePortRange != OldSelector->RemotePortRange)) {

+        *CreateNew = TRUE;

+      }

+      break;

+

+    case EFI_IP4_PROTO_ICMP:

+      if ((Mask & ICMP_TYPE) == 0) {

+        NewSelector->LocalPort = OldSelector->LocalPort;

+      } else if (NewSelector->LocalPort != OldSelector->LocalPort) {

+        *CreateNew = TRUE;

+      }

+

+      if ((Mask & ICMP_CODE) == 0) {

+        NewSelector->RemotePort = OldSelector->RemotePort;

+      } else if (NewSelector->RemotePort != OldSelector->RemotePort) {

+        *CreateNew = TRUE;

+      }

+      break;

+  }

+  //

+  // Process Data

+  //

+  if ((Mask & NAME) != 0) {

+    AsciiStrCpy ((CHAR8 *) OldData->Name, (CHAR8 *) NewData->Name);

+  }

+

+  if ((Mask & PACKET_FLAG) != 0) {

+    OldData->PackageFlag = NewData->PackageFlag;

+  }

+

+  if ((Mask & ACTION) != 0) {

+    OldData->Action = NewData->Action;

+  }

+

+  if (OldData->Action != EfiIPsecActionProtect) {

+    OldData->ProcessingPolicy = NULL;

+  } else {

+    //

+    // Protect

+    //

+    if (OldData->ProcessingPolicy == NULL) {

+      //

+      // Just point to new data if originally NULL.

+      //

+      OldData->ProcessingPolicy = NewData->ProcessingPolicy;

+      if (OldData->ProcessingPolicy->Mode == EfiIPsecTunnel &&

+          (Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)

+        ) {

+        //

+        // Change to Protect action and Tunnel mode, but without providing local/remote tunnel address.

+        //

+        ShellPrintHiiEx (

+          -1,

+          -1,

+          NULL,

+          STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+          mHiiHandle,

+          mAppName,

+          L"--tunnel-local --tunnel-remote"

+          );

+        return EFI_INVALID_PARAMETER;

+      }

+    } else {

+      //

+      // Modify some of the data.

+      //

+      if ((Mask & EXT_SEQUENCE) != 0) {

+        OldData->ProcessingPolicy->ExtSeqNum = NewData->ProcessingPolicy->ExtSeqNum;

+      }

+

+      if ((Mask & SEQUENCE_OVERFLOW) != 0) {

+        OldData->ProcessingPolicy->SeqOverflow = NewData->ProcessingPolicy->SeqOverflow;

+      }

+

+      if ((Mask & FRAGMENT_CHECK) != 0) {

+        OldData->ProcessingPolicy->FragCheck = NewData->ProcessingPolicy->FragCheck;

+      }

+

+      if ((Mask & LIFEBYTE) != 0) {

+        OldData->ProcessingPolicy->SaLifetime.ByteCount = NewData->ProcessingPolicy->SaLifetime.ByteCount;

+      }

+

+      if ((Mask & LIFETIME_SOFT) != 0) {

+        OldData->ProcessingPolicy->SaLifetime.SoftLifetime = NewData->ProcessingPolicy->SaLifetime.SoftLifetime;

+      }

+

+      if ((Mask & LIFETIME) != 0) {

+        OldData->ProcessingPolicy->SaLifetime.HardLifetime = NewData->ProcessingPolicy->SaLifetime.HardLifetime;

+      }

+

+      if ((Mask & MODE) != 0) {

+        OldData->ProcessingPolicy->Mode = NewData->ProcessingPolicy->Mode;

+      }

+

+      if ((Mask & IPSEC_PROTO) != 0) {

+        OldData->ProcessingPolicy->Proto = NewData->ProcessingPolicy->Proto;

+      }

+

+      if ((Mask & AUTH_ALGO) != 0) {

+        OldData->ProcessingPolicy->AuthAlgoId = NewData->ProcessingPolicy->AuthAlgoId;

+      }

+

+      if ((Mask & ENCRYPT_ALGO) != 0) {

+        OldData->ProcessingPolicy->EncAlgoId = NewData->ProcessingPolicy->EncAlgoId;

+      }

+

+      if (OldData->ProcessingPolicy->Mode != EfiIPsecTunnel) {

+        OldData->ProcessingPolicy->TunnelOption = NULL;

+      } else {

+        if (OldData->ProcessingPolicy->TunnelOption == NULL) {

+          //

+          // Set from Transport mode to Tunnel mode, should ensure TUNNEL_LOCAL & TUNNEL_REMOTE both exists.

+          //

+          if ((Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)) {

+            ShellPrintHiiEx (

+              -1,

+              -1,

+              NULL,

+              STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+              mHiiHandle,

+              mAppName,

+              L"--tunnel-local --tunnel-remote"

+              );

+            return EFI_INVALID_PARAMETER;

+          }

+

+          OldData->ProcessingPolicy->TunnelOption = NewData->ProcessingPolicy->TunnelOption;

+        } else {

+          if ((Mask & TUNNEL_LOCAL) != 0) {

+            CopyMem (

+              &OldData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,

+              &NewData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,

+              sizeof (EFI_IP_ADDRESS)

+              );

+          }

+

+          if ((Mask & TUNNEL_REMOTE) != 0) {

+            CopyMem (

+              &OldData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,

+              &NewData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,

+              sizeof (EFI_IP_ADDRESS)

+              );

+          }

+

+          if ((Mask & DONT_FRAGMENT) != 0) {

+            OldData->ProcessingPolicy->TunnelOption->DF = NewData->ProcessingPolicy->TunnelOption->DF;

+          }

+        }

+      }

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Combine old SAD entry with new SAD entry.

+

+  @param[in, out] OldSaId      The pointer to the EFI_IPSEC_SA_ID structure.

+  @param[in, out] OldData      The pointer to the EFI_IPSEC_SA_DATA structure.

+  @param[in]      NewSaId      The pointer to the EFI_IPSEC_SA_ID structure.

+  @param[in]      NewData      The pointer to the EFI_IPSEC_SA_DATA structure.

+  @param[in]      Mask         The pointer to the Mask.

+  @param[out]     CreateNew    The switch to create new.

+

+  @retval EFI_SUCCESS              Combined successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

+

+**/

+EFI_STATUS

+CombineSadEntry (

+  IN OUT EFI_IPSEC_SA_ID      *OldSaId,

+  IN OUT EFI_IPSEC_SA_DATA    *OldData,

+  IN     EFI_IPSEC_SA_ID      *NewSaId,

+  IN     EFI_IPSEC_SA_DATA    *NewData,

+  IN     UINT32               Mask,

+     OUT BOOLEAN              *CreateNew

+  )

+{

+

+  *CreateNew = FALSE;

+

+  if ((Mask & SPI) == 0) {

+    NewSaId->Spi = OldSaId->Spi;

+  } else if (NewSaId->Spi != OldSaId->Spi) {

+    *CreateNew = TRUE;

+  }

+

+  if ((Mask & IPSEC_PROTO) == 0) {

+    NewSaId->Proto = OldSaId->Proto;

+  } else if (NewSaId->Proto != OldSaId->Proto) {

+    *CreateNew = TRUE;

+  }

+

+  if ((Mask & DEST) == 0) {

+    CopyMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS));

+  } else if (CompareMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS)) != 0) {

+    *CreateNew = TRUE;

+  }

+

+  //

+  // Process SA_DATA.

+  //

+  if ((Mask & MODE) != 0) {

+    OldData->Mode = NewData->Mode;

+  }

+

+  if ((Mask & SEQUENCE_NUMBER) != 0) {

+    OldData->SNCount = NewData->SNCount;

+  }

+

+  if ((Mask & ANTIREPLAY_WINDOW) != 0) {

+    OldData->AntiReplayWindows = NewData->AntiReplayWindows;

+  }

+

+  if ((Mask & AUTH_ALGO) != 0) {

+    OldData->AlgoInfo.EspAlgoInfo.AuthAlgoId    = NewData->AlgoInfo.EspAlgoInfo.AuthAlgoId;

+  }

+

+  if ((Mask & AUTH_KEY) != 0) {

+    OldData->AlgoInfo.EspAlgoInfo.AuthKey       = NewData->AlgoInfo.EspAlgoInfo.AuthKey;

+    OldData->AlgoInfo.EspAlgoInfo.AuthKeyLength = NewData->AlgoInfo.EspAlgoInfo.AuthKeyLength;

+  }

+

+  if ((Mask & ENCRYPT_ALGO) != 0) {

+    OldData->AlgoInfo.EspAlgoInfo.EncAlgoId     = NewData->AlgoInfo.EspAlgoInfo.EncAlgoId;

+  }

+

+  if ((Mask & ENCRYPT_KEY) != 0) {

+    OldData->AlgoInfo.EspAlgoInfo.EncKey        = NewData->AlgoInfo.EspAlgoInfo.EncKey;

+    OldData->AlgoInfo.EspAlgoInfo.EncKeyLength  = NewData->AlgoInfo.EspAlgoInfo.EncKeyLength;

+  }

+

+  if (NewSaId->Proto == EfiIPsecAH) {

+    if ((Mask & (ENCRYPT_ALGO | ENCRYPT_KEY)) != 0) {

+      //

+      // Should not provide encrypt_* if AH.

+      //

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_UNWANTED_PARAMETER),

+        mHiiHandle,

+        mAppName,

+        L"--encrypt-algo --encrypt-key"

+        );

+      return EFI_INVALID_PARAMETER;

+    }

+  }

+

+  if (NewSaId->Proto == EfiIPsecESP && OldSaId->Proto == EfiIPsecAH) {

+    //

+    // AH -> ESP

+    // Should provide encrypt_algo at least.

+    //

+    if ((Mask & ENCRYPT_ALGO) == 0) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),

+        mHiiHandle,

+        mAppName,

+        L"--encrypt-algo"

+        );

+      return EFI_INVALID_PARAMETER;

+    }

+

+    //

+    // Encrypt_key should be provided if algorithm is not NONE.

+    //

+    if (NewData->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (Mask & ENCRYPT_KEY) == 0) {

+      ShellPrintHiiEx (

+        -1,

+        -1,

+        NULL,

+        STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),

+        mHiiHandle,

+        mAppName,

+        L"--encrypt-algo"

+        );

+      return EFI_INVALID_PARAMETER;

+    }

+  }

+

+  if ((Mask & LIFEBYTE) != 0) {

+    OldData->SaLifetime.ByteCount    = NewData->SaLifetime.ByteCount;

+  }

+

+  if ((Mask & LIFETIME_SOFT) != 0) {

+    OldData->SaLifetime.SoftLifetime = NewData->SaLifetime.SoftLifetime;

+  }

+

+  if ((Mask & LIFETIME) != 0) {

+    OldData->SaLifetime.HardLifetime = NewData->SaLifetime.HardLifetime;

+  }

+

+  if ((Mask & PATH_MTU) != 0) {

+    OldData->PathMTU                 = NewData->PathMTU;

+  }

+  //

+  // Process SpdSelector.

+  //

+  if (OldData->SpdSelector == NULL) {

+    if ((Mask & (LOCAL | REMOTE | PROTO | LOCAL_PORT | REMOTE_PORT | ICMP_TYPE | ICMP_CODE)) != 0) {

+      if ((Mask & (LOCAL | REMOTE | PROTO)) != (LOCAL | REMOTE | PROTO)) {

+        ShellPrintHiiEx (

+          -1,

+          -1,

+          NULL,

+          STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),

+          mHiiHandle,

+          mAppName,

+          L"--local --remote --proto"

+          );

+        return EFI_INVALID_PARAMETER;

+      }

+

+      OldData->SpdSelector = NewData->SpdSelector;

+    }

+  } else {

+    if ((Mask & LOCAL) != 0) {

+      OldData->SpdSelector->LocalAddressCount  = NewData->SpdSelector->LocalAddressCount;

+      OldData->SpdSelector->LocalAddress       = NewData->SpdSelector->LocalAddress;

+    }

+

+    if ((Mask & REMOTE) != 0) {

+      OldData->SpdSelector->RemoteAddressCount = NewData->SpdSelector->RemoteAddressCount;

+      OldData->SpdSelector->RemoteAddress      = NewData->SpdSelector->RemoteAddress;

+    }

+

+    if ((Mask & PROTO) != 0) {

+      OldData->SpdSelector->NextLayerProtocol  = NewData->SpdSelector->NextLayerProtocol;

+    }

+

+    if (OldData->SpdSelector != NULL) {

+      switch (OldData->SpdSelector->NextLayerProtocol) {

+        case EFI_IP4_PROTO_TCP:

+        case EFI_IP4_PROTO_UDP:

+          if ((Mask & LOCAL_PORT) != 0) {

+            OldData->SpdSelector->LocalPort  = NewData->SpdSelector->LocalPort;

+          }

+

+          if ((Mask & REMOTE_PORT) != 0) {

+            OldData->SpdSelector->RemotePort = NewData->SpdSelector->RemotePort;

+          }

+          break;

+

+        case EFI_IP4_PROTO_ICMP:

+          if ((Mask & ICMP_TYPE) != 0) {

+            OldData->SpdSelector->LocalPort  = (UINT8) NewData->SpdSelector->LocalPort;

+          }

+

+          if ((Mask & ICMP_CODE) != 0) {

+            OldData->SpdSelector->RemotePort = (UINT8) NewData->SpdSelector->RemotePort;

+          }

+          break;

+      }

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Combine old PAD entry with new PAD entry.

+

+  @param[in, out] OldPadId     The pointer to the EFI_IPSEC_PAD_ID structure.

+  @param[in, out] OldData      The pointer to the EFI_IPSEC_PAD_DATA structure.

+  @param[in]      NewPadId     The pointer to the EFI_IPSEC_PAD_ID structure.

+  @param[in]      NewData      The pointer to the EFI_IPSEC_PAD_DATA structure.

+  @param[in]      Mask         The pointer to the Mask.

+  @param[out]     CreateNew    The switch to create new.

+

+  @retval EFI_SUCCESS              Combined successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

+

+**/

+EFI_STATUS

+CombinePadEntry (

+  IN OUT EFI_IPSEC_PAD_ID      *OldPadId,

+  IN OUT EFI_IPSEC_PAD_DATA    *OldData,

+  IN     EFI_IPSEC_PAD_ID      *NewPadId,

+  IN     EFI_IPSEC_PAD_DATA    *NewData,

+  IN     UINT32                Mask,

+     OUT BOOLEAN               *CreateNew

+  )

+{

+

+  *CreateNew = FALSE;

+

+  if ((Mask & (PEER_ID | PEER_ADDRESS)) == 0) {

+    CopyMem (NewPadId, OldPadId, sizeof (EFI_IPSEC_PAD_ID));

+  } else {

+    if ((Mask & PEER_ID) != 0) {

+      if (OldPadId->PeerIdValid) {

+        if (StrCmp ((CONST CHAR16 *) OldPadId->Id.PeerId, (CONST CHAR16 *) NewPadId->Id.PeerId) != 0) {

+          *CreateNew = TRUE;

+        }

+      } else {

+        *CreateNew = TRUE;

+      }

+    } else {

+      //

+      // MASK & PEER_ADDRESS

+      //

+      if (OldPadId->PeerIdValid) {

+        *CreateNew = TRUE;

+      } else {

+        if ((CompareMem (&OldPadId->Id.IpAddress.Address, &NewPadId->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0) ||

+            (OldPadId->Id.IpAddress.PrefixLength != NewPadId->Id.IpAddress.PrefixLength)) {

+          *CreateNew = TRUE;

+        }

+      }

+    }

+  }

+

+  if ((Mask & AUTH_PROTO) != 0) {

+    OldData->AuthProtocol = NewData->AuthProtocol;

+  }

+

+  if ((Mask & AUTH_METHOD) != 0) {

+    OldData->AuthMethod = NewData->AuthMethod;

+  }

+

+  if ((Mask & IKE_ID) != 0) {

+    OldData->IkeIdFlag = NewData->IkeIdFlag;

+  }

+

+  if ((Mask & AUTH_DATA) != 0) {

+    OldData->AuthDataSize = NewData->AuthDataSize;

+    OldData->AuthData     = NewData->AuthData;

+  }

+

+  if ((Mask & REVOCATION_DATA) != 0) {

+    OldData->RevocationDataSize = NewData->RevocationDataSize;

+    OldData->RevocationData     = NewData->RevocationData;

+  }

+

+  return EFI_SUCCESS;

+}

+

+COMBINE_POLICY_ENTRY mCombinePolicyEntry[] = {

+  (COMBINE_POLICY_ENTRY) CombineSpdEntry,

+  (COMBINE_POLICY_ENTRY) CombineSadEntry,

+  (COMBINE_POLICY_ENTRY) CombinePadEntry

+};

+

+/**

+  Edit entry information in the database.

+

+  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.

+  @param[in] Data        The pointer to the data.

+  @param[in] Context     The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.

+

+  @retval EFI_SUCCESS    Continue the iteration.

+  @retval EFI_ABORTED    Abort the iteration.

+**/

+EFI_STATUS

+EditOperatePolicyEntry (

+  IN EFI_IPSEC_CONFIG_SELECTOR    *Selector,

+  IN VOID                         *Data,

+  IN EDIT_POLICY_ENTRY_CONTEXT    *Context

+  )

+{

+  EFI_STATUS    Status;

+  BOOLEAN       CreateNew;

+

+  if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {

+    ASSERT (Context->DataType < 3);

+

+    Status = mCombinePolicyEntry[Context->DataType] (

+               Selector,

+               Data,

+               Context->Selector,

+               Context->Data,

+               Context->Mask,

+               &CreateNew

+               );

+    if (!EFI_ERROR (Status)) {

+      if (CreateNew) {

+        //

+        // Insert new entry before old entry

+        //

+        Status = mIpSecConfig->SetData (

+                                 mIpSecConfig,

+                                 Context->DataType,

+                                 Context->Selector,

+                                 Data,

+                                 Selector

+                                 );

+        ASSERT_EFI_ERROR (Status);

+        //

+        // Delete old entry

+        //

+        Status = mIpSecConfig->SetData (

+                                 mIpSecConfig,

+                                 Context->DataType,

+                                 Selector,

+                                 NULL,

+                                 NULL

+                                 );

+        ASSERT_EFI_ERROR (Status);

+      } else {

+        Status = mIpSecConfig->SetData (

+                                 mIpSecConfig,

+                                 Context->DataType,

+                                 Context->Selector,

+                                 Data,

+                                 NULL

+                                 );

+      }

+    }

+

+    Context->Status = Status;

+    return EFI_ABORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Edit entry information in database according to datatype.

+

+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS             Edit entry information successfully.

+  @retval EFI_NOT_FOUND           Can't find the specified entry.

+  @retval Others                  Some mistaken case.

+**/

+EFI_STATUS

+EditPolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN LIST_ENTRY                    *ParamPackage

+  )

+{

+  EFI_STATUS                   Status;

+  EDIT_POLICY_ENTRY_CONTEXT    Context;

+  CONST CHAR16                 *ValueStr;

+

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");

+  if (ValueStr == NULL) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);

+    return EFI_NOT_FOUND;

+  }

+

+  Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);

+  if (!EFI_ERROR (Status)) {

+    Context.DataType = DataType;

+    Context.Status   = EFI_NOT_FOUND;

+    Status = mCreatePolicyEntry[DataType] (&Context.Selector, &Context.Data, ParamPackage, &Context.Mask, FALSE);

+    if (!EFI_ERROR (Status)) {

+      ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) EditOperatePolicyEntry, &Context);

+      Status = Context.Status;

+    }

+

+    if (Context.Selector != NULL) {

+      gBS->FreePool (Context.Selector);

+    }

+

+    if (Context.Data != NULL) {

+      gBS->FreePool (Context.Data);

+    }

+  }

+

+  if (Status == EFI_NOT_FOUND) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);

+  } else if (EFI_ERROR (Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_EDIT_FAILED), mHiiHandle, mAppName);

+  }

+

+  return Status;

+

+}

+

+/**

+  Insert entry information in database.

+

+  @param[in] Selector    The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.

+  @param[in] Data        The pointer to the data.

+  @param[in] Context     The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.

+

+  @retval EFI_SUCCESS    Continue the iteration.

+  @retval EFI_ABORTED    Abort the iteration.

+**/

+EFI_STATUS

+InsertPolicyEntry (

+  IN EFI_IPSEC_CONFIG_SELECTOR      *Selector,

+  IN VOID                           *Data,

+  IN INSERT_POLICY_ENTRY_CONTEXT    *Context

+  )

+{

+  //

+  // Found the entry which we want to insert before.

+  //

+  if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {

+

+    Context->Status = mIpSecConfig->SetData (

+                                      mIpSecConfig,

+                                      Context->DataType,

+                                      Context->Selector,

+                                      Context->Data,

+                                      Selector

+                                      );

+    //

+    // Abort the iteration after the insertion.

+    //

+    return EFI_ABORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Insert or add entry information in database according to datatype.

+

+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS             Insert or add entry information successfully.

+  @retval EFI_NOT_FOUND           Can't find the specified entry.

+  @retval EFI_BUFFER_TOO_SMALL    The entry already existed.

+  @retval EFI_UNSUPPORTED         The operation is not supported.

+  @retval Others                  Some mistaken case.

+**/

+EFI_STATUS

+AddOrInsertPolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN LIST_ENTRY                    *ParamPackage

+  )

+{

+  EFI_STATUS                     Status;

+  EFI_IPSEC_CONFIG_SELECTOR      *Selector;

+  VOID                           *Data;

+  INSERT_POLICY_ENTRY_CONTEXT    Context;

+  UINT32                         Mask;

+  UINTN                          DataSize;

+  CONST CHAR16                   *ValueStr;

+

+  Status = mCreatePolicyEntry[DataType] (&Selector, &Data, ParamPackage, &Mask, TRUE);

+  if (!EFI_ERROR (Status)) {

+    //

+    // Find if the Selector to be inserted already exists.

+    //

+    DataSize = 0;

+    Status = mIpSecConfig->GetData (

+                             mIpSecConfig,

+                             DataType,

+                             Selector,

+                             &DataSize,

+                             NULL

+                             );

+    if (Status == EFI_BUFFER_TOO_SMALL) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_EXISTS), mHiiHandle, mAppName);

+    } else if (ShellCommandLineGetFlag (ParamPackage, L"-a")) {

+      Status = mIpSecConfig->SetData (

+                               mIpSecConfig,

+                               DataType,

+                               Selector,

+                               Data,

+                               NULL

+                               );

+    } else {

+      ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");

+      if (ValueStr == NULL) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);

+        return EFI_NOT_FOUND;

+      }

+

+      Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);

+      if (!EFI_ERROR (Status)) {

+        Context.DataType  = DataType;

+        Context.Status    = EFI_NOT_FOUND;

+        Context.Selector  = Selector;

+        Context.Data      = Data;

+

+        ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) InsertPolicyEntry, &Context);

+        Status = Context.Status;

+        if (Status == EFI_NOT_FOUND) {

+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);

+        }

+      }

+    }

+

+    gBS->FreePool (Selector);

+    gBS->FreePool (Data);

+  }

+

+  if (Status == EFI_UNSUPPORTED) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_UNSUPPORT), mHiiHandle, mAppName);

+  } else if (EFI_ERROR (Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_FAILED), mHiiHandle, mAppName);

+  }

+

+  return Status;

+}

diff --git a/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h
new file mode 100644
index 0000000..5161bac
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h
@@ -0,0 +1,158 @@
+/** @file

+  The function declaration of policy entry operation in IpSecConfig application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _POLICY_ENTRY_OPERATION_H_

+#define _POLICY_ENTRY_OPERATION_H_

+

+#define LOCAL              BIT(0)

+#define REMOTE             BIT(1)

+#define PROTO              BIT(2)

+#define LOCAL_PORT         BIT(3)

+#define REMOTE_PORT        BIT(4)

+#define ICMP_TYPE          BIT(5)

+#define ICMP_CODE          BIT(6)

+#define NAME               BIT(7)

+#define PACKET_FLAG        BIT(8)

+#define ACTION             BIT(9)

+#define EXT_SEQUENCE       BIT(10)

+#define SEQUENCE_OVERFLOW  BIT(11)

+#define FRAGMENT_CHECK     BIT(12)

+#define LIFEBYTE           BIT(13)

+#define LIFETIME_SOFT      BIT(14)

+#define LIFETIME           BIT(15)

+#define MODE               BIT(16)

+#define TUNNEL_LOCAL       BIT(17)

+#define TUNNEL_REMOTE      BIT(18)

+#define DONT_FRAGMENT      BIT(19)

+#define IPSEC_PROTO        BIT(20)

+#define AUTH_ALGO          BIT(21)

+#define ENCRYPT_ALGO       BIT(22)

+#define SPI                BIT(23)

+#define DEST               BIT(24)

+#define SEQUENCE_NUMBER    BIT(25)

+#define ANTIREPLAY_WINDOW  BIT(26)

+#define AUTH_KEY           BIT(27)

+#define ENCRYPT_KEY        BIT(28)

+#define PATH_MTU           BIT(29)

+

+#define PEER_ID            BIT(0)

+#define PEER_ADDRESS       BIT(1)

+#define AUTH_PROTO         BIT(2)

+#define AUTH_METHOD        BIT(3)

+#define IKE_ID             BIT(4)

+#define AUTH_DATA          BIT(5)

+#define REVOCATION_DATA    BIT(6)

+

+typedef struct {

+  EFI_IPSEC_CONFIG_DATA_TYPE    DataType;

+  EFI_IPSEC_CONFIG_SELECTOR     *Selector;    // Data to be inserted.

+  VOID                          *Data;

+  UINT32                        Mask;

+  POLICY_ENTRY_INDEXER          Indexer;

+  EFI_STATUS                    Status;       // Indicate whether insertion succeeds.

+} EDIT_POLICY_ENTRY_CONTEXT;

+

+typedef struct {

+  EFI_IPSEC_CONFIG_DATA_TYPE    DataType;

+  EFI_IPSEC_CONFIG_SELECTOR     *Selector;    // Data to be inserted.

+  VOID                          *Data;

+  POLICY_ENTRY_INDEXER          Indexer;

+  EFI_STATUS                    Status;       // Indicate whether insertion succeeds.

+} INSERT_POLICY_ENTRY_CONTEXT;

+

+/**

+  The prototype for the CreateSpdEntry()/CreateSadEntry()/CreatePadEntry().

+  Fill in EFI_IPSEC_CONFIG_SELECTOR and corresponding data thru ParamPackage list.

+

+  @param[out] Selector        The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.

+  @param[out] Data            The pointer to corresponding data.

+  @param[in]  ParamPackage    The pointer to the ParamPackage list.

+  @param[out] Mask            The pointer to the Mask.

+  @param[in]  CreateNew       The switch to create new.

+

+  @retval EFI_SUCCESS              Filled in EFI_IPSEC_CONFIG_SELECTOR and corresponding data successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

+

+**/

+typedef

+EFI_STATUS

+(*CREATE_POLICY_ENTRY) (

+  OUT EFI_IPSEC_CONFIG_SELECTOR    **Selector,

+  OUT VOID                         **Data,

+  IN  LIST_ENTRY                   *ParamPackage,

+  OUT UINT32                       *Mask,

+  IN  BOOLEAN                      CreateNew

+  );

+

+/**

+  The prototype for the CombineSpdEntry()/CombineSadEntry()/CombinePadEntry().

+  Combine old SPD/SAD/PAD entry with new SPD/SAD/PAD entry.

+

+  @param[in, out] OldSelector    The pointer to the old EFI_IPSEC_CONFIG_SELECTOR union.

+  @param[in, out] OldData        The pointer to the corresponding old data.

+  @param[in]      NewSelector    The pointer to the new EFI_IPSEC_CONFIG_SELECTOR union.

+  @param[in]      NewData        The pointer to the corresponding new data.

+  @param[in]      Mask           The pointer to the Mask.

+  @param[out]     CreateNew      The switch to create new.

+

+  @retval EFI_SUCCESS              Combined successfully.

+  @retval EFI_INVALID_PARAMETER    Invalid user input parameter.

+

+**/

+typedef

+EFI_STATUS

+(* COMBINE_POLICY_ENTRY) (

+  EFI_IPSEC_CONFIG_SELECTOR    *OldSelector,

+  VOID                         *OldData,

+  EFI_IPSEC_CONFIG_SELECTOR    *NewSelector,

+  VOID                         *NewData,

+  UINT32                       Mask,

+  BOOLEAN                      *CreateNew

+  );

+

+/**

+  Insert or add entry information in database according to datatype.

+

+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS             Insert or add entry information successfully.

+  @retval EFI_NOT_FOUND           Can't find the specified entry.

+  @retval EFI_BUFFER_TOO_SMALL    The entry already existed.

+  @retval EFI_UNSUPPORTED         The operation is not supported./

+  @retval Others                  Some mistaken case.

+**/

+EFI_STATUS

+AddOrInsertPolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN LIST_ENTRY                    *ParamPackage

+  );

+

+/**

+  Edit entry information in the database according to datatype.

+

+  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.

+  @param[in] ParamPackage    The pointer to the ParamPackage list.

+

+  @retval EFI_SUCCESS             Edit entry information successfully.

+  @retval EFI_NOT_FOUND           Can't find the specified entry.

+  @retval Others                  Some mistaken case.

+**/

+EFI_STATUS

+EditPolicyEntry (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,

+  IN LIST_ENTRY                    *ParamPackage

+  );

+#endif

diff --git a/NetworkPkg/Application/Ping6/Ia32/Tsc.c b/NetworkPkg/Application/Ping6/Ia32/Tsc.c
new file mode 100644
index 0000000..e2eae99
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ia32/Tsc.c
@@ -0,0 +1,28 @@
+/** @file

+  The implement to read TSC in IA32 platform.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include <Library/BaseLib.h>

+

+/**

+  Reads and returns the current value of the Time Stamp Counter (TSC).

+

+  @return The current value of TSC.

+

+**/

+UINT64

+ReadTime ()

+{

+  return AsmReadTsc ();

+}

diff --git a/NetworkPkg/Application/Ping6/Ipf/Itc.c b/NetworkPkg/Application/Ping6/Ipf/Itc.c
new file mode 100644
index 0000000..131e5c0
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ipf/Itc.c
@@ -0,0 +1,28 @@
+/** @file

+  The implement to read ITC in IA64 platform.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include <Library/BaseLib.h>

+

+/**

+  Reads and returns the current value of the Interval Timer Counter Register (ITC).

+

+  @return The current value of ITC.

+

+**/

+UINT64

+ReadTime ()

+{

+  return AsmReadItc ();

+}

diff --git a/NetworkPkg/Application/Ping6/Ping6.c b/NetworkPkg/Application/Ping6/Ping6.c
new file mode 100644
index 0000000..b783c5a
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ping6.c
@@ -0,0 +1,1179 @@
+/** @file

+  The implementation for Ping6 application.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include <Library/ShellLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/BaseLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/DebugLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/HiiLib.h>

+#include <Library/NetLib.h>

+

+#include <Protocol/Cpu.h>

+#include <Protocol/ServiceBinding.h>

+#include <Protocol/Ip6.h>

+#include <Protocol/Ip6Config.h>

+

+#include "Ping6.h"

+

+SHELL_PARAM_ITEM    Ping6ParamList[] = {

+  {

+    L"-l",

+    TypeValue

+  },

+  {

+    L"-n",

+    TypeValue

+  },

+  {

+    L"-s",

+    TypeValue

+  },

+  {

+    L"-?",

+    TypeFlag

+  },

+  {

+    NULL,

+    TypeMax

+  },

+};

+

+//

+// Global Variables in Ping6 application.

+//

+EFI_HII_HANDLE    mHiiHandle;

+CONST CHAR16      *mIp6DstString;

+CONST CHAR16      *mIp6SrcString;

+EFI_GUID          mEfiPing6Guid = EFI_PING6_GUID;

+UINT32            mFrequency = 0;

+/**

+  Get and caculate the frequency in tick/ms.

+  The result is saved in the globle variable mFrequency

+

+  @retval EFI_SUCCESS    Caculated the frequency successfully.

+  @retval Others         Failed to caculate the frequency.

+

+**/

+EFI_STATUS

+Ping6GetFrequency (

+  VOID

+  )

+{

+  EFI_STATUS               Status;

+  EFI_CPU_ARCH_PROTOCOL    *Cpu;

+  UINT64                   CurrentTick;

+  UINT32                   TimerPeriod;

+

+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = Cpu->GetTimerValue (Cpu, 0, &CurrentTick, (UINT64 *) &TimerPeriod);

+

+  if (EFI_ERROR (Status)) {

+    //

+    // For NT32 Simulator only. 358049 is a similar value to keep timer granularity.

+    // Set the timer period by ourselves.

+    //

+    TimerPeriod = NTTIMERPERIOD;

+  }

+  //

+  // The timer period is in femtosecond (1 femtosecond is 1e-15 second).

+  // So 1e+12 is divided by timer period to produce the freq in tick/ms.

+  //

+  mFrequency = (UINT32) DivU64x32 (1000000000000ULL, TimerPeriod);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Get and caculate the duration in ms.

+

+  @param[in]  Begin    The start point of time.

+  @param[in]  End      The end point of time.

+

+  @return The duration in ms.

+

+**/

+UINT32

+Ping6CalculateTick (

+  IN UINT64    Begin,

+  IN UINT64    End

+  )

+{

+  ASSERT (End > Begin);

+  return (UINT32) DivU64x32 (End - Begin, mFrequency);

+}

+

+/**

+  Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.

+

+  @param[in]    TxInfo    The pointer to PING6_ICMP6_TX_INFO.

+

+**/

+VOID

+Ping6DestroyTxInfo (

+  IN PING6_ICMP6_TX_INFO    *TxInfo

+  )

+{

+  EFI_IP6_TRANSMIT_DATA    *TxData;

+  EFI_IP6_FRAGMENT_DATA    *FragData;

+  UINTN                    Index;

+

+  ASSERT (TxInfo != NULL);

+

+  if (TxInfo->Token != NULL) {

+

+    if (TxInfo->Token->Event != NULL) {

+      gBS->CloseEvent (TxInfo->Token->Event);

+    }

+

+    TxData = TxInfo->Token->Packet.TxData;

+    if (TxData != NULL) {

+

+      if (TxData->OverrideData != NULL) {

+        FreePool (TxData->OverrideData);

+      }

+

+      if (TxData->ExtHdrs != NULL) {

+        FreePool (TxData->ExtHdrs);

+      }

+

+      for (Index = 0; Index < TxData->FragmentCount; Index++) {

+        FragData = TxData->FragmentTable[Index].FragmentBuffer;

+        if (FragData != NULL) {

+          FreePool (FragData);

+        }

+      }

+    }

+

+    FreePool (TxInfo->Token);

+  }

+

+  FreePool (TxInfo);

+}

+

+/**

+  Match the request, and reply with SequenceNum/TimeStamp.

+

+  @param[in]    Private    The pointer to PING6_PRIVATE_DATA.

+  @param[in]    Packet     The pointer to ICMP6_ECHO_REQUEST_REPLY.

+

+  @retval EFI_SUCCESS      The match is successful.

+  @retval EFI_NOT_FOUND    The reply can't be matched with any request.

+

+**/

+EFI_STATUS

+Ping6MatchEchoReply (

+  IN PING6_PRIVATE_DATA          *Private,

+  IN ICMP6_ECHO_REQUEST_REPLY    *Packet

+  )

+{

+  PING6_ICMP6_TX_INFO    *TxInfo;

+  LIST_ENTRY             *Entry;

+  LIST_ENTRY             *NextEntry;

+

+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {

+    TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);

+

+    if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {

+      Private->RxCount++;

+      RemoveEntryList (&TxInfo->Link);

+      Ping6DestroyTxInfo (TxInfo);

+      return EFI_SUCCESS;

+    }

+  }

+

+  return EFI_NOT_FOUND;

+}

+

+/**

+  The original intention is to send a request.

+  Currently, the application retransmits an icmp6 echo request packet

+  per second in sendnumber times that is specified by the user.

+  Because nothing can be done here, all things move to the timer rountine.

+

+  @param[in]    Event      A EFI_EVENT type event.

+  @param[in]    Context    The pointer to Context.

+

+**/

+VOID

+EFIAPI

+Ping6OnEchoRequestSent (

+  IN EFI_EVENT    Event,

+  IN VOID         *Context

+  )

+{

+}

+

+/**

+  receive reply, match and print reply infomation.

+

+  @param[in]    Event      A EFI_EVENT type event.

+  @param[in]    Context    The pointer to context.

+

+**/

+VOID

+EFIAPI

+Ping6OnEchoReplyReceived (

+  IN EFI_EVENT    Event,

+  IN VOID         *Context

+  )

+{

+  EFI_STATUS                  Status;

+  PING6_PRIVATE_DATA          *Private;

+  EFI_IP6_COMPLETION_TOKEN    *RxToken;

+  EFI_IP6_RECEIVE_DATA        *RxData;

+  ICMP6_ECHO_REQUEST_REPLY    *Reply;

+  UINT32                      PayLoad;

+  UINT32                      Rtt;

+  CHAR8                       Near;

+

+  Private = (PING6_PRIVATE_DATA *) Context;

+

+  if (Private->Status == EFI_ABORTED) {

+    return;

+  }

+

+  RxToken = &Private->RxToken;

+  RxData  = RxToken->Packet.RxData;

+  Reply   = RxData->FragmentTable[0].FragmentBuffer;

+  PayLoad = RxData->DataLength;

+

+  if (RxData->Header->NextHeader != IP6_ICMP) {

+    goto ON_EXIT;

+  }

+

+  if (!IP6_IS_MULTICAST (&Private->DstAddress) && 

+      !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {

+    goto ON_EXIT;

+  }

+

+  if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {

+    goto ON_EXIT;

+  }

+

+  if (PayLoad != Private->BufferSize) {

+    goto ON_EXIT;

+  }

+  //

+  // Check whether the reply matches the sent request before.

+  //

+  Status = Ping6MatchEchoReply (Private, Reply);

+  if (EFI_ERROR(Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // Display statistics on this icmp6 echo reply packet.

+  //

+  Rtt  = Ping6CalculateTick (Reply->TimeStamp, ReadTime ());

+  if (Rtt != 0) {

+    Near = (CHAR8) '=';

+  } else {

+    Near = (CHAR8) '<';

+  }

+

+  Private->RttSum += Rtt;

+  Private->RttMin  = Private->RttMin > Rtt ? Rtt : Private->RttMin;

+  Private->RttMax  = Private->RttMax < Rtt ? Rtt : Private->RttMax;

+

+  ShellPrintHiiEx (

+    -1,

+    -1,

+    NULL,

+    STRING_TOKEN (STR_PING6_REPLY_INFO),

+    mHiiHandle,

+    PayLoad,

+    mIp6DstString,

+    Reply->SequenceNum,

+    RxData->Header->HopLimit,

+    Near,

+    Rtt

+    );

+

+ON_EXIT:

+

+  if (Private->RxCount < Private->SendNum) {

+    //

+    // Continue to receive icmp6 echo reply packets.

+    //

+    RxToken->Status = EFI_ABORTED;

+

+    Status = Private->Ip6->Receive (Private->Ip6, RxToken);

+

+    if (EFI_ERROR (Status)) {

+      Private->Status = EFI_ABORTED;

+    }

+  } else {

+    //

+    // All reply have already been received from the dest host.

+    //

+    Private->Status = EFI_SUCCESS;

+  }

+  //

+  // Singal to recycle the each rxdata here, not at the end of process.

+  //

+  gBS->SignalEvent (RxData->RecycleSignal);

+}

+

+/**

+  Initial EFI_IP6_COMPLETION_TOKEN.

+

+  @param[in]    Private        The pointer of PING6_PRIVATE_DATA.

+  @param[in]    TimeStamp      The TimeStamp of request.

+  @param[in]    SequenceNum    The SequenceNum of request.

+

+  @return The pointer of EFI_IP6_COMPLETION_TOKEN.

+

+**/

+EFI_IP6_COMPLETION_TOKEN *

+Ping6GenerateToken (

+  IN PING6_PRIVATE_DATA    *Private,

+  IN UINT64                TimeStamp,

+  IN UINT16                SequenceNum

+  )

+{

+  EFI_STATUS                  Status;

+  EFI_IP6_COMPLETION_TOKEN    *Token;

+  EFI_IP6_TRANSMIT_DATA       *TxData;

+  ICMP6_ECHO_REQUEST_REPLY    *Request;

+

+  Request = AllocateZeroPool (Private->BufferSize);

+

+  if (Request == NULL) {

+    return NULL;

+  }

+  //

+  // Assembly icmp6 echo request packet.

+  //

+  Request->Type        = ICMP_V6_ECHO_REQUEST;

+  Request->Code        = 0;

+  Request->SequenceNum = SequenceNum;

+  Request->TimeStamp   = TimeStamp;

+  Request->Identifier  = 0;

+  //

+  // Leave check sum to ip6 layer, since it has no idea of source address

+  // selection.

+  //

+  Request->Checksum    = 0;

+

+  TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));

+

+  if (TxData == NULL) {

+    FreePool (Request);

+    return NULL;

+  }

+  //

+  // Assembly ipv6 token for transmit.

+  //

+  TxData->OverrideData       = 0;

+  TxData->ExtHdrsLength      = 0;

+  TxData->ExtHdrs            = NULL;

+  TxData->DataLength         = Private->BufferSize;

+  TxData->FragmentCount      = 1;

+  TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request;

+  TxData->FragmentTable[0].FragmentLength = Private->BufferSize;

+

+  Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));

+

+  if (Token == NULL) {

+    FreePool (Request);

+    FreePool (TxData);

+    return NULL;

+  }

+

+  Token->Status         = EFI_ABORTED;

+  Token->Packet.TxData  = TxData;

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_CALLBACK,

+                  Ping6OnEchoRequestSent,

+                  Private,

+                  &Token->Event

+                  );

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Request);

+    FreePool (TxData);

+    FreePool (Token);

+    return NULL;

+  }

+

+  return Token;

+}

+

+/**

+  Transmit the EFI_IP6_COMPLETION_TOKEN.

+

+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.

+

+  @retval EFI_SUCCESS             Transmitted successfully.

+  @retval EFI_OUT_OF_RESOURCES    No memory is available on the platform.

+  @retval others                  Transmitted unsuccessfully.

+

+**/

+EFI_STATUS

+Ping6SendEchoRequest (

+  IN PING6_PRIVATE_DATA    *Private

+  )

+{

+  EFI_STATUS             Status;

+  PING6_ICMP6_TX_INFO    *TxInfo;

+

+  TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));

+

+  if (TxInfo == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  TxInfo->TimeStamp   = ReadTime ();

+  TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);

+

+  TxInfo->Token       = Ping6GenerateToken (

+                          Private,

+                          TxInfo->TimeStamp,

+                          TxInfo->SequenceNum

+                          );

+

+  if (TxInfo->Token == NULL) {

+    Ping6DestroyTxInfo (TxInfo);

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);

+

+  if (EFI_ERROR (Status)) {

+    Ping6DestroyTxInfo (TxInfo);

+    return Status;

+  }

+

+  InsertTailList (&Private->TxList, &TxInfo->Link);

+  Private->TxCount++;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Place a completion token into the receive packet queue to receive the echo reply.

+

+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.

+

+  @retval EFI_SUCCESS      Put the token into the receive packet queue successfully.

+  @retval others           Put the token into the receive packet queue unsuccessfully.

+

+**/

+EFI_STATUS

+Ping6ReceiveEchoReply (

+  IN PING6_PRIVATE_DATA    *Private

+  )

+{

+  EFI_STATUS    Status;

+

+  ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN));

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_CALLBACK,

+                  Ping6OnEchoReplyReceived,

+                  Private,

+                  &Private->RxToken.Event

+                  );

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Private->RxToken.Status = EFI_NOT_READY;

+

+  return Private->Ip6->Receive (Private->Ip6, &Private->RxToken);

+}

+

+/**

+  Remove the timeout request from the list.

+

+  @param[in]    Event    A EFI_EVENT type event.

+  @param[in]    Context  The pointer to Context.

+

+**/

+VOID

+EFIAPI

+Ping6OnTimerRoutine (

+  IN EFI_EVENT    Event,

+  IN VOID         *Context

+  )

+{

+  EFI_STATUS             Status;

+  PING6_PRIVATE_DATA     *Private;

+  PING6_ICMP6_TX_INFO    *TxInfo;

+  LIST_ENTRY             *Entry;

+  LIST_ENTRY             *NextEntry;

+  UINT32                 Time;

+

+  Private = (PING6_PRIVATE_DATA *) Context;

+

+  //

+  // Retransmit icmp6 echo request packets per second in sendnumber times.

+  //

+  if (Private->TxCount < Private->SendNum) {

+

+    Status = Ping6SendEchoRequest (Private);

+    if (Private->TxCount != 0){

+      if (EFI_ERROR (Status)) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), mHiiHandle, Private->TxCount + 1);

+      }

+    }

+  }

+  //

+  // Check whether any icmp6 echo request in the list timeout.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {

+    TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);

+    Time   = Ping6CalculateTick (TxInfo->TimeStamp, ReadTime ());

+

+    //

+    // Remove the timeout echo request from txlist.

+    //

+    if (Time > PING6_DEFAULT_TIMEOUT) {

+

+      if (EFI_ERROR (TxInfo->Token->Status)) {

+        Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);

+      }

+      //

+      // Remove the timeout icmp6 echo request from list.

+      //

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), mHiiHandle, TxInfo->SequenceNum);

+

+      RemoveEntryList (&TxInfo->Link);

+      Ping6DestroyTxInfo (TxInfo);

+

+      if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {

+        //

+        // All the left icmp6 echo request in the list timeout.

+        //

+        Private->Status = EFI_TIMEOUT;

+      }

+    }

+  }

+}

+

+/**

+  Create a valid IP6 instance.

+

+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.

+

+  @retval EFI_SUCCESS              Create a valid IP6 instance successfully.

+  @retval EFI_ABORTED              Locate handle with ip6 service binding protocol unsuccessfully.

+  @retval EFI_INVALID_PARAMETER    The source address is unspecified when the destination address is a link -ocal address.

+  @retval EFI_OUT_OF_RESOURCES     No memory is available on the platform.

+  @retval EFI_NOT_FOUND            The source address is not found.

+**/

+EFI_STATUS

+Ping6CreateIp6Instance (

+  IN  PING6_PRIVATE_DATA    *Private

+  )

+{

+  EFI_STATUS                       Status;

+  UINTN                            HandleIndex;

+  UINTN                            HandleNum;

+  EFI_HANDLE                       *HandleBuffer;

+  EFI_SERVICE_BINDING_PROTOCOL     *Ip6Sb;

+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;

+  EFI_IP6_CONFIG_DATA              Ip6Config;

+  EFI_IP6_CONFIG_INTERFACE_INFO    *IfInfo;

+  UINTN                            IfInfoSize;

+  EFI_IPv6_ADDRESS                 *Addr;

+  UINTN                            AddrIndex;

+

+  HandleBuffer = NULL;

+  Ip6Sb        = NULL;

+  IfInfo       = NULL;

+  IfInfoSize   = 0;

+

+  //

+  // Locate all the handles with ip6 service binding protocol.

+  //

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiIp6ServiceBindingProtocolGuid,

+                  NULL,

+                  &HandleNum,

+                  &HandleBuffer

+                  );

+  if (EFI_ERROR (Status) || (HandleNum == 0)) {

+    return EFI_ABORTED;

+  }

+  //

+  // Source address is required when pinging a link-local address on multi-

+  // interfaces host.

+  //

+  if (NetIp6IsLinkLocalAddr (&Private->DstAddress) &&

+      NetIp6IsUnspecifiedAddr (&Private->SrcAddress) &&

+      (HandleNum > 1)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), mHiiHandle);

+    Status = EFI_INVALID_PARAMETER;

+    goto ON_ERROR;

+  }

+  //

+  // For each ip6 protocol, check interface addresses list.

+  //

+  for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {

+

+    Ip6Sb      = NULL;

+    IfInfo     = NULL;

+    IfInfoSize = 0;

+

+    Status = gBS->HandleProtocol (

+                    HandleBuffer[HandleIndex],

+                    &gEfiIp6ServiceBindingProtocolGuid,

+                    (VOID **) &Ip6Sb

+                    );

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+

+    if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {

+      //

+      // No need to match interface address.

+      //

+      break;

+    } else {

+      //

+      // Ip6config protocol and ip6 service binding protocol are installed

+      // on the same handle.

+      //

+      Status = gBS->HandleProtocol (

+                      HandleBuffer[HandleIndex],

+                      &gEfiIp6ConfigProtocolGuid,

+                      (VOID **) &Ip6Cfg

+                      );

+

+      if (EFI_ERROR (Status)) {

+        goto ON_ERROR;

+      }

+      //

+      // Get the interface information size.

+      //

+      Status = Ip6Cfg->GetData (

+                         Ip6Cfg,

+                         Ip6ConfigDataTypeInterfaceInfo,

+                         &IfInfoSize,

+                         NULL

+                         );

+

+      if (Status != EFI_BUFFER_TOO_SMALL) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);

+        goto ON_ERROR;

+      }

+

+      IfInfo = AllocateZeroPool (IfInfoSize);

+

+      if (IfInfo == NULL) {

+        Status = EFI_OUT_OF_RESOURCES;

+        goto ON_ERROR;

+      }

+      //

+      // Get the interface info.

+      //

+      Status = Ip6Cfg->GetData (

+                         Ip6Cfg,

+                         Ip6ConfigDataTypeInterfaceInfo,

+                         &IfInfoSize,

+                         IfInfo

+                         );

+

+      if (EFI_ERROR (Status)) {

+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);

+        goto ON_ERROR;

+      }

+      //

+      // Check whether the source address is one of the interface addresses.

+      //

+      for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {

+

+        Addr = &(IfInfo->AddressInfo[AddrIndex].Address);

+        if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {

+          //

+          // Match a certain interface address.

+          //

+          break;

+        }

+      }

+

+      if (AddrIndex < IfInfo->AddressInfoCount) {

+        //

+        // Found a nic handle with right interface address.

+        //

+        break;

+      }

+    }

+

+    FreePool (IfInfo);

+    IfInfo = NULL;

+  }

+  //

+  // No exact interface address matched.

+  //

+

+  if (HandleIndex == HandleNum) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SOURCE_NOT_FOUND), mHiiHandle, mIp6SrcString);

+    Status = EFI_NOT_FOUND;

+    goto ON_ERROR;

+  }

+

+  Private->NicHandle = HandleBuffer[HandleIndex];

+

+  ASSERT (Ip6Sb != NULL);

+  Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Ip6ChildHandle,

+                  &gEfiIp6ProtocolGuid,

+                  (VOID **) &Private->Ip6,

+                  Private->ImageHandle,

+                  Private->Ip6ChildHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));

+

+  //

+  // Configure the ip6 instance for icmp6 packet exchange.

+  //

+  Ip6Config.DefaultProtocol   = 58;

+  Ip6Config.AcceptAnyProtocol = FALSE;

+  Ip6Config.AcceptIcmpErrors  = TRUE;

+  Ip6Config.AcceptPromiscuous = FALSE;

+  Ip6Config.TrafficClass      = 0;

+  Ip6Config.HopLimit          = 128;

+  Ip6Config.FlowLabel         = 0;

+  Ip6Config.ReceiveTimeout    = 0;

+  Ip6Config.TransmitTimeout   = 0;

+

+  IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);

+

+  IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);

+

+  Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);

+

+  if (EFI_ERROR (Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), mHiiHandle, Status);

+    goto ON_ERROR;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+  if (HandleBuffer != NULL) {

+    FreePool (HandleBuffer);

+  }

+

+  if (IfInfo != NULL) {

+    FreePool (IfInfo);

+  }

+

+  if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {

+    Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);

+  }

+

+  return Status;

+}

+

+/**

+  Destory the IP6 instance.

+

+  @param[in]    Private    The pointer of PING6_PRIVATE_DATA.

+

+**/

+VOID

+Ping6DestoryIp6Instance (

+  IN PING6_PRIVATE_DATA    *Private

+  )

+{

+  EFI_STATUS                      Status;

+  EFI_SERVICE_BINDING_PROTOCOL    *Ip6Sb;

+

+  gBS->CloseProtocol (

+         Private->Ip6ChildHandle,

+         &gEfiIp6ProtocolGuid,

+         Private->ImageHandle,

+         Private->Ip6ChildHandle

+         );

+

+  Status = gBS->HandleProtocol (

+                  Private->NicHandle,

+                  &gEfiIp6ServiceBindingProtocolGuid,

+                  (VOID **) &Ip6Sb

+                  );

+

+  if (!EFI_ERROR(Status)) {

+    Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);

+  }

+}

+

+/**

+  The Ping6 Process.

+

+  @param[in]   ImageHandle    The firmware allocated handle for the UEFI image.

+  @param[in]   SendNumber     The send request count.

+  @param[in]   BufferSize     The send buffer size.

+  @param[in]   SrcAddress     The source IPv6 address.

+  @param[in]   DstAddress     The destination IPv6 address.

+

+  @retval EFI_SUCCESS    The ping6 processed successfullly.

+  @retval others         The ping6 processed unsuccessfully.

+

+**/

+EFI_STATUS

+Ping6 (

+  IN EFI_HANDLE          ImageHandle,

+  IN UINT32              SendNumber,

+  IN UINT32              BufferSize,

+  IN EFI_IPv6_ADDRESS    *SrcAddress,

+  IN EFI_IPv6_ADDRESS    *DstAddress

+  )

+{

+  EFI_STATUS             Status;

+  EFI_INPUT_KEY          Key;

+  PING6_PRIVATE_DATA     *Private;

+  PING6_ICMP6_TX_INFO    *TxInfo;

+  LIST_ENTRY             *Entry;

+  LIST_ENTRY             *NextEntry;

+

+  Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));

+

+  ASSERT (Private != NULL);

+

+  Private->ImageHandle = ImageHandle;

+  Private->SendNum     = SendNumber;

+  Private->BufferSize  = BufferSize;

+  Private->RttMin      = 0xFFFF;

+  Private->Status      = EFI_NOT_READY;

+

+  InitializeListHead (&Private->TxList);

+

+  IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);

+  IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);

+

+  //

+  // Open and configure a ip6 instance for ping6.

+  //

+  Status = Ping6CreateIp6Instance (Private);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // Print the command line itself.

+  //

+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), mHiiHandle, mIp6DstString, Private->BufferSize);

+  //

+  // Create a ipv6 token to receive the first icmp6 echo reply packet.

+  //

+  Status = Ping6ReceiveEchoReply (Private);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // Create and start timer to send icmp6 echo request packet per second.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,

+                  TPL_CALLBACK,

+                  Ping6OnTimerRoutine,

+                  Private,

+                  &Private->Timer

+                  );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // Create a ipv6 token to send the first icmp6 echo request packet.

+  //

+  Status = Ping6SendEchoRequest (Private);

+  //

+  // EFI_NOT_READY for IPsec is enable and IKE is not established.

+  //

+  if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {

+    if(Status == EFI_NOT_FOUND) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), mHiiHandle, mIp6DstString);

+    }

+

+    goto ON_EXIT;

+  }

+

+  Status = gBS->SetTimer (

+                  Private->Timer,

+                  TimerPeriodic,

+                  PING6_ONE_SECOND

+                  );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // Control the ping6 process by two factors:

+  // 1. Hot key

+  // 2. Private->Status

+  //   2.1. success means all icmp6 echo request packets get reply packets.

+  //   2.2. timeout means the last icmp6 echo reply request timeout to get reply.

+  //   2.3. noready means ping6 process is on-the-go.

+  //

+  while (Private->Status == EFI_NOT_READY) {

+    Private->Ip6->Poll (Private->Ip6);

+

+    //

+    // Terminate the ping6 process by 'esc' or 'ctl-c'.

+    //

+    Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);

+

+    if (!EFI_ERROR(Status)) {

+      if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||

+         ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {

+        goto ON_STAT;

+      }

+    }

+  }

+

+ON_STAT:

+  //

+  // Display the statistics in all.

+  //

+  gBS->SetTimer (Private->Timer, TimerCancel, 0);

+

+  if (Private->TxCount != 0) {

+    ShellPrintHiiEx (

+      -1,

+      -1,

+      NULL,

+      STRING_TOKEN (STR_PING6_STAT),

+      mHiiHandle,

+      Private->TxCount,

+      Private->RxCount,

+      (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,

+      Private->RttSum

+      );

+  }

+

+  if (Private->RxCount != 0) {

+    ShellPrintHiiEx (

+      -1,

+      -1,

+      NULL,

+      STRING_TOKEN (STR_PING6_RTT),

+      mHiiHandle,

+      Private->RttMin,

+      Private->RttMax,

+      Private->RttSum / Private->RxCount

+      );

+  }

+

+ON_EXIT:

+

+  if (Private != NULL) {

+    Private->Status = EFI_ABORTED;

+

+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {

+      TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);

+

+      Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);

+

+      RemoveEntryList (&TxInfo->Link);

+      Ping6DestroyTxInfo (TxInfo);

+    }

+

+    if (Private->Timer != NULL) {

+      gBS->CloseEvent (Private->Timer);

+    }

+

+    if (Private->Ip6 != NULL) {

+      Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);

+    }

+

+    if (Private->RxToken.Event != NULL) {

+      gBS->CloseEvent (Private->RxToken.Event);

+    }

+

+    if (Private->Ip6ChildHandle != NULL) {

+      Ping6DestoryIp6Instance (Private);

+    }

+

+    FreePool (Private);

+  }

+

+  return Status;

+}

+

+/**

+  This is the declaration of an EFI image entry point. This entry point is

+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including

+  both device drivers and bus drivers.

+

+  The entry point for the Ping6 application that parses the command line input and calls the Ping6 process.

+

+  @param[in] ImageHandle    The firmware allocated handle for the UEFI image.

+  @param[in] SystemTable    A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS               The operation completed successfully.

+  @retval EFI_INVALID_PARAMETETR    Input parameters combination is invalid.

+  @retval Others                    Some errors occur.

+

+**/

+EFI_STATUS

+EFIAPI

+InitializePing6 (

+  IN  EFI_HANDLE          ImageHandle,

+  IN  EFI_SYSTEM_TABLE    *SystemTable

+  )

+{

+  EFI_STATUS          Status;

+  EFI_IPv6_ADDRESS    DstAddress;

+  EFI_IPv6_ADDRESS    SrcAddress;

+  UINT64              BufferSize;

+  UINTN               SendNumber;

+  LIST_ENTRY          *ParamPackage;

+  CONST CHAR16        *ValueStr;

+  CONST CHAR16        *ValueStrPtr;

+  UINTN               NonOptionCount;

+

+  //

+  // Register our string package with HII and return the handle to it.

+  //

+  mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, Ping6Strings, NULL);

+  ASSERT (mHiiHandle != NULL);

+

+  Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, NULL, TRUE, FALSE);

+  if (EFI_ERROR(Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);

+    goto ON_EXIT;

+  }

+

+  if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_HELP), mHiiHandle);

+    goto ON_EXIT;

+  }

+

+  SendNumber = 10;

+  BufferSize = 16;

+

+  //

+  // Parse the paramter of count number.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");

+  ValueStrPtr = ValueStr;

+  if (ValueStr != NULL) {

+    SendNumber = ShellStrToUintn (ValueStrPtr);

+

+    //

+    // ShellStrToUintn will return 0 when input is 0 or an invalid input string.

+    //

+    if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), mHiiHandle, ValueStr);

+      Status = EFI_INVALID_PARAMETER;

+      goto ON_EXIT;

+    }

+  }

+  //

+  // Parse the paramter of buffer size.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");

+  ValueStrPtr = ValueStr;

+  if (ValueStr != NULL) {

+    BufferSize = ShellStrToUintn (ValueStrPtr);

+

+    //

+    // ShellStrToUintn will return 0 when input is 0 or an invalid input string.

+    //

+    if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), mHiiHandle, ValueStr);

+      Status = EFI_INVALID_PARAMETER;

+      goto ON_EXIT;

+    }

+  }

+

+  ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));

+  ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));

+

+  //

+  // Parse the paramter of source ip address.

+  //

+  ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");

+  ValueStrPtr = ValueStr;

+  if (ValueStr != NULL) {

+    mIp6SrcString = ValueStr;

+    Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);

+      Status = EFI_INVALID_PARAMETER;

+      goto ON_EXIT;

+    }

+  }

+  //

+  // Parse the paramter of destination ip address.

+  //

+  NonOptionCount = ShellCommandLineGetCount();

+  ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));

+  if (NonOptionCount != 2) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);

+    Status = EFI_INVALID_PARAMETER;

+    goto ON_EXIT;

+  }

+  ValueStrPtr = ValueStr;

+  if (ValueStr != NULL) {

+    mIp6DstString = ValueStr;

+    Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);

+    if (EFI_ERROR (Status)) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);

+      Status = EFI_INVALID_PARAMETER;

+      goto ON_EXIT;

+    }

+  }

+  //

+  // Get frequency to calculate the time from ticks.

+  //

+  Status = Ping6GetFrequency ();

+

+  if (EFI_ERROR(Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // Enter into ping6 process.

+  //

+  Status = Ping6 (

+             ImageHandle,

+             (UINT32)SendNumber,

+             (UINT32)BufferSize,

+             &SrcAddress,

+             &DstAddress

+             );

+

+ON_EXIT:

+  ShellCommandLineFreeVarList (ParamPackage);

+  HiiRemovePackages (mHiiHandle);

+  return Status;

+}

diff --git a/NetworkPkg/Application/Ping6/Ping6.h b/NetworkPkg/Application/Ping6/Ping6.h
new file mode 100644
index 0000000..02271e1
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ping6.h
@@ -0,0 +1,92 @@
+/** @file

+  The interface function declaration of shell application Ping6 (Ping for v6 series).

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _PING6_H_

+#define _PING6_H_

+

+#define EFI_PING6_GUID \

+  { \

+    0x3f0b2478, 0x3619, 0x46c5, {0x81, 0x50, 0xa5, 0xab, 0xdd, 0xb6, 0x6b, 0xd9} \

+  }

+

+#define PING6_DEFAULT_TIMEOUT      5000

+#define PING6_MAX_SEND_NUMBER      10000

+#define PING6_MAX_BUFFER_SIZE      32768

+#define PING6_ONE_SECOND           10000000

+

+//

+// A similar amount of time that passes in femtoseconds

+// for each increment of TimerValue. It is for NT32 only.

+//

+#define NTTIMERPERIOD    358049

+

+#pragma pack(1)

+

+typedef struct _ICMP6_ECHO_REQUEST_REPLY {

+  UINT8                       Type;

+  UINT8                       Code;

+  UINT16                      Checksum;

+  UINT16                      Identifier;

+  UINT16                      SequenceNum;

+  UINT64                      TimeStamp;

+  UINT8                       Data[1];

+} ICMP6_ECHO_REQUEST_REPLY;

+

+#pragma pack()

+

+typedef struct _PING6_ICMP6_TX_INFO {

+  LIST_ENTRY                  Link;

+  UINT16                      SequenceNum;

+  UINT64                      TimeStamp;

+  EFI_IP6_COMPLETION_TOKEN    *Token;

+} PING6_ICMP6_TX_INFO;

+

+typedef struct _PING6_PRIVATE_DATA {

+  EFI_HANDLE                  ImageHandle;

+  EFI_HANDLE                  NicHandle;

+  EFI_HANDLE                  Ip6ChildHandle;

+  EFI_IP6_PROTOCOL            *Ip6;

+  EFI_EVENT                   Timer;

+

+  EFI_STATUS                  Status;

+  LIST_ENTRY                  TxList;

+  EFI_IP6_COMPLETION_TOKEN    RxToken;

+  UINT16                      RxCount;

+  UINT16                      TxCount;

+  UINT32                      RttSum;

+  UINT32                      RttMin;

+  UINT32                      RttMax;

+  UINT32                      SequenceNum;

+

+  EFI_IPv6_ADDRESS            SrcAddress;

+  EFI_IPv6_ADDRESS            DstAddress;

+  UINT32                      SendNum;

+  UINT32                      BufferSize;

+} PING6_PRIVATE_DATA;

+

+/**

+  Reads and returns the current value of register.

+  In IA64, the register is the Interval Timer Vector (ITV).

+  In X86(IA32/X64), the register is the Time Stamp Counter (TSC)

+

+  @return The current value of the register.

+

+**/

+UINT64

+ReadTime (

+  VOID

+  );

+

+#endif

diff --git a/NetworkPkg/Application/Ping6/Ping6.inf b/NetworkPkg/Application/Ping6/Ping6.inf
new file mode 100644
index 0000000..56b2163
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ping6.inf
@@ -0,0 +1,64 @@
+## @file

+#  Component description file for Ping6 application.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010006

+  BASE_NAME                      = Ping6

+  FILE_GUID                      = F35F733F-5235-4d7b-83FA-97780CEBCB20

+  MODULE_TYPE                    = UEFI_APPLICATION

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = InitializePing6

+

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF

+#

+

+[Sources]

+  Ping6.c

+  Ping6Strings.uni

+  Ping6.h

+

+[Sources.IA32]

+  Ia32/Tsc.c

+

+[Sources.X64]

+  X64/Tsc.c

+

+[Sources.IPF]

+  Ipf/Itc.c

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+  ShellPkg/ShellPkg.dec

+

+[LibraryClasses]

+  BaseLib

+  UefiBootServicesTableLib

+  UefiApplicationEntryPoint

+  BaseMemoryLib

+  ShellLib

+  MemoryAllocationLib

+  DebugLib

+  HiiLib

+  NetLib

+

+[Protocols]

+  gEfiCpuArchProtocolGuid                       ## CONSUMS

+  gEfiIp6ProtocolGuid                           ## CONSUMS

+  gEfiIp6ServiceBindingProtocolGuid             ## CONSUMS

+  gEfiIp6ConfigProtocolGuid                     ## CONSUMS

diff --git a/NetworkPkg/Application/Ping6/Ping6Strings.uni b/NetworkPkg/Application/Ping6/Ping6Strings.uni
new file mode 100644
index 0000000..c8b919c
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ping6Strings.uni
Binary files differ
diff --git a/NetworkPkg/Application/Ping6/X64/Tsc.c b/NetworkPkg/Application/Ping6/X64/Tsc.c
new file mode 100644
index 0000000..b3e7bdb
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/X64/Tsc.c
@@ -0,0 +1,28 @@
+/** @file

+  The implement to read TSC in X64 platform.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include <Library/BaseLib.h>

+

+/**

+  Reads and returns the current value of Time Stamp Counter (TSC).

+

+  @return The current value of TSC

+

+**/

+UINT64

+ReadTime ()

+{

+  return AsmReadTsc ();

+}

diff --git a/NetworkPkg/Application/VConfig/VConfig.c b/NetworkPkg/Application/VConfig/VConfig.c
new file mode 100644
index 0000000..c948131
--- /dev/null
+++ b/NetworkPkg/Application/VConfig/VConfig.c
@@ -0,0 +1,668 @@
+/** @file

+  Shell application for VLAN configuration.

+

+  Copyright (C) 2009 - 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include <Uefi.h>

+

+#include <Protocol/VlanConfig.h>

+

+#include <Library/UefiApplicationEntryPoint.h>

+#include <Library/UefiLib.h>

+#include <Library/ShellLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/HiiLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/NetLib.h>

+

+#define INVALID_NIC_INDEX   0xffff

+#define INVALID_VLAN_ID     0xffff

+

+//

+// This is the generated String package data for all .UNI files.

+// This data array is ready to be used as input of HiiAddPackages() to

+// create a packagelist (which contains Form packages, String packages, etc).

+//

+extern UINT8      VConfigStrings[];

+

+EFI_HANDLE        mImageHandle  = NULL;

+EFI_HII_HANDLE    mHiiHandle    = NULL;

+

+SHELL_PARAM_ITEM  mParamList[] = {

+  {

+    L"-l",

+    TypeValue

+  },

+  {

+    L"-a",

+    TypeMaxValue

+  },

+  {

+    L"-d",

+    TypeValue

+  },

+  {

+    NULL,

+    TypeMax

+  }

+};

+

+/**

+  Locate the network interface handle buffer.

+

+  @param[out]  NumberOfHandles Pointer to the number of handles.

+  @param[out]  HandleBuffer    Pointer to the buffer to store the returned handles.

+

+**/

+VOID

+LocateNicHandleBuffer (

+  OUT UINTN                       *NumberOfHandles,

+  OUT EFI_HANDLE                  **HandleBuffer

+  )

+{

+  EFI_STATUS  Status;

+

+  *NumberOfHandles  = 0;

+  *HandleBuffer     = NULL;

+

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiVlanConfigProtocolGuid,

+                  NULL,

+                  NumberOfHandles,

+                  HandleBuffer

+                  );

+  if (EFI_ERROR (Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_LOCATE_FAIL), mHiiHandle, Status);

+  }

+}

+

+/**

+  Extract the decimal index from the network interface name.

+

+  @param[in]  Name           Name of the network interface.

+

+  @retval INVALID_NIC_INDEX  Failed to extract the network interface index.

+  @return others             The network interface index.

+

+**/

+UINTN

+NicNameToIndex (

+  IN CHAR16                   *Name

+  )

+{

+  CHAR16  *Str;

+

+  Str = Name + 3;

+  if ((StrnCmp (Name, L"eth", 3) != 0) || (*Str == 0)) {

+    return INVALID_NIC_INDEX;

+  }

+

+  while (*Str != 0) {

+    if ((*Str < L'0') || (*Str > L'9')) {

+      return INVALID_NIC_INDEX;

+    }

+

+    Str++;

+  }

+

+  return (UINT16) StrDecimalToUintn (Name + 3);

+}

+

+/**

+  Find network interface device handle by its name.

+

+  @param[in]  Name           Name of the network interface.

+

+  @retval NULL               Cannot find the network interface.

+  @return others             Handle of the network interface.

+

+**/

+EFI_HANDLE

+NicNameToHandle (

+  IN CHAR16                   *Name

+  )

+{

+  UINTN       NumberOfHandles;

+  EFI_HANDLE  *HandleBuffer;

+  UINTN       Index;

+  EFI_HANDLE  Handle;

+

+  //

+  // Find all NIC handles.

+  //

+  LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer);

+  if (NumberOfHandles == 0) {

+    return NULL;

+  }

+

+  Index = NicNameToIndex (Name);

+  if (Index >= NumberOfHandles) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_IF), mHiiHandle, Name);

+    Handle = NULL;

+  } else {

+    Handle = HandleBuffer[Index];

+  }

+

+  FreePool (HandleBuffer);

+  return Handle;

+}

+

+/**

+  Open VlanConfig protocol from a handle.

+

+  @param[in]  Handle         The handle to open the VlanConfig protocol.

+

+  @return The VlanConfig protocol interface.

+

+**/

+EFI_VLAN_CONFIG_PROTOCOL *

+OpenVlanConfigProtocol (

+  IN EFI_HANDLE                 Handle

+  )

+{

+  EFI_VLAN_CONFIG_PROTOCOL  *VlanConfig;

+

+  VlanConfig = NULL;

+  gBS->OpenProtocol (

+         Handle,

+         &gEfiVlanConfigProtocolGuid,

+         (VOID **) &VlanConfig,

+         mImageHandle,

+         Handle,

+         EFI_OPEN_PROTOCOL_GET_PROTOCOL

+         );

+

+  return VlanConfig;

+}

+

+/**

+  Close VlanConfig protocol of a handle.

+

+  @param[in]  Handle         The handle to close the VlanConfig protocol.

+

+**/

+VOID

+CloseVlanConfigProtocol (

+  IN EFI_HANDLE                 Handle

+  )

+{

+  gBS->CloseProtocol (

+         Handle,

+         &gEfiVlanConfigProtocolGuid,

+         mImageHandle,

+         Handle

+         );

+}

+

+/**

+  Display VLAN configuration of a network interface.

+

+  @param[in]  Handle         Handle of the network interface.

+  @param[in]  NicIndex       Index of the network interface.

+

+**/

+VOID

+ShowNicVlanInfo (

+  IN EFI_HANDLE              Handle,

+  IN UINTN                   NicIndex

+  )

+{

+  CHAR16                    *MacStr;

+  EFI_STATUS                Status;

+  UINTN                     Index;

+  EFI_VLAN_CONFIG_PROTOCOL  *VlanConfig;

+  UINT16                    NumberOfVlan;

+  EFI_VLAN_FIND_DATA        *VlanData;

+

+  VlanConfig = OpenVlanConfigProtocol (Handle);

+  if (VlanConfig == NULL) {

+    return ;

+  }

+

+  MacStr  = NULL;

+  Status  = NetLibGetMacString (Handle, mImageHandle, &MacStr);

+  if (EFI_ERROR (Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_MAC_FAIL), mHiiHandle, Status);

+    goto Exit;

+  }

+

+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_ETH_MAC), mHiiHandle, NicIndex, MacStr);

+

+  Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData);

+  if (EFI_ERROR (Status)) {

+    if (Status == EFI_NOT_FOUND) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VLAN), mHiiHandle);

+    } else {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_FIND_FAIL), mHiiHandle, Status);

+    }

+

+    goto Exit;

+  }

+

+  for (Index = 0; Index < NumberOfVlan; Index++) {

+    ShellPrintHiiEx (

+      -1,

+      -1,

+      NULL,

+      STRING_TOKEN (STR_VCONFIG_VLAN_DISPLAY),

+      mHiiHandle,

+      VlanData[Index].VlanId,

+      VlanData[Index].Priority

+      );

+  }

+

+  FreePool (VlanData);

+

+Exit:

+  CloseVlanConfigProtocol (Handle);

+

+  if (MacStr != NULL) {

+    FreePool (MacStr);

+  }

+}

+

+/**

+  Display the VLAN configuration of all, or a specified network interface.

+

+  @param[in]  Name           Name of the network interface. If NULL, the VLAN

+                             configuration of all network will be displayed.

+

+**/

+VOID

+DisplayVlan (

+  IN CHAR16              *Name OPTIONAL

+  )

+{

+  UINTN       NumberOfHandles;

+  EFI_HANDLE  *HandleBuffer;

+  UINTN       Index;

+  EFI_HANDLE  NicHandle;

+

+  if (Name != NULL) {

+    //

+    // Display specified NIC

+    //

+    NicHandle = NicNameToHandle (Name);

+    if (NicHandle == NULL) {

+      return ;

+    }

+

+    ShowNicVlanInfo (NicHandle, 0);

+    return ;

+  }

+

+  //

+  // Find all NIC handles

+  //

+  LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer);

+  if (NumberOfHandles == 0) {

+    return ;

+  }

+

+  for (Index = 0; Index < NumberOfHandles; Index++) {

+    ShowNicVlanInfo (HandleBuffer[Index], Index);

+  }

+

+  FreePool (HandleBuffer);

+}

+

+/**

+  Convert a NULL-terminated unicode decimal VLAN ID string to VLAN ID.

+

+  @param[in]  String       Pointer to VLAN ID string from user input.

+

+  @retval Value translated from String, or INVALID_VLAN_ID is string is invalid.

+

+**/

+UINT16

+StrToVlanId (

+  IN CHAR16             *String

+  )

+{

+  CHAR16  *Str;

+

+  if (String == NULL) {

+    return INVALID_VLAN_ID;

+  }

+

+  Str = String;

+  while ((*Str >= '0') && (*Str <= '9')) {

+    Str++;

+  }

+

+  if (*Str != 0) {

+    return INVALID_VLAN_ID;

+  }

+

+  return (UINT16) StrDecimalToUintn (String);

+}

+

+/**

+  Add a VLAN device.

+

+  @param[in]  ParamStr       Parameter string from user input.

+

+**/

+VOID

+AddVlan (

+  IN CHAR16             *ParamStr

+  )

+{

+  CHAR16                    *Name;

+  CHAR16                    *VlanIdStr;

+  CHAR16                    *PriorityStr;

+  CHAR16                    *StrPtr;

+  BOOLEAN                   IsSpace;

+  UINTN                     VlanId;

+  UINTN                     Priority;

+  EFI_HANDLE                Handle;

+  EFI_HANDLE                VlanHandle;

+  EFI_VLAN_CONFIG_PROTOCOL  *VlanConfig;

+  EFI_STATUS                Status;

+

+  VlanConfig  = NULL;

+  Priority    = 0;

+

+  if (ParamStr == NULL) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle);

+    return ;

+  }

+

+  StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr);

+  if (StrPtr == NULL) {

+    return ;

+  }

+

+  Name        = StrPtr;

+  VlanIdStr   = NULL;

+  PriorityStr = NULL;

+  IsSpace     = FALSE;

+  while (*StrPtr != 0) {

+    if (*StrPtr == L' ') {

+      *StrPtr = 0;

+      IsSpace = TRUE;

+    } else {

+      if (IsSpace) {

+        //

+        // Start of a parameter.

+        //

+        if (VlanIdStr == NULL) {

+          //

+          // 2nd parameter is VLAN ID.

+          //

+          VlanIdStr = StrPtr;

+        } else if (PriorityStr == NULL) {

+          //

+          // 3rd parameter is Priority.

+          //

+          PriorityStr = StrPtr;

+        } else {

+          //

+          // Ignore else parameters.

+          //

+          break;

+        }

+      }

+

+      IsSpace = FALSE;

+    }

+

+    StrPtr++;

+  }

+

+  Handle = NicNameToHandle (Name);

+  if (Handle == NULL) {

+    goto Exit;

+  }

+

+  VlanConfig = OpenVlanConfigProtocol (Handle);

+  if (VlanConfig == NULL) {

+    goto Exit;

+  }

+

+  //

+  // Check VLAN ID.

+  //

+  if ((VlanIdStr == NULL) || (*VlanIdStr == 0)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle);

+    goto Exit;

+  }

+

+  VlanId = StrToVlanId (VlanIdStr);

+  if (VlanId > 4094) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr);

+    goto Exit;

+  }

+

+  //

+  // Check Priority.

+  //

+  if ((PriorityStr != NULL) && (*PriorityStr != 0)) {

+    Priority = StrDecimalToUintn (PriorityStr);

+    if (Priority > 7) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_PRIORITY), mHiiHandle, PriorityStr);

+      goto Exit;

+    }

+  }

+

+  //

+  // Set VLAN

+  //

+  Status = VlanConfig->Set (VlanConfig, (UINT16) VlanId, (UINT8) Priority);

+  if (EFI_ERROR (Status)) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_FAIL), mHiiHandle, Status);

+    goto Exit;

+  }

+

+  //

+  // Connect the VLAN device.

+  //

+  VlanHandle = NetLibGetVlanHandle (Handle, (UINT16) VlanId);

+  if (VlanHandle != NULL) {

+    gBS->ConnectController (VlanHandle, NULL, NULL, TRUE);

+  }

+

+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_SUCCESS), mHiiHandle);

+

+Exit:

+  if (VlanConfig != NULL) {

+    CloseVlanConfigProtocol (Handle);

+  }

+

+  FreePool (Name);

+}

+

+/**

+  Remove a VLAN device.

+

+  @param[in]  ParamStr       Parameter string from user input.

+

+**/

+VOID

+DeleteVlan (

+  CHAR16 *ParamStr

+  )

+{

+  CHAR16                    *Name;

+  CHAR16                    *VlanIdStr;

+  CHAR16                    *StrPtr;

+  UINTN                     VlanId;

+  EFI_HANDLE                Handle;

+  EFI_VLAN_CONFIG_PROTOCOL  *VlanConfig;

+  EFI_STATUS                Status;

+  UINT16                    NumberOfVlan;

+  EFI_VLAN_FIND_DATA        *VlanData;

+

+  VlanConfig = NULL;

+

+  if (ParamStr == NULL) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle);

+    return ;

+  }

+

+  StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr);

+  if (StrPtr == NULL) {

+    return ;

+  }

+

+  Name      = StrPtr;

+  VlanIdStr = NULL;

+  while (*StrPtr != 0) {

+    if (*StrPtr == L'.') {

+      *StrPtr   = 0;

+      VlanIdStr = StrPtr + 1;

+      break;

+    }

+

+    StrPtr++;

+  }

+

+  Handle = NicNameToHandle (Name);

+  if (Handle == NULL) {

+    goto Exit;

+  }

+

+  VlanConfig = OpenVlanConfigProtocol (Handle);

+  if (VlanConfig == NULL) {

+    goto Exit;

+  }

+

+  //

+  // Check VLAN ID

+  //

+  if (VlanIdStr == NULL || *VlanIdStr == 0) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle);

+    goto Exit;

+  }

+

+  VlanId = StrToVlanId (VlanIdStr);

+  if (VlanId > 4094) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr);

+    goto Exit;

+  }

+

+  //

+  // Delete VLAN.

+  //

+  Status = VlanConfig->Remove (VlanConfig, (UINT16) VlanId);

+  if (EFI_ERROR (Status)) {

+    if (Status == EFI_NOT_FOUND) {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NOT_FOUND), mHiiHandle);

+    } else {

+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_FAIL), mHiiHandle, Status);

+    }

+

+    goto Exit;

+  }

+

+  //

+  // Check whether this is the last VLAN to remove.

+  //

+  Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData);

+  if (EFI_ERROR (Status)) {

+    //

+    // This is the last VLAN to remove, try to connect the controller handle.

+    //

+    gBS->ConnectController (Handle, NULL, NULL, TRUE);

+  } else {

+    FreePool (VlanData);

+  }

+

+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_SUCCESS), mHiiHandle);

+

+Exit:

+  if (VlanConfig != NULL) {

+    CloseVlanConfigProtocol (Handle);

+  }

+

+  FreePool (Name);

+}

+

+/**

+  The actual entry point for the application.

+

+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.

+  @param[in] SystemTable    A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS       The entry point executed successfully.

+  @retval other             Some error occur when executing this entry point.

+

+**/

+EFI_STATUS

+EFIAPI

+VlanConfigMain (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  )

+{

+  LIST_ENTRY    *List;

+  CONST CHAR16  *Str;

+

+  mImageHandle = ImageHandle;

+

+  //

+  // Register our string package to HII database.

+  //

+  mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, VConfigStrings, NULL);

+  if (mHiiHandle == NULL) {

+    return EFI_SUCCESS;

+  }

+

+  List = NULL;

+  ShellCommandLineParseEx (mParamList, &List, NULL, FALSE, FALSE);

+  if (List == NULL) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle);

+    goto Exit;

+  }

+

+  if (ShellCommandLineGetFlag (List, L"-?")) {

+    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_HELP), mHiiHandle);

+    goto Exit;

+  }

+

+  if (ShellCommandLineGetFlag (List, L"-l")) {

+    Str = ShellCommandLineGetValue (List, L"-l");

+    DisplayVlan ((CHAR16 *) Str);

+    goto Exit;

+  }

+

+  if (ShellCommandLineGetFlag (List, L"-a")) {

+    Str = ShellCommandLineGetValue (List, L"-a");

+    AddVlan ((CHAR16 *) Str);

+    goto Exit;

+  }

+

+  if (ShellCommandLineGetFlag (List, L"-d")) {

+    Str = ShellCommandLineGetValue (List, L"-d");

+    DeleteVlan ((CHAR16 *) Str);

+    goto Exit;

+  }

+

+  //

+  // No valid argument till now.

+  //

+  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle);

+

+Exit:

+  if (List != NULL) {

+    ShellCommandLineFreeVarList (List);

+  }

+

+  //

+  // Remove our string package from HII database.

+  //

+  HiiRemovePackages (mHiiHandle);

+

+  return EFI_SUCCESS;

+}

diff --git a/NetworkPkg/Application/VConfig/VConfig.inf b/NetworkPkg/Application/VConfig/VConfig.inf
new file mode 100644
index 0000000..e69da02
--- /dev/null
+++ b/NetworkPkg/Application/VConfig/VConfig.inf
@@ -0,0 +1,47 @@
+## @file

+#  Component files for VLAN configuration shell application.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = VConfig

+  FILE_GUID                      = 87E36301-0406-44db-AAF3-9E0E591F3725

+  MODULE_TYPE                    = UEFI_APPLICATION

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = VlanConfigMain

+

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF

+#

+

+[Sources]

+  VConfigStrings.uni

+  VConfig.c

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+  ShellPkg/ShellPkg.dec

+

+[LibraryClasses]

+  UefiApplicationEntryPoint

+  UefiBootServicesTableLib

+  UefiLib

+  ShellLib

+  NetLib

+  MemoryAllocationLib

+  HiiLib

+

+[Protocols]

+  gEfiVlanConfigProtocolGuid

diff --git a/NetworkPkg/Application/VConfig/VConfigStrings.uni b/NetworkPkg/Application/VConfig/VConfigStrings.uni
new file mode 100644
index 0000000..e8bdcef
--- /dev/null
+++ b/NetworkPkg/Application/VConfig/VConfigStrings.uni
Binary files differ
diff --git a/NetworkPkg/Dhcp6Dxe/ComponentName.c b/NetworkPkg/Dhcp6Dxe/ComponentName.c
new file mode 100644
index 0000000..314ca37
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/ComponentName.c
@@ -0,0 +1,312 @@
+/** @file

+  UEFI Component Name(2) protocol implementation for Dhcp6 driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Dhcp6Impl.h"

+

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6ComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that attempt to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that attempts to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language, from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6ComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,

+  IN  EFI_HANDLE                                      ControllerHandle,

+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,

+  IN  CHAR8                                           *Language,

+  OUT CHAR16                                          **ControllerName

+  );

+

+

+//

+// EFI Component Name Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL    gDhcp6ComponentName = {

+  Dhcp6ComponentNameGetDriverName,

+  Dhcp6ComponentNameGetControllerName,

+  "eng"

+};

+

+//

+// EFI Component Name 2 Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL   gDhcp6ComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Dhcp6ComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Dhcp6ComponentNameGetControllerName,

+  "en"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE       mDhcp6DriverNameTable[] = {

+  {

+    "eng;en",

+    L"DHCP6 Protocol Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6ComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+           Language,

+           This->SupportedLanguages,

+           mDhcp6DriverNameTable,

+           DriverName,

+           (BOOLEAN)(This == &gDhcp6ComponentName)

+           );

+}

+

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in the

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language, from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6ComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,

+  IN  EFI_HANDLE                                      ControllerHandle,

+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,

+  IN  CHAR8                                           *Language,

+  OUT CHAR16                                          **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c
new file mode 100644
index 0000000..d14f169
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c
@@ -0,0 +1,782 @@
+/** @file

+  Driver Binding functions and Service Binding functions

+  implementationfor for Dhcp6 Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Dhcp6Impl.h"

+

+

+EFI_DRIVER_BINDING_PROTOCOL gDhcp6DriverBinding = {

+  Dhcp6DriverBindingSupported,

+  Dhcp6DriverBindingStart,

+  Dhcp6DriverBindingStop,

+  0xa,

+  NULL,

+  NULL

+};

+

+EFI_SERVICE_BINDING_PROTOCOL gDhcp6ServiceBindingTemplate = {

+  Dhcp6ServiceBindingCreateChild,

+  Dhcp6ServiceBindingDestroyChild

+};

+

+

+/**

+  Configure the default Udp6Io to receive all the DHCP6 traffic

+  on this network interface.

+

+  @param[in]  UdpIo                  The pointer to Udp6Io to be configured.

+  @param[in]  Context                The pointer to the context.

+

+  @retval EFI_SUCCESS            The Udp6Io is successfully configured.

+  @retval Others                 Failed to configure the Udp6Io.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6ConfigureUdpIo (

+  IN UDP_IO                 *UdpIo,

+  IN VOID                   *Context

+  )

+{

+  EFI_UDP6_PROTOCOL         *Udp6;

+  EFI_UDP6_CONFIG_DATA      *Config;

+

+  Udp6   = UdpIo->Protocol.Udp6;

+  Config = &(UdpIo->Config.Udp6);

+

+  ZeroMem (Config, sizeof (EFI_UDP6_CONFIG_DATA));

+

+  //

+  // Set Udp6 configure data for the Dhcp6 instance.

+  //

+  Config->AcceptPromiscuous  = FALSE;

+  Config->AcceptAnyPort      = FALSE;

+  Config->AllowDuplicatePort = FALSE;

+  Config->TrafficClass       = 0;

+  Config->HopLimit           = 128;

+  Config->ReceiveTimeout     = 0;

+  Config->TransmitTimeout    = 0;

+

+  //

+  // Configure an endpoint of client(0, 546), server(0, 0), the addresses

+  // will be overridden later. Note that we MUST not limit RemotePort.

+  // More details, refer to RFC 3315 section 5.2.

+  //

+  Config->StationPort        = DHCP6_PORT_CLIENT;

+  Config->RemotePort         = 0;

+

+  return Udp6->Configure (Udp6, Config);;

+}

+

+

+/**

+  Destory the Dhcp6 service. The Dhcp6 service may be partly initialized,

+  or partly destroyed. If a resource is destroyed, it is marked as such in

+  case the destroy failed and being called again later.

+

+  @param[in, out]  Service       The pointer to Dhcp6 service to be destroyed.

+

+**/

+VOID

+Dhcp6DestroyService (

+  IN OUT DHCP6_SERVICE          *Service

+  )

+{

+  //

+  // All children instances should have been already destoryed here.

+  //

+  ASSERT (Service->NumOfChild == 0);

+

+  if (Service->ClientId != NULL) {

+    FreePool (Service->ClientId);

+  }

+

+  if (Service->UdpIo != NULL) {

+    UdpIoFreeIo (Service->UdpIo);

+  }

+

+  FreePool (Service);

+}

+

+

+/**

+  Create a new Dhcp6 service for the Nic controller.

+

+  @param[in]  Controller             The controller to be installed DHCP6 service

+                                     binding protocol.

+  @param[in]  ImageHandle            The image handle of the Dhcp6 driver.

+  @param[out] Service                The return pointer of the new Dhcp6 service.

+

+  @retval EFI_SUCCESS            The Dhcp6 service is created successfully.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource.

+

+**/

+EFI_STATUS

+Dhcp6CreateService (

+  IN  EFI_HANDLE            Controller,

+  IN  EFI_HANDLE            ImageHandle,

+  OUT DHCP6_SERVICE         **Service

+  )

+{

+  DHCP6_SERVICE             *Dhcp6Srv;

+

+  *Service = NULL;

+  Dhcp6Srv = AllocateZeroPool (sizeof (DHCP6_SERVICE));

+

+  if (Dhcp6Srv == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Open the SNP protocol to get mode data later.

+  //

+  Dhcp6Srv->Snp = NULL;

+  NetLibGetSnpHandle (Controller, &Dhcp6Srv->Snp);

+  if (Dhcp6Srv->Snp == NULL) {

+    FreePool (Dhcp6Srv);

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // Initialize the fields of the new Dhcp6 service.

+  //

+  Dhcp6Srv->Signature       = DHCP6_SERVICE_SIGNATURE;

+  Dhcp6Srv->InDestory       = FALSE;

+  Dhcp6Srv->Controller      = Controller;

+  Dhcp6Srv->Image           = ImageHandle;

+  Dhcp6Srv->Xid             = (0xffffff & NET_RANDOM (NetRandomInitSeed ()));

+

+  CopyMem (

+    &Dhcp6Srv->ServiceBinding,

+    &gDhcp6ServiceBindingTemplate,

+    sizeof (EFI_SERVICE_BINDING_PROTOCOL)

+    );

+

+  //

+  // Generate client Duid in the format of Duid-llt.

+  //

+  Dhcp6Srv->ClientId        = Dhcp6GenerateClientId (Dhcp6Srv->Snp->Mode);

+

+  if (Dhcp6Srv->ClientId == NULL) {

+    FreePool (Dhcp6Srv);

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // Create an Udp6Io for stateful transmit/receive of each Dhcp6 instance.

+  //

+  Dhcp6Srv->UdpIo = UdpIoCreateIo (

+                      Controller,

+                      ImageHandle,

+                      Dhcp6ConfigureUdpIo,

+                      UDP_IO_UDP6_VERSION,

+                      NULL

+                      );

+

+  if (Dhcp6Srv->UdpIo == NULL) {

+    FreePool (Dhcp6Srv->ClientId);

+    FreePool (Dhcp6Srv);

+    return EFI_DEVICE_ERROR;

+  }

+

+  InitializeListHead (&Dhcp6Srv->Child);

+

+  *Service = Dhcp6Srv;

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Destroy the Dhcp6 instance and recycle the resources.

+

+  @param[in, out]  Instance        The pointer to the Dhcp6 instance.

+

+**/

+VOID

+Dhcp6DestroyInstance (

+  IN OUT DHCP6_INSTANCE         *Instance

+  )

+{

+  //

+  // Clean up the retry list first.

+  //

+  Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL);

+  gBS->CloseEvent (Instance->Timer);

+

+  //

+  // Clean up the current configure data.

+  //

+  if (Instance->Config != NULL) {

+    Dhcp6CleanupConfigData (Instance->Config);

+    FreePool (Instance->Config);

+  }

+

+  //

+  // Clean up the current Ia.

+  //

+  if (Instance->IaCb.Ia != NULL) {

+    if (Instance->IaCb.Ia->ReplyPacket != NULL) {

+      FreePool (Instance->IaCb.Ia->ReplyPacket);

+    }

+    FreePool (Instance->IaCb.Ia);

+  }

+

+  if (Instance->Unicast != NULL) {

+    FreePool (Instance->Unicast);

+  }

+

+  if (Instance->AdSelect != NULL) {

+    FreePool (Instance->AdSelect);

+  }

+

+  FreePool (Instance);

+}

+

+

+/**

+  Create the Dhcp6 instance and initialize it.

+

+  @param[in]  Service              The pointer to the Dhcp6 service.

+  @param[out] Instance             The pointer to the Dhcp6 instance.

+

+  @retval EFI_SUCCESS            The Dhcp6 instance is created.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.

+

+**/

+EFI_STATUS

+Dhcp6CreateInstance (

+  IN  DHCP6_SERVICE         *Service,

+  OUT DHCP6_INSTANCE        **Instance

+  )

+{

+  EFI_STATUS                Status;

+  DHCP6_INSTANCE            *Dhcp6Ins;

+

+  *Instance = NULL;

+  Dhcp6Ins  = AllocateZeroPool (sizeof (DHCP6_INSTANCE));

+

+  if (Dhcp6Ins == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Initialize the fields of the new Dhcp6 instance.

+  //

+  Dhcp6Ins->Signature       = DHCP6_INSTANCE_SIGNATURE;

+  Dhcp6Ins->UdpSts          = EFI_ALREADY_STARTED;

+  Dhcp6Ins->Service         = Service;

+  Dhcp6Ins->InDestory       = FALSE;

+  Dhcp6Ins->MediaPresent    = TRUE;

+

+  CopyMem (

+    &Dhcp6Ins->Dhcp6,

+    &gDhcp6ProtocolTemplate,

+    sizeof (EFI_DHCP6_PROTOCOL)

+    );

+

+  InitializeListHead (&Dhcp6Ins->TxList);

+  InitializeListHead (&Dhcp6Ins->InfList);

+

+  //

+  // There is a timer for each Dhcp6 instance, which is used to track the

+  // lease time of Ia and the retransmisson time of all sent packets.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,

+                  TPL_CALLBACK,

+                  Dhcp6OnTimerTick,

+                  Dhcp6Ins,

+                  &Dhcp6Ins->Timer

+                  );

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Dhcp6Ins);

+    return Status;

+  }

+

+  *Instance = Dhcp6Ins;

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Entry point of the DHCP6 driver to install various protocols.

+

+  @param[in]  ImageHandle           The handle of the UEFI image file.

+  @param[in]  SystemTable           The pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The operation completed successfully.

+  @retval Others                Unexpected error occurs.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6DriverEntryPoint (

+  IN EFI_HANDLE                   ImageHandle,

+  IN EFI_SYSTEM_TABLE             *SystemTable

+  )

+{

+  return EfiLibInstallDriverBindingComponentName2 (

+           ImageHandle,

+           SystemTable,

+           &gDhcp6DriverBinding,

+           ImageHandle,

+           &gDhcp6ComponentName,

+           &gDhcp6ComponentName2

+           );

+}

+

+

+/**

+  Test to see if this driver supports ControllerHandle. This service

+  is called by the EFI boot service ConnectController(). In

+  order to make drivers as small as possible, there are a few calling

+  restrictions for this service. ConnectController() must

+  follow these calling restrictions. If any other agent wishes to call

+  Supported() it must also follow these calling restrictions.

+

+  @param[in]  This                The pointer to the driver binding protocol.

+  @param[in]  ControllerHandle    The handle of device to be tested.

+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child

+                                  device to be started.

+

+  @retval EFI_SUCCESS         This driver supports this device.

+  @retval Others              This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6DriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  )

+{

+  return gBS->OpenProtocol (

+                ControllerHandle,

+                &gEfiUdp6ServiceBindingProtocolGuid,

+                NULL,

+                This->DriverBindingHandle,

+                ControllerHandle,

+                EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                );

+}

+

+

+/**

+  Start this driver on ControllerHandle. This service is called by the

+  EFI boot service ConnectController(). In order to make

+  drivers as small as possible, there are a few calling restrictions for

+  this service. ConnectController() must follow these

+  calling restrictions. If any other agent wishes to call Start() it

+  must also follow these calling restrictions.

+

+  @param[in]  This                 The pointer to the driver binding protocol.

+  @param[in]  ControllerHandle     The handle of device to be started.

+  @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child

+                                   device to be started.

+

+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.

+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.

+  @retval other                This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6DriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  )

+{

+  EFI_STATUS                      Status;

+  DHCP6_SERVICE                   *Service;

+

+  //

+  // Check the Dhcp6 serivce whether already started.

+  //

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiDhcp6ServiceBindingProtocolGuid,

+                  NULL,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+

+  if (!EFI_ERROR (Status)) {

+    return EFI_ALREADY_STARTED;

+  }

+

+  //

+  // Create and initialize the Dhcp6 service.

+  //

+  Status = Dhcp6CreateService (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &Service

+             );

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  ASSERT (Service != NULL);

+

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &ControllerHandle,

+                  &gEfiDhcp6ServiceBindingProtocolGuid,

+                  &Service->ServiceBinding,

+                  NULL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    Dhcp6DestroyService (Service);

+    return Status;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Stop this driver on ControllerHandle. This service is called by the

+  EFI boot service DisconnectController(). In order to

+  make drivers as small as possible, there are a few calling

+  restrictions for this service. DisconnectController()

+  must follow these calling restrictions. If any other agent wishes

+  to call Stop() it must also follow these calling restrictions.

+

+  @param[in]  This              Protocol instance pointer.

+  @param[in]  ControllerHandle  Handle of device to stop driver on

+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of

+                                children is zero stop the entire bus driver.

+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS           This driver is removed ControllerHandle

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+  @retval other                 This driver was not removed from this device

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6DriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer   OPTIONAL

+  )

+{

+  EFI_STATUS                       Status;

+  EFI_TPL                          OldTpl;

+  EFI_HANDLE                       NicHandle;

+  EFI_SERVICE_BINDING_PROTOCOL     *ServiceBinding;

+  DHCP6_SERVICE                    *Service;

+  DHCP6_INSTANCE                   *Instance;

+

+  //

+  // Find and check the Nic handle by the controller handle.

+  //

+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);

+

+  if (NicHandle == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  NicHandle,

+                  &gEfiDhcp6ServiceBindingProtocolGuid,

+                  (VOID **) &ServiceBinding,

+                  This->DriverBindingHandle,

+                  NicHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Service = DHCP6_SERVICE_FROM_THIS (ServiceBinding);

+

+  if (Service->InDestory) {

+    return EFI_SUCCESS;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (NumberOfChildren == 0) {

+    //

+    // Destory the service itself if no child instance left.

+    //

+    Service->InDestory = TRUE;

+

+    Status = gBS->UninstallProtocolInterface (

+                    NicHandle,

+                    &gEfiDhcp6ServiceBindingProtocolGuid,

+                    ServiceBinding

+                    );

+

+    if (EFI_ERROR (Status)) {

+      Service->InDestory = FALSE;

+      goto ON_EXIT;

+    }

+

+    Dhcp6DestroyService (Service);

+

+  } else {

+    //

+    // Destory all the children instances before destory the service.

+    //

+    while (!IsListEmpty (&Service->Child)) {

+      Instance = NET_LIST_HEAD (&Service->Child, DHCP6_INSTANCE, Link);

+      ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);

+    }

+    //

+    // Any of child failed to be destroyed.

+    //

+    if (Service->NumOfChild != 0) {

+      Status = EFI_DEVICE_ERROR;

+    }

+  }

+

+ON_EXIT:

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+

+/**

+  Creates a child handle and installs a protocol.

+

+  The CreateChild() function installs a protocol on ChildHandle.

+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.

+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.

+

+  @param[in]      This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,

+                              then a new handle is created. If it is a pointer to an existing

+                              UEFI handle, then the protocol is added to the existing UEFI handle.

+

+  @retval EFI_SUCCES            The protocol was added to ChildHandle.

+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.

+  @retval other                 The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6ServiceBindingCreateChild (

+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN OUT EFI_HANDLE                    *ChildHandle

+  )

+{

+  EFI_STATUS                       Status;

+  EFI_TPL                          OldTpl;

+  DHCP6_SERVICE                    *Service;

+  DHCP6_INSTANCE                   *Instance;

+  VOID                             *Udp6;

+

+  if (This == NULL || ChildHandle == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Service = DHCP6_SERVICE_FROM_THIS (This);

+

+  Status  = Dhcp6CreateInstance (Service, &Instance);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  ASSERT (Instance != NULL);

+

+  //

+  // Start the timer when the instance is ready to use.

+  //

+  Status = gBS->SetTimer (

+                  Instance->Timer,

+                  TimerPeriodic,

+                  TICKS_PER_SECOND

+                  );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Install the DHCP6 protocol onto ChildHandle.

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  ChildHandle,

+                  &gEfiDhcp6ProtocolGuid,

+                  &Instance->Dhcp6,

+                  NULL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Instance->Handle = *ChildHandle;

+

+  //

+  // Open the UDP6 protocol BY_CHILD.

+  //

+  Status = gBS->OpenProtocol (

+                  Service->UdpIo->UdpHandle,

+                  &gEfiUdp6ProtocolGuid,

+                  (VOID **) &Udp6,

+                  gDhcp6DriverBinding.DriverBindingHandle,

+                  Instance->Handle,

+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                  );

+

+  if (EFI_ERROR (Status)) {

+

+    gBS->UninstallMultipleProtocolInterfaces (

+           Instance->Handle,

+           &gEfiDhcp6ProtocolGuid,

+           &Instance->Dhcp6,

+           NULL

+           );

+    goto ON_ERROR;

+  }

+

+  //

+  // Add into the children list of its parent service.

+  //

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  InsertTailList (&Service->Child, &Instance->Link);

+  Service->NumOfChild++;

+

+  gBS->RestoreTPL (OldTpl);

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  Dhcp6DestroyInstance (Instance);

+  return Status;

+}

+

+

+/**

+  Destroys a child handle with a protocol installed on it.

+

+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol

+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the

+  last protocol on ChildHandle, then ChildHandle is destroyed.

+

+  @param[in]  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in]  ChildHandle Handle of the child to destroy

+

+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.

+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.

+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.

+  @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle

+                                because its services are being used.

+  @retval other                 The child handle was not destroyed

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6ServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    ChildHandle

+  )

+{

+  EFI_STATUS                       Status;

+  EFI_TPL                          OldTpl;

+  EFI_DHCP6_PROTOCOL               *Dhcp6;

+  DHCP6_SERVICE                    *Service;

+  DHCP6_INSTANCE                   *Instance;

+

+  if (This == NULL || ChildHandle == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Retrieve the private context data structures

+  //

+  Status = gBS->OpenProtocol (

+                  ChildHandle,

+                  &gEfiDhcp6ProtocolGuid,

+                  (VOID **) &Dhcp6,

+                  gDhcp6DriverBinding.DriverBindingHandle,

+                  ChildHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Instance = DHCP6_INSTANCE_FROM_THIS (Dhcp6);

+  Service  = DHCP6_SERVICE_FROM_THIS (This);

+

+  if (Instance->Service != Service) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Instance->InDestory) {

+    return EFI_SUCCESS;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  Instance->InDestory = TRUE;

+

+  Status = gBS->CloseProtocol (

+                  Service->UdpIo->UdpHandle,

+                  &gEfiUdp6ProtocolGuid,

+                  gDhcp6DriverBinding.DriverBindingHandle,

+                  ChildHandle

+                  );

+

+  if (EFI_ERROR (Status)) {

+    Instance->InDestory = FALSE;

+    gBS->RestoreTPL (OldTpl);

+    return Status;

+  }

+

+  //

+  // Uninstall the MTFTP6 protocol first to enable a top down destruction.

+  //

+  Status = gBS->UninstallProtocolInterface (

+                  ChildHandle,

+                  &gEfiDhcp6ProtocolGuid,

+                  Dhcp6

+                  );

+

+  if (EFI_ERROR (Status)) {

+    Instance->InDestory = FALSE;

+    gBS->RestoreTPL (OldTpl);

+    return Status;

+  }

+

+  //

+  // Remove it from the children list of its parent service.

+  //

+  RemoveEntryList (&Instance->Link);

+  Service->NumOfChild--;

+

+  Dhcp6DestroyInstance (Instance);

+

+  gBS->RestoreTPL (OldTpl);

+

+  return EFI_SUCCESS;

+}

diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h
new file mode 100644
index 0000000..5a88be4
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h
@@ -0,0 +1,155 @@
+/** @file

+  Driver Binding functions and Service Binding functions

+  declaration for Dhcp6 Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_DHCP6_DRIVER_H__

+#define __EFI_DHCP6_DRIVER_H__

+

+#include <Protocol/ServiceBinding.h>

+

+extern EFI_COMPONENT_NAME_PROTOCOL  gDhcp6ComponentName;

+extern EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2;

+

+/**

+  Test to see if this driver supports ControllerHandle. This service

+  is called by the EFI boot service ConnectController(). In

+  order to make drivers as small as possible, there are a few calling

+  restrictions for this service. ConnectController() must

+  follow these calling restrictions. If any other agent wishes to call

+  Supported(), it must also follow these calling restrictions.

+

+  @param[in]  This                Protocol instance pointer.

+  @param[in]  ControllerHandle    Handle of device to test.

+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child

+                                  device to start.

+

+  @retval EFI_SUCCESS         This driver supports this device.

+  @retval EFI_ALREADY_STARTED This driver is already running on this device.

+  @retval other               This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6DriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+/**

+  Start this driver on ControllerHandle. This service is called by the

+  EFI boot service ConnectController(). In order to make

+  drivers as small as possible, there are a few calling restrictions for

+  this service. ConnectController() must follow these

+  calling restrictions. If any other agent wishes to call Start(), it

+  must also follow these calling restrictions.

+

+  @param[in]  This                 Protocol instance pointer.

+  @param[in]  ControllerHandle     Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child

+                                   device to start.

+

+  @retval EFI_SUCCESS          This driver is added to ControllerHandle.

+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.

+  @retval other                This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6DriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+/**

+  Stop this driver on ControllerHandle. This service is called by the

+  EFI boot service DisconnectController(). In order to

+  make drivers as small as possible, there are a few calling

+  restrictions for this service. DisconnectController()

+  must follow these calling restrictions. If any other agent wishes

+  to call Stop() it must also follow these calling restrictions.

+

+  @param[in]  This              Protocol instance pointer.

+  @param[in]  ControllerHandle  Handle of device to stop driver on.

+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If the number of

+                                children is zero, stop the entire bus driver.

+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS       This driver is removed ControllerHandle.

+  @retval other             This driver was not removed from this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6DriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer      OPTIONAL

+  );

+

+/**

+  Creates a child handle and installs a protocol.

+

+  The CreateChild() function installs a protocol on ChildHandle.

+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.

+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.

+

+  @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param  ChildHandle Pointer to the handle of the child to create. If it is NULL,

+                      then a new handle is created. If it is a pointer to an existing UEFI handle,

+                      then the protocol is added to the existing UEFI handle.

+

+  @retval EFI_SUCCES            The protocol was added to ChildHandle.

+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.

+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create

+                                the child.

+  @retval other                 The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6ServiceBindingCreateChild (

+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN OUT EFI_HANDLE                    *ChildHandle

+  );

+

+/**

+  Destroys a child handle with a protocol installed on it.

+

+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol

+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the

+  last protocol on ChildHandle, then ChildHandle is destroyed.

+

+  @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param  ChildHandle Handle of the child to destroy.

+

+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.

+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.

+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.

+  @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle

+                                because its services are being used.

+  @retval other                 The child handle was not destroyed

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6ServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    ChildHandle

+  );

+

+#endif

diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf b/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf
new file mode 100644
index 0000000..f10b07a
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf
@@ -0,0 +1,69 @@
+## @file

+#  Component description file for Dhcp6 module.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = Dhcp6Dxe

+  FILE_GUID                      = 95E3669D-34BE-4775-A651-7EA41B69D89E

+  MODULE_TYPE                    = UEFI_DRIVER

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = Dhcp6DriverEntryPoint

+  UNLOAD_IMAGE                   = NetLibDefaultUnload

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC

+#

+#  DRIVER_BINDING                =  gDhcp6DriverBinding

+#  COMPONENT_NAME                =  gDhcp6ComponentName

+#  COMPONENT_NAME2               =  gDhcp6ComponentName2

+#

+

+[Sources]

+  Dhcp6Driver.c

+  Dhcp6Driver.h

+  Dhcp6Impl.c

+  Dhcp6Impl.h

+  Dhcp6Io.c

+  Dhcp6Io.h

+  Dhcp6Utility.c

+  Dhcp6Utility.h

+  ComponentName.c

+

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+

+

+[LibraryClasses]

+  UefiLib

+  BaseLib

+  BaseMemoryLib

+  MemoryAllocationLib

+  UefiDriverEntryPoint

+  UefiBootServicesTableLib

+  UefiRuntimeServicesTableLib

+  DebugLib

+  NetLib

+  UdpIoLib

+

+

+[Protocols]

+  gEfiUdp6ServiceBindingProtocolGuid

+  gEfiUdp6ProtocolGuid

+  gEfiDhcp6ServiceBindingProtocolGuid

+  gEfiDhcp6ProtocolGuid

+

diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c
new file mode 100644
index 0000000..3e73976
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c
@@ -0,0 +1,1220 @@
+/** @file

+  This EFI_DHCP6_PROTOCOL interface implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Dhcp6Impl.h"

+

+//

+// Well-known multi-cast address defined in section-24.1 of rfc-3315

+//

+//   ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2

+//   ALL_DHCP_Servers address:                  FF05::1:3

+//

+EFI_IPv6_ADDRESS   mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};

+EFI_IPv6_ADDRESS   mAllDhcpServersAddress         = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}};

+

+EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = {

+  EfiDhcp6GetModeData,

+  EfiDhcp6Configure,

+  EfiDhcp6Start,

+  EfiDhcp6InfoRequest,

+  EfiDhcp6RenewRebind,

+  EfiDhcp6Decline,

+  EfiDhcp6Release,

+  EfiDhcp6Stop,

+  EfiDhcp6Parse

+};

+

+/**

+  Starts the DHCPv6 standard S.A.R.R. process.

+

+  The Start() function starts the DHCPv6 standard process. This function can

+  be called only when the state of Dhcp6 instance is in the Dhcp6Init state.

+  If the DHCP process completes successfully, the state of the Dhcp6 instance

+  will be transferred through Dhcp6Selecting and Dhcp6Requesting to the

+  Dhcp6Bound state.

+  Refer to rfc-3315 for precise state transitions during this process. At the

+  time when each event occurs in this process, the callback function that was set

+  by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this

+  opportunity to control the process.

+

+  @param[in]  This              The pointer to Dhcp6 protocol.

+

+  @retval EFI_SUCCESS           The DHCPv6 standard process has started, or it has

+                                completed when CompletionEvent is NULL.

+  @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Child instance hasn't been configured.

+  @retval EFI_INVALID_PARAMETER This is NULL.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_TIMEOUT           The DHCPv6 configuration process failed because no

+                                response was received from the server within the

+                                specified timeout value.

+  @retval EFI_ABORTED           The user aborted the DHCPv6 process.

+  @retval EFI_ALREADY_STARTED   Some other Dhcp6 instance already started the DHCPv6

+                                standard process.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+  @retval EFI_NO_MEDIA          There was a media error.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Start (

+  IN EFI_DHCP6_PROTOCOL        *This

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_TPL                      OldTpl;

+  DHCP6_INSTANCE               *Instance;

+  DHCP6_SERVICE                *Service;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = DHCP6_INSTANCE_FROM_THIS (This);

+  Service  = Instance->Service;

+

+  //

+  // The instance hasn't been configured.

+  //

+  if (Instance->Config == NULL) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  ASSERT (Instance->IaCb.Ia != NULL);

+

+  //

+  // The instance has already been started.

+  //

+  if (Instance->IaCb.Ia->State != Dhcp6Init) {

+    return EFI_ALREADY_STARTED;

+  }

+

+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);

+  Instance->UdpSts = EFI_ALREADY_STARTED;

+

+  //

+  // Need to clear initial time to make sure that elapsed-time

+  // is set to 0 for first Solicit.

+  //

+  Instance->StartTime = 0;

+

+  //

+  // Send the solicit message to start S.A.R.R process.

+  //

+  Status = Dhcp6SendSolicitMsg (Instance);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Register receive callback for the stateful exchange process.

+  //

+  Status = UdpIoRecvDatagram(

+             Service->UdpIo,

+             Dhcp6ReceivePacket,

+             Service,

+             0

+             );

+

+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {

+    goto ON_ERROR;

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  //

+  // Poll udp out of the net tpl if synchronous call.

+  //

+  if (Instance->Config->IaInfoEvent == NULL) {

+

+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {

+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);

+    }

+    return Instance->UdpSts;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+

+/**

+  Stops the DHCPv6 standard S.A.R.R. process.

+

+  The Stop() function is used to stop the DHCPv6 standard process. After this

+  function is called successfully, the state of Dhcp6 instance is transferred

+  into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called

+  before DHCPv6 standard process can be started again. This function can be

+  called when the Dhcp6 instance is in any state.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+

+  @retval EFI_SUCCESS           The Dhcp6 instance is now in the Dhcp6Init state.

+  @retval EFI_INVALID_PARAMETER This is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Stop (

+  IN EFI_DHCP6_PROTOCOL        *This

+  )

+{

+  EFI_TPL                      OldTpl;

+  EFI_STATUS                   Status;

+  EFI_UDP6_PROTOCOL            *Udp6;

+  DHCP6_INSTANCE               *Instance;

+  DHCP6_SERVICE                *Service;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = DHCP6_INSTANCE_FROM_THIS (This);

+  Service  = Instance->Service;

+  Udp6     = Service->UdpIo->Protocol.Udp6;

+  Status   = EFI_SUCCESS;

+

+  //

+  // The instance hasn't been configured.

+  //

+  if (Instance->Config == NULL) {

+    return Status;

+  }

+

+  ASSERT (Instance->IaCb.Ia != NULL);

+

+  //

+  // The instance has already been stopped.

+  //

+  if (Instance->IaCb.Ia->State == Dhcp6Init ||

+      Instance->IaCb.Ia->State == Dhcp6Selecting ||

+      Instance->IaCb.Ia->State == Dhcp6Requesting

+      ) {

+    return Status;

+  }

+

+  //

+  // Release the current ready Ia.

+  //

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  Instance->UdpSts = EFI_ALREADY_STARTED;

+  Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia);

+

+  gBS->RestoreTPL (OldTpl);

+

+  //

+  // Poll udp out of the net tpl if synchoronus call.

+  //

+  if (Instance->Config->IaInfoEvent == NULL) {

+    ASSERT (Udp6 != NULL);

+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {

+      Udp6->Poll (Udp6);

+    }

+    Status = Instance->UdpSts;

+  }

+

+  //

+  // Clean up the session data for the released Ia.

+  //

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+  Dhcp6CleanupSession (Instance, EFI_SUCCESS);

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  Returns the current operating mode data for the Dhcp6 instance.

+

+  The GetModeData() function returns the current operating mode and

+  cached data packet for the Dhcp6 instance.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+  @param[out] Dhcp6ModeData     The pointer to the Dhcp6 mode data.

+  @param[out] Dhcp6ConfigData   The pointer to the Dhcp6 configure data.

+

+  @retval EFI_SUCCESS           The mode data was returned.

+  @retval EFI_INVALID_PARAMETER This is NULL.

+  @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Protocol instance was not

+                                configured.

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6GetModeData (

+  IN  EFI_DHCP6_PROTOCOL       *This,

+  OUT EFI_DHCP6_MODE_DATA      *Dhcp6ModeData      OPTIONAL,

+  OUT EFI_DHCP6_CONFIG_DATA    *Dhcp6ConfigData    OPTIONAL

+  )

+{

+  EFI_TPL                      OldTpl;

+  EFI_DHCP6_IA                 *Ia;

+  DHCP6_INSTANCE               *Instance;

+  DHCP6_SERVICE                *Service;

+  UINT32                       IaSize;

+  UINT32                       IdSize;

+

+  if (This == NULL || (Dhcp6ModeData == NULL && Dhcp6ConfigData == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = DHCP6_INSTANCE_FROM_THIS (This);

+  Service  = Instance->Service;

+

+  if (Instance->Config == NULL && Dhcp6ConfigData != NULL) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  ASSERT (Service->ClientId != NULL);

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // User needs a copy of instance config data.

+  //

+  if (Dhcp6ConfigData != NULL) {

+    ZeroMem (Dhcp6ConfigData, sizeof(EFI_DHCP6_CONFIG_DATA));

+    //

+    // Duplicate config data, including all reference buffers.

+    //

+    if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) {

+      goto ON_ERROR;

+    }

+  }

+

+  //

+  // User need a copy of instance mode data.

+  //

+  if (Dhcp6ModeData != NULL) {

+    ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA));

+    //

+    // Duplicate a copy of EFI_DHCP6_DUID for client Id.

+    //

+    IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length);

+

+    Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize);

+    if (Dhcp6ModeData->ClientId == NULL) {

+      goto ON_ERROR;

+    }

+

+    CopyMem (

+      Dhcp6ModeData->ClientId,

+      Service->ClientId,

+      IdSize

+      );

+

+    Ia = Instance->IaCb.Ia;

+    if (Ia != NULL) {

+      //

+      // Duplicate a copy of EFI_DHCP6_IA for configured Ia.

+      //

+      IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS);

+

+      Dhcp6ModeData->Ia = AllocateZeroPool (IaSize);

+      if (Dhcp6ModeData->Ia == NULL) {

+        goto ON_ERROR;

+      }

+

+      CopyMem (

+        Dhcp6ModeData->Ia,

+        Ia,

+        IaSize

+        );

+

+      //

+      // Duplicate a copy of reply packet if has.

+      //

+      if (Ia->ReplyPacket != NULL) {

+        Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size);

+        if (Dhcp6ModeData->Ia->ReplyPacket == NULL) {

+          goto ON_ERROR;

+        }

+        CopyMem (

+          Dhcp6ModeData->Ia->ReplyPacket,

+          Ia->ReplyPacket,

+          Ia->ReplyPacket->Size

+          );

+      }

+    }

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  if (Dhcp6ConfigData != NULL) {

+    Dhcp6CleanupConfigData (Dhcp6ConfigData);

+  }

+  if (Dhcp6ModeData != NULL) {

+    Dhcp6CleanupModeData (Dhcp6ModeData);

+  }

+  gBS->RestoreTPL (OldTpl);

+

+  return EFI_OUT_OF_RESOURCES;

+}

+

+

+/**

+  Initializes, changes, or resets the operational settings for the Dhcp6 instance.

+

+  The Configure() function is used to initialize or clean up the configuration

+  data of the Dhcp6 instance:

+  - When Dhcp6CfgData is not NULL and Configure() is called successfully, the

+    configuration data will be initialized in the Dhcp6 instance, and the state

+    of the configured IA will be transferred into Dhcp6Init.

+  - When Dhcp6CfgData is NULL and Configure() is called successfully, the

+    configuration data will be cleaned up and no IA will be associated with

+    the Dhcp6 instance.

+  To update the configuration data for an Dhcp6 instance, the original data

+  must be cleaned up before setting the new configuration data.

+

+  @param[in]  This                   The pointer to the Dhcp6 protocol

+  @param[in]  Dhcp6CfgData           The pointer to the EFI_DHCP6_CONFIG_DATA.

+

+  @retval EFI_SUCCESS           The Dhcp6 is configured successfully with the

+                                Dhcp6Init state, or cleaned up the original

+                                configuration setting.

+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance was already configured.

+                                The Dhcp6 instance has already started the

+                                DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.

+  @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Configure (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN EFI_DHCP6_CONFIG_DATA     *Dhcp6CfgData    OPTIONAL

+  )

+{

+  EFI_TPL                      OldTpl;

+  EFI_STATUS                   Status;

+  LIST_ENTRY                   *Entry;

+  DHCP6_INSTANCE               *Other;

+  DHCP6_INSTANCE               *Instance;

+  DHCP6_SERVICE                *Service;

+  UINTN                        Index;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = DHCP6_INSTANCE_FROM_THIS (This);

+  Service  = Instance->Service;

+

+  //

+  // Check the parameter of configure data.

+  //

+  if (Dhcp6CfgData != NULL) {

+    if (Dhcp6CfgData->OptionCount > 0 && Dhcp6CfgData->OptionList == NULL) {

+      return EFI_INVALID_PARAMETER;

+    }

+    if (Dhcp6CfgData->OptionList != NULL) {

+      for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) {

+        if (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId ||

+            Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit ||

+            Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept ||

+            Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana ||

+            Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata

+            ) {

+          return EFI_INVALID_PARAMETER;

+        }

+      }

+    }

+

+    if (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA &&

+        Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA

+        ) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (Dhcp6CfgData->IaInfoEvent == NULL && Dhcp6CfgData->SolicitRetransmission == NULL) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (Dhcp6CfgData->SolicitRetransmission != NULL &&

+        Dhcp6CfgData->SolicitRetransmission->Mrc == 0 &&

+        Dhcp6CfgData->SolicitRetransmission->Mrd == 0

+        ) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    //

+    // Make sure the (IaId, IaType) is unique over all the instances.

+    //

+    NET_LIST_FOR_EACH (Entry, &Service->Child) {

+      Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link);

+      if (Other->IaCb.Ia != NULL &&

+          Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type &&

+          Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId

+          ) {

+        return EFI_INVALID_PARAMETER;

+      }

+    }

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (Dhcp6CfgData != NULL) {

+    //

+    // It's not allowed to configure one instance twice without configure null.

+    //

+    if (Instance->Config != NULL) {

+      gBS->RestoreTPL (OldTpl);

+      return EFI_ACCESS_DENIED;

+    }

+

+    //

+    // Duplicate config data including all reference buffers.

+    //

+    Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA));

+    if (Instance->Config == NULL) {

+      gBS->RestoreTPL (OldTpl);

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData);

+    if (EFI_ERROR(Status)) {

+      FreePool (Instance->Config);

+      gBS->RestoreTPL (OldTpl);

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    //

+    // Initialize the Ia descriptor from the config data, and leave the other

+    // fields of the Ia as default value 0.

+    //

+    Instance->IaCb.Ia = AllocateZeroPool (sizeof(EFI_DHCP6_IA));

+    if (Instance->IaCb.Ia == NULL) {

+      Dhcp6CleanupConfigData (Instance->Config);

+      FreePool (Instance->Config);

+      gBS->RestoreTPL (OldTpl);

+      return EFI_OUT_OF_RESOURCES;

+    }

+    CopyMem (

+      &Instance->IaCb.Ia->Descriptor,

+      &Dhcp6CfgData->IaDescriptor,

+      sizeof(EFI_DHCP6_IA_DESCRIPTOR)

+      );

+

+  } else {

+

+    if (Instance->Config == NULL) {

+      ASSERT (Instance->IaCb.Ia == NULL);

+      gBS->RestoreTPL (OldTpl);

+      return EFI_SUCCESS;

+    }

+

+    //

+    // It's not allowed to configure a started instance as null.

+    //

+    if (Instance->IaCb.Ia->State != Dhcp6Init) {

+      gBS->RestoreTPL (OldTpl);

+      return EFI_ACCESS_DENIED;

+    }

+

+    Dhcp6CleanupConfigData (Instance->Config);

+    FreePool (Instance->Config);

+    Instance->Config = NULL;

+

+    FreePool (Instance->IaCb.Ia);

+    Instance->IaCb.Ia = NULL;

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Request configuration information without the assignment of any

+  Ia addresses of the client.

+

+  The InfoRequest() function is used to request configuration information

+  without the assignment of any IPv6 address of the client. The client sends

+  out an Information Request packet to obtain the required configuration

+  information, and DHCPv6 server responds with a Reply packet containing

+  the information for the client. The received Reply packet will be passed

+  to the user by ReplyCallback function. If the user returns EFI_NOT_READY from

+  ReplyCallback, the Dhcp6 instance will continue to receive other Reply

+  packets unless timeout according to the Retransmission parameter.

+  Otherwise, the Information Request exchange process will be finished

+  successfully if user returns EFI_SUCCESS from ReplyCallback.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+  @param[in]  SendClientId      If TRUE, the DHCPv6 protocol instance will build Client

+                                Identifier option and include it into Information Request

+                                packet. Otherwise, Client Identifier option will not be included.

+  @param[in]  OptionRequest     The pointer to the buffer of option request options.

+  @param[in]  OptionCount       The option number in the OptionList.

+  @param[in]  OptionList        The list of appended options.

+  @param[in]  Retransmission    The pointer to the retransmission of the message.

+  @param[in]  TimeoutEvent      The event of timeout.

+  @param[in]  ReplyCallback     The callback function when the reply was received.

+  @param[in]  CallbackContext   The pointer to the parameter passed to the callback.

+

+  @retval EFI_SUCCESS           The DHCPv6 information request exchange process

+                                completed when TimeoutEvent is NULL. Information

+                                Request packet has been sent to DHCPv6 server when

+                                TimeoutEvent is not NULL.

+  @retval EFI_NO_RESPONSE       The DHCPv6 information request exchange process failed

+                                because of no response, or not all requested-options

+                                are responded by DHCPv6 servers when Timeout happened.

+  @retval EFI_ABORTED           The DHCPv6 information request exchange process was aborted

+                                by user.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6InfoRequest (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN BOOLEAN                   SendClientId,

+  IN EFI_DHCP6_PACKET_OPTION   *OptionRequest,

+  IN UINT32                    OptionCount,

+  IN EFI_DHCP6_PACKET_OPTION   *OptionList[]    OPTIONAL,

+  IN EFI_DHCP6_RETRANSMISSION  *Retransmission,

+  IN EFI_EVENT                 TimeoutEvent     OPTIONAL,

+  IN EFI_DHCP6_INFO_CALLBACK   ReplyCallback,

+  IN VOID                      *CallbackContext OPTIONAL

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_TPL                      OldTpl;

+  DHCP6_INSTANCE               *Instance;

+  DHCP6_SERVICE                *Service;

+  DHCP6_INF_CB                 *InfCb;

+  UINTN                        Index;

+

+  if (This == NULL || OptionRequest == NULL || Retransmission == NULL || ReplyCallback == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Retransmission != NULL && Retransmission->Mrc == 0 && Retransmission->Mrd == 0) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (OptionCount > 0 && OptionList == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (OptionList != NULL) {

+    for (Index = 0; Index < OptionCount; Index++) {

+      if (OptionList[Index]->OpCode == Dhcp6OptClientId || OptionList[Index]->OpCode == Dhcp6OptRequestOption) {

+        return EFI_INVALID_PARAMETER;

+      }

+    }

+  }

+

+  Instance = DHCP6_INSTANCE_FROM_THIS (This);

+  Service  = Instance->Service;

+

+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);

+  Instance->UdpSts = EFI_ALREADY_STARTED;

+

+  //

+  // Create and initialize the control block for the info-request.

+  //

+  InfCb = AllocateZeroPool (sizeof(DHCP6_INF_CB));

+

+  if (InfCb == NULL) {

+    gBS->RestoreTPL (OldTpl);

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  InfCb->ReplyCallback   = ReplyCallback;

+  InfCb->CallbackContext = CallbackContext;

+  InfCb->TimeoutEvent    = TimeoutEvent;

+

+  InsertTailList (&Instance->InfList, &InfCb->Link);

+

+  //

+  // Send the info-request message to start exchange process.

+  //

+  Status = Dhcp6SendInfoRequestMsg (

+             Instance,

+             InfCb,

+             SendClientId,

+             OptionRequest,

+             OptionCount,

+             OptionList,

+             Retransmission

+             );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Register receive callback for the stateless exchange process.

+  //

+  Status = UdpIoRecvDatagram(

+             Service->UdpIo,

+             Dhcp6ReceivePacket,

+             Service,

+             0

+             );

+

+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {

+    goto ON_ERROR;

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  //

+  // Poll udp out of the net tpl if synchoronus call.

+  //

+  if (TimeoutEvent == NULL) {

+

+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {

+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);

+    }

+    return Instance->UdpSts;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  RemoveEntryList (&InfCb->Link);

+  FreePool (InfCb);

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  Manually extend the valid and preferred lifetimes for the IPv6 addresses

+  of the configured IA and update other configuration parameters by sending a

+  Renew or Rebind packet.

+

+  The RenewRebind() function is used to manually extend the valid and preferred

+  lifetimes for the IPv6 addresses of the configured IA, and update other

+  configuration parameters by sending Renew or Rebind packet.

+  - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,

+    it sends Renew packet to the previously DHCPv6 server and transfer the

+    state of the configured IA to Dhcp6Renewing. If valid Reply packet received,

+    the state transfers to Dhcp6Bound and the valid and preferred timer restarts.

+    If fails, the state transfers to Dhcp6Bound, but the timer continues.

+  - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,

+    it will send a Rebind packet. If valid Reply packet is received, the state transfers

+    to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state

+    transfers to Dhcp6Init, and the IA can't be used.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+  @param[in]  RebindRequest     If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.

+                                Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.

+

+  @retval EFI_SUCCESS           The DHCPv6 renew/rebind exchange process has

+                                completed and at least one IPv6 address of the

+                                configured IA has been bound again when

+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL.

+                                The EFI DHCPv6 Protocol instance has sent Renew

+                                or Rebind packet when

+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.

+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the

+                                state of the configured IA is not in Dhcp6Bound.

+  @retval EFI_ALREADY_STARTED   The state of the configured IA has already entered

+                                Dhcp6Renewing when RebindRequest is FALSE.

+                                The state of the configured IA has already entered

+                                Dhcp6Rebinding when RebindRequest is TRUE.

+  @retval EFI_ABORTED           The DHCPv6 renew/rebind exchange process aborted

+                                by the user.

+  @retval EFI_NO_RESPONSE       The DHCPv6 renew/rebind exchange process failed

+                                because of no response.

+  @retval EFI_NO_MAPPING        No IPv6 address has been bound to the configured

+                                IA after the DHCPv6 renew/rebind exchange process.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6RenewRebind (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN BOOLEAN                   RebindRequest

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_TPL                      OldTpl;

+  DHCP6_INSTANCE               *Instance;

+  DHCP6_SERVICE                *Service;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = DHCP6_INSTANCE_FROM_THIS (This);

+  Service  = Instance->Service;

+

+  //

+  // The instance hasn't been configured.

+  //

+  if (Instance->Config == NULL) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  ASSERT (Instance->IaCb.Ia != NULL);

+

+  //

+  // The instance has already entered renewing or rebinding state.

+  //

+  if ((Instance->IaCb.Ia->State == Dhcp6Rebinding && RebindRequest) ||

+      (Instance->IaCb.Ia->State == Dhcp6Renewing && !RebindRequest)

+      ) {

+    return EFI_ALREADY_STARTED;

+  }

+

+  if (Instance->IaCb.Ia->State != Dhcp6Bound) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);

+  Instance->UdpSts = EFI_ALREADY_STARTED;

+

+  //

+  // Send renew/rebind message to start exchange process.

+  //

+  Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Register receive callback for the stateful exchange process.

+  //

+  Status = UdpIoRecvDatagram(

+             Service->UdpIo,

+             Dhcp6ReceivePacket,

+             Service,

+             0

+             );

+

+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {

+    goto ON_ERROR;

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  //

+  // Poll udp out of the net tpl if synchoronus call.

+  //

+  if (Instance->Config->IaInfoEvent == NULL) {

+

+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {

+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);

+    }

+    return Instance->UdpSts;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+

+/**

+  Inform that one or more addresses assigned by a server are already

+  in use by another node.

+

+  The Decline() function is used to manually decline the assignment of

+  IPv6 addresses, which have been already used by another node. If all

+  IPv6 addresses of the configured IA are declined through this function,

+  the state of the IA will switch through Dhcp6Declining to Dhcp6Init.

+  Otherwise, the state of the IA will restore to Dhcp6Bound after the

+  declining process. The Decline() can only be called when the IA is in

+  Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,

+  this function is a blocking operation. It will return after the

+  declining process finishes, or aborted by user.

+

+  @param[in]  This              The pointer to EFI_DHCP6_PROTOCOL.

+  @param[in]  AddressCount      The number of declining addresses.

+  @param[in]  Addresses         The pointer to the buffer stored the declining

+                                addresses.

+

+  @retval EFI_SUCCESS           The DHCPv6 decline exchange process completed

+                                when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.

+                                The Dhcp6 instance sent Decline packet when

+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL.

+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the

+                                state of the configured IA is not in Dhcp6Bound.

+  @retval EFI_ABORTED           The DHCPv6 decline exchange process aborted by user.

+  @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with

+                                the configured IA for this instance.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Decline (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN UINT32                    AddressCount,

+  IN EFI_IPv6_ADDRESS          *Addresses

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_TPL                      OldTpl;

+  EFI_DHCP6_IA                 *DecIa;

+  DHCP6_INSTANCE               *Instance;

+  DHCP6_SERVICE                *Service;

+

+  if (This == NULL || AddressCount == 0 || Addresses == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = DHCP6_INSTANCE_FROM_THIS (This);

+  Service  = Instance->Service;

+

+  //

+  // The instance hasn't been configured.

+  //

+  if (Instance->Config == NULL) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  ASSERT (Instance->IaCb.Ia != NULL);

+

+  if (Instance->IaCb.Ia->State != Dhcp6Bound) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  //

+  // Check whether all the declined addresses belongs to the configured Ia.

+  //

+  Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);

+

+  if (EFI_ERROR(Status)) {

+    return Status;

+  }

+

+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);

+  Instance->UdpSts = EFI_ALREADY_STARTED;

+

+  //

+  // Deprive of all the declined addresses from the configured Ia, and create a

+  // DeclineIa used to create decline message.

+  //

+  DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);

+

+  if (DecIa == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_ERROR;

+  }

+

+  //

+  // Send the decline message to start exchange process.

+  //

+  Status = Dhcp6SendDeclineMsg (Instance, DecIa);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Register receive callback for the stateful exchange process.

+  //

+  Status = UdpIoRecvDatagram(

+             Service->UdpIo,

+             Dhcp6ReceivePacket,

+             Service,

+             0

+             );

+

+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {

+    goto ON_ERROR;

+  }

+

+  FreePool (DecIa);

+  gBS->RestoreTPL (OldTpl);

+

+  //

+  // Poll udp out of the net tpl if synchoronus call.

+  //

+  if (Instance->Config->IaInfoEvent == NULL) {

+

+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {

+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);

+    }

+    return Instance->UdpSts;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  if (DecIa != NULL) {

+    FreePool (DecIa);

+  }

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  Release one or more addresses associated with the configured Ia

+  for current instance.

+

+  The Release() function is used to manually release one or more

+  IPv6 addresses. If AddressCount is zero, it will release all IPv6

+  addresses of the configured IA. If all IPv6 addresses of the IA are

+  released through this function, the state of the IA will switch

+  through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the

+  IA will restore to Dhcp6Bound after the releasing process.

+  The Release() can only be called when the IA is in Dhcp6Bound state.

+  If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is

+  a blocking operation. It will return after the releasing process

+  finishes, or is aborted by user.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+  @param[in]  AddressCount      The number of releasing addresses.

+  @param[in]  Addresses         The pointer to the buffer stored the releasing

+                                addresses.

+

+  @retval EFI_SUCCESS           The DHCPv6 release exchange process

+                                completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent

+                                was NULL. The Dhcp6 instance was sent Release

+                                packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent

+                                was not NULL.

+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the

+                                state of the configured IA is not in Dhcp6Bound.

+  @retval EFI_ABORTED           The DHCPv6 release exchange process aborted by user.

+  @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with

+                                the configured IA for this instance.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Release (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN UINT32                    AddressCount,

+  IN EFI_IPv6_ADDRESS          *Addresses

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_TPL                      OldTpl;

+  EFI_DHCP6_IA                 *RelIa;

+  DHCP6_INSTANCE               *Instance;

+  DHCP6_SERVICE                *Service;

+

+  if (This == NULL || (AddressCount != 0 && Addresses == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = DHCP6_INSTANCE_FROM_THIS (This);

+  Service  = Instance->Service;

+

+  //

+  // The instance hasn't been configured.

+  //

+  if (Instance->Config == NULL) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  ASSERT (Instance->IaCb.Ia != NULL);

+

+  if (Instance->IaCb.Ia->State != Dhcp6Bound) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  //

+  // Check whether all the released addresses belongs to the configured Ia.

+  //

+  Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);

+

+  if (EFI_ERROR(Status)) {

+    return Status;

+  }

+

+  OldTpl           = gBS->RaiseTPL (TPL_CALLBACK);

+  Instance->UdpSts = EFI_ALREADY_STARTED;

+

+  //

+  // Deprive of all the released addresses from the configured Ia, and create a

+  // ReleaseIa used to create release message.

+  //

+  RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);

+

+  if (RelIa == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_ERROR;

+  }

+

+  //

+  // Send the release message to start exchange process.

+  //

+  Status = Dhcp6SendReleaseMsg (Instance, RelIa);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Register receive callback for the stateful exchange process.

+  //

+  Status = UdpIoRecvDatagram(

+             Service->UdpIo,

+             Dhcp6ReceivePacket,

+             Service,

+             0

+             );

+

+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {

+    goto ON_ERROR;

+  }

+

+  FreePool (RelIa);

+  gBS->RestoreTPL (OldTpl);

+

+  //

+  // Poll udp out of the net tpl if synchoronus call.

+  //

+  if (Instance->Config->IaInfoEvent == NULL) {

+    while (Instance->UdpSts == EFI_ALREADY_STARTED) {

+      Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);

+    }

+    return Instance->UdpSts;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  if (RelIa != NULL) {

+    FreePool (RelIa);

+  }

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  Parse the option data in the Dhcp6 packet.

+

+  The Parse() function is used to retrieve the option list in the DHCPv6 packet.

+

+  @param[in]      This              The pointer to the Dhcp6 protocol.

+  @param[in]      Packet            The pointer to the Dhcp6 packet.

+  @param[in, out] OptionCount       The number of option in the packet.

+  @param[out]     PacketOptionList  The array of pointers to each option in the packet.

+

+  @retval EFI_SUCCESS           The packet was successfully parsed.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_BUFFER_TOO_SMALL  *OptionCount is smaller than the number of options

+                                that were found in the Packet.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Parse (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN EFI_DHCP6_PACKET          *Packet,

+  IN OUT UINT32                *OptionCount,

+  OUT EFI_DHCP6_PACKET_OPTION  *PacketOptionList[]  OPTIONAL

+  )

+{

+  UINT32                       OptCnt;

+  UINT32                       OptLen;

+  UINT16                       DataLen;

+  UINT8                        *Start;

+  UINT8                        *End;

+

+  if (This == NULL || Packet == NULL || OptionCount == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (*OptionCount != 0 && PacketOptionList == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Packet->Length > Packet->Size || Packet->Length < sizeof (EFI_DHCP6_HEADER)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  //  The format of Dhcp6 option:

+  //

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |          option-code          |   option-len (option data)    |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                          option-data                          |

+  //    |                      (option-len octets)                      |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  OptCnt = 0;

+  OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER);

+  Start  = Packet->Dhcp6.Option;

+  End    = Start + OptLen;

+

+  //

+  // Calculate the number of option in the packet.

+  //

+  while (Start < End) {

+    DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;

+    Start  += (NTOHS (DataLen) + 4);

+    OptCnt++;

+  }

+

+  //

+  // It will return buffer too small if pass-in option count is smaller than the

+  // actual count of options in the packet.

+  //

+  if (OptCnt > *OptionCount) {

+    *OptionCount = OptCnt;

+    return EFI_BUFFER_TOO_SMALL;

+  }

+

+  ZeroMem (

+    PacketOptionList,

+    (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *))

+    );

+

+  OptCnt = 0;

+  Start  = Packet->Dhcp6.Option;

+

+  while (Start < End) {

+

+    PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *) Start;

+    DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;

+    Start  += (NTOHS (DataLen) + 4);

+    OptCnt++;

+  }

+

+  return EFI_SUCCESS;

+}

+

diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h
new file mode 100644
index 0000000..8fb1dfa
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h
@@ -0,0 +1,597 @@
+/** @file

+  Dhcp6 internal data structure and definition declaration.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_DHCP6_IMPL_H__

+#define __EFI_DHCP6_IMPL_H__

+

+

+#include <Uefi.h>

+

+#include <Protocol/Dhcp6.h>

+#include <Protocol/Udp6.h>

+#include <Protocol/ServiceBinding.h>

+#include <Protocol/DriverBinding.h>

+

+#include <Library/UdpIoLib.h>

+#include <Library/DebugLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiRuntimeServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/BaseLib.h>

+#include <Library/NetLib.h>

+

+

+typedef struct _DHCP6_IA_CB    DHCP6_IA_CB;

+typedef struct _DHCP6_INF_CB   DHCP6_INF_CB;

+typedef struct _DHCP6_TX_CB    DHCP6_TX_CB;

+typedef struct _DHCP6_SERVICE  DHCP6_SERVICE;

+typedef struct _DHCP6_INSTANCE DHCP6_INSTANCE;

+

+#include "Dhcp6Utility.h"

+#include "Dhcp6Io.h"

+#include "Dhcp6Driver.h"

+

+#define DHCP6_SERVICE_SIGNATURE   SIGNATURE_32 ('D', 'H', '6', 'S')

+#define DHCP6_INSTANCE_SIGNATURE  SIGNATURE_32 ('D', 'H', '6', 'I')

+

+//

+// Transmit parameters of solicit message, refers to section-5.5 of rfc-3315.

+//

+#define DHCP6_SOL_MAX_DELAY       1

+#define DHCP6_SOL_IRT             1

+#define DHCP6_SOL_MRC             0

+#define DHCP6_SOL_MRT             120

+#define DHCP6_SOL_MRD             0

+//

+// Transmit parameters of request message, refers to section-5.5 of rfc-3315.

+//

+#define DHCP6_REQ_IRT             1

+#define DHCP6_REQ_MRC             10

+#define DHCP6_REQ_MRT             30

+#define DHCP6_REQ_MRD             0

+//

+// Transmit parameters of confirm message, refers to section-5.5 of rfc-3315.

+//

+#define DHCP6_CNF_MAX_DELAY       1

+#define DHCP6_CNF_IRT             1

+#define DHCP6_CNF_MRC             0

+#define DHCP6_CNF_MRT             4

+#define DHCP6_CNF_MRD             10

+//

+// Transmit parameters of renew message, refers to section-5.5 of rfc-3315.

+//

+#define DHCP6_REN_IRT             10

+#define DHCP6_REN_MRC             0

+#define DHCP6_REN_MRT             600

+#define DHCP6_REN_MRD             0

+//

+// Transmit parameters of rebind message, refers to section-5.5 of rfc-3315.

+//

+#define DHCP6_REB_IRT             10

+#define DHCP6_REB_MRC             0

+#define DHCP6_REB_MRT             600

+#define DHCP6_REB_MRD             0

+//

+// Transmit parameters of information request message, refers to section-5.5 of rfc-3315.

+//

+#define DHCP6_INF_MAX_DELAY       1

+#define DHCP6_INF_IRT             1

+#define DHCP6_INF_MRC             0

+#define DHCP6_INF_MRT             120

+#define DHCP6_INF_MRD             0

+//

+// Transmit parameters of release message, refers to section-5.5 of rfc-3315.

+//

+#define DHCP6_REL_IRT             1

+#define DHCP6_REL_MRC             5

+#define DHCP6_REL_MRT             0

+#define DHCP6_REL_MRD             0

+//

+// Transmit parameters of decline message, refers to section-5.5 of rfc-3315.

+//

+#define DHCP6_DEC_IRT             1

+#define DHCP6_DEC_MRC             5

+#define DHCP6_DEC_MRT             0

+#define DHCP6_DEC_MRD             0

+

+#define DHCP6_PACKET_ALL          0

+#define DHCP6_PACKET_STATEFUL     1

+#define DHCP6_PACKET_STATELESS    2

+

+#define DHCP6_BASE_PACKET_SIZE    1024

+

+#define DHCP6_PORT_CLIENT         546

+#define DHCP6_PORT_SERVER         547

+

+#define DHCP6_INSTANCE_FROM_THIS(Instance) CR ((Instance), DHCP6_INSTANCE, Dhcp6, DHCP6_INSTANCE_SIGNATURE)

+#define DHCP6_SERVICE_FROM_THIS(Service)   CR ((Service), DHCP6_SERVICE, ServiceBinding, DHCP6_SERVICE_SIGNATURE)

+

+extern EFI_IPv6_ADDRESS           mAllDhcpRelayAndServersAddress;

+extern EFI_IPv6_ADDRESS           mAllDhcpServersAddress;

+extern EFI_DHCP6_PROTOCOL         gDhcp6ProtocolTemplate;

+

+//

+// Enumeration of Dhcp6 message type, refers to section-5.3 of rfc-3315.

+//

+typedef enum {

+  Dhcp6MsgSolicit               = 1,

+  Dhcp6MsgAdvertise             = 2,

+  Dhcp6MsgRequest               = 3,

+  Dhcp6MsgConfirm               = 4,

+  Dhcp6MsgRenew                 = 5,

+  Dhcp6MsgRebind                = 6,

+  Dhcp6MsgReply                 = 7,

+  Dhcp6MsgRelease               = 8,

+  Dhcp6MsgDecline               = 9,

+  Dhcp6MsgReconfigure           = 10,

+  Dhcp6MsgInfoRequest           = 11

+} DHCP6_MSG_TYPE;

+

+//

+// Enumeration of option code in Dhcp6 packet, refers to section-24.3 of rfc-3315.

+//

+typedef enum {

+  Dhcp6OptClientId              = 1,

+  Dhcp6OptServerId              = 2,

+  Dhcp6OptIana                  = 3,

+  Dhcp6OptIata                  = 4,

+  Dhcp6OptIaAddr                = 5,

+  Dhcp6OptRequestOption         = 6,

+  Dhcp6OptPreference            = 7,

+  Dhcp6OptElapsedTime           = 8,

+  Dhcp6OptReplayMessage         = 9,

+  Dhcp6OptAuthentication        = 11,

+  Dhcp6OptServerUnicast         = 12,

+  Dhcp6OptStatusCode            = 13,

+  Dhcp6OptRapidCommit           = 14,

+  Dhcp6OptUserClass             = 15,

+  Dhcp6OptVendorClass           = 16,

+  Dhcp6OptVendorInfo            = 17,

+  Dhcp6OptInterfaceId           = 18,

+  Dhcp6OptReconfigMessage       = 19,

+  Dhcp6OptReconfigureAccept     = 20

+} DHCP6_OPT_CODE;

+

+//

+// Enumeration of status code recorded by IANA, refers to section-24.4 of rfc-3315.

+//

+typedef enum {

+  Dhcp6StsSuccess               = 0,

+  Dhcp6StsUnspecFail            = 1,

+  Dhcp6StsNoAddrsAvail          = 2,

+  Dhcp6StsNoBinding             = 3,

+  Dhcp6StsNotOnLink             = 4,

+  Dhcp6StsUseMulticast          = 5

+} DHCP6_STS_CODE;

+

+//

+// Enumeration of Duid type recorded by IANA, refers to section-24.5 of rfc-3315.

+//

+typedef enum {

+  Dhcp6DuidTypeLlt              = 1,

+  Dhcp6DuidTypeEn               = 2,

+  Dhcp6DuidTypeLl               = 3

+} DHCP6_DUID_TYPE;

+

+//

+// Control block for each IA.

+//

+struct _DHCP6_IA_CB {

+  EFI_DHCP6_IA                  *Ia;

+  UINT32                        T1;

+  UINT32                        T2;

+  UINT32                        AllExpireTime;

+  UINT32                        LeaseTime;

+};

+

+//

+// Control block for each transmitted message.

+//

+struct _DHCP6_TX_CB {

+  LIST_ENTRY                    Link;

+  UINT32                        Xid;

+  EFI_DHCP6_PACKET              *TxPacket;

+  EFI_DHCP6_RETRANSMISSION      RetryCtl;

+  UINT32                        RetryCnt;

+  UINT32                        RetryExp;

+  UINT32                        RetryLos;

+  UINT32                        TickTime;

+  UINT16                        *Elapsed;

+};

+

+//

+// Control block for each info-request message.

+//

+struct _DHCP6_INF_CB {

+  LIST_ENTRY                    Link;

+  UINT32                        Xid;

+  EFI_DHCP6_INFO_CALLBACK       ReplyCallback;

+  VOID                          *CallbackContext;

+  EFI_EVENT                     TimeoutEvent;

+};

+

+//

+// Control block for Dhcp6 instance, it's per configuration data.

+//

+struct _DHCP6_INSTANCE {

+  UINT32                        Signature;

+  EFI_HANDLE                    Handle;

+  DHCP6_SERVICE                 *Service;

+  LIST_ENTRY                    Link;

+  EFI_DHCP6_PROTOCOL            Dhcp6;

+  EFI_EVENT                     Timer;

+  EFI_DHCP6_CONFIG_DATA         *Config;

+  EFI_DHCP6_IA                  *CacheIa;

+  DHCP6_IA_CB                   IaCb;

+  LIST_ENTRY                    TxList;

+  LIST_ENTRY                    InfList;

+  EFI_DHCP6_PACKET              *AdSelect;

+  UINT8                         AdPref;

+  EFI_IPv6_ADDRESS              *Unicast;

+  EFI_STATUS                    UdpSts;

+  BOOLEAN                       InDestory;

+  BOOLEAN                       MediaPresent;

+  UINT64                        StartTime;

+};

+

+//

+// Control block for Dhcp6 service, it's per Nic handle.

+//

+struct _DHCP6_SERVICE {

+  UINT32                        Signature;

+  EFI_HANDLE                    Controller;

+  EFI_HANDLE                    Image;

+  EFI_SERVICE_BINDING_PROTOCOL  ServiceBinding;

+  EFI_SIMPLE_NETWORK_PROTOCOL   *Snp;

+  EFI_DHCP6_DUID                *ClientId;

+  UDP_IO                        *UdpIo;

+  UINT32                        Xid;

+  LIST_ENTRY                    Child;

+  UINTN                         NumOfChild;

+  BOOLEAN                       InDestory;

+};

+

+/**

+  Starts the DHCPv6 standard S.A.R.R. process.

+

+  The Start() function starts the DHCPv6 standard process. This function can

+  be called only when the state of Dhcp6 instance is in the Dhcp6Init state.

+  If the DHCP process completes successfully, the state of the Dhcp6 instance

+  will be transferred through Dhcp6Selecting and Dhcp6Requesting to the

+  Dhcp6Bound state.

+  Refer to rfc-3315 for precise state transitions during this process. At the

+  time when each event occurs in this process, the callback function that was set

+  by EFI_DHCP6_PROTOCOL.Configure() will be called and the user can take this

+  opportunity to control the process.

+

+  @param[in]  This              The pointer to Dhcp6 protocol.

+

+  @retval EFI_SUCCESS           The DHCPv6 standard process has started, or it

+                                completed when CompletionEvent was NULL.

+  @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Child instance hasn't been configured.

+  @retval EFI_INVALID_PARAMETER This is NULL.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_TIMEOUT           The DHCPv6 configuration process failed because no

+                                response was received from the server within the

+                                specified timeout value.

+  @retval EFI_ABORTED           The user aborted the DHCPv6 process.

+  @retval EFI_ALREADY_STARTED   Some other Dhcp6 instance already started the DHCPv6

+                                standard process.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Start (

+  IN EFI_DHCP6_PROTOCOL        *This

+  );

+

+/**

+  Stops the DHCPv6 standard S.A.R.R. process.

+

+  The Stop() function is used to stop the DHCPv6 standard process. After this

+  function is called successfully, the state of Dhcp6 instance is transferred

+  into the Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called

+  before DHCPv6 standard process can be started again. This function can be

+  called when the Dhcp6 instance is in any state.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+

+  @retval EFI_SUCCESS           The Dhcp6 instance is now in the Dhcp6Init state.

+  @retval EFI_INVALID_PARAMETER This is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Stop (

+  IN EFI_DHCP6_PROTOCOL        *This

+  );

+

+/**

+  Returns the current operating mode data for the Dhcp6 instance.

+

+  The GetModeData() function returns the current operating mode and

+  cached data packet for the Dhcp6 instance.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+  @param[out] Dhcp6ModeData     The pointer to the Dhcp6 mode data.

+  @param[out] Dhcp6ConfigData   The pointer to the Dhcp6 configure data.

+

+  @retval EFI_SUCCESS           The mode data was returned.

+  @retval EFI_INVALID_PARAMETER This is NULL.

+  @retval EFI_ACCESS_DENIED     The EFI DHCPv6 Protocol instance has not

+                                been configured when Dhcp6ConfigData is

+                                not NULL.

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6GetModeData (

+  IN  EFI_DHCP6_PROTOCOL       *This,

+  OUT EFI_DHCP6_MODE_DATA      *Dhcp6ModeData      OPTIONAL,

+  OUT EFI_DHCP6_CONFIG_DATA    *Dhcp6ConfigData    OPTIONAL

+  );

+

+/**

+  Initializes, changes, or resets the operational settings for the Dhcp6 instance.

+

+  The Configure() function is used to initialize or clean up the configuration

+  data of the Dhcp6 instance:

+  - When Dhcp6CfgData is not NULL and Configure() is called successfully, the

+    configuration data will be initialized in the Dhcp6 instance and the state

+    of the configured IA will be transferred into Dhcp6Init.

+  - When Dhcp6CfgData is NULL and Configure() is called successfully, the

+    configuration data will be cleaned up and no IA will be associated with

+    the Dhcp6 instance.

+  To update the configuration data for an Dhcp6 instance, the original data

+  must be cleaned up before setting the new configuration data.

+

+  @param[in]  This                   The pointer to the Dhcp6 protocol

+  @param[in]  Dhcp6CfgData           The pointer to the EFI_DHCP6_CONFIG_DATA.

+

+  @retval EFI_SUCCESS           The Dhcp6 is configured successfully with the

+                                Dhcp6Init state, or cleaned up the original

+                                configuration setting.

+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance has been already configured

+                                when Dhcp6CfgData is not NULL.

+                                The Dhcp6 instance has already started the

+                                DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.

+  @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Configure (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN EFI_DHCP6_CONFIG_DATA     *Dhcp6CfgData    OPTIONAL

+  );

+

+/**

+  Request configuration information without the assignment of any

+  Ia addresses of the client.

+

+  The InfoRequest() function is used to request configuration information

+  without the assignment of any IPv6 address of the client. Client sends

+  out Information Request packet to obtain the required configuration

+  information, and DHCPv6 server responds with Reply packet containing

+  the information for the client. The received Reply packet will be passed

+  to the user by ReplyCallback function. If user returns EFI_NOT_READY from

+  ReplyCallback, the Dhcp6 instance will continue to receive other Reply

+  packets unless timeout according to the Retransmission parameter.

+  Otherwise, the Information Request exchange process will be finished

+  successfully if user returns EFI_SUCCESS from ReplyCallback.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+  @param[in]  SendClientId      If TRUE, the DHCPv6 protocol instance will build Client

+                                Identifier option and include it into Information Request

+                                packet. Otherwise, Client Identifier option will not be included.

+  @param[in]  OptionRequest     The pointer to the buffer of option request options.

+  @param[in]  OptionCount       The option number in the OptionList.

+  @param[in]  OptionList        The list of appended options.

+  @param[in]  Retransmission    The pointer to the retransmission of the message.

+  @param[in]  TimeoutEvent      The event of timeout.

+  @param[in]  ReplyCallback     The callback function when a reply was received.

+  @param[in]  CallbackContext   The pointer to the parameter passed to the callback.

+

+  @retval EFI_SUCCESS           The DHCPv6 information request exchange process

+                                completed when TimeoutEvent is NULL. Information

+                                Request packet has been sent to DHCPv6 server when

+                                TimeoutEvent is not NULL.

+  @retval EFI_NO_RESPONSE       The DHCPv6 information request exchange process failed

+                                because of no response, or not all requested-options

+                                are responded to by DHCPv6 servers when Timeout happened.

+  @retval EFI_ABORTED           The DHCPv6 information request exchange process was aborted

+                                by the user.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6InfoRequest (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN BOOLEAN                   SendClientId,

+  IN EFI_DHCP6_PACKET_OPTION   *OptionRequest,

+  IN UINT32                    OptionCount,

+  IN EFI_DHCP6_PACKET_OPTION   *OptionList[]    OPTIONAL,

+  IN EFI_DHCP6_RETRANSMISSION  *Retransmission,

+  IN EFI_EVENT                 TimeoutEvent     OPTIONAL,

+  IN EFI_DHCP6_INFO_CALLBACK   ReplyCallback,

+  IN VOID                      *CallbackContext OPTIONAL

+  );

+

+/**

+  Manually extend the valid and preferred lifetimes for the IPv6 addresses

+  of the configured IA and update other configuration parameters by sending

+  Renew or Rebind packet.

+

+  The RenewRebind() function is used to manually extend the valid and preferred

+  lifetimes for the IPv6 addresses of the configured IA and update other

+  configuration parameters by sending a Renew or Rebind packet.

+  - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,

+    it will send Renew packet to the previously DHCPv6 server and transfer the

+    state of the configured IA to Dhcp6Renewing. If valid Reply packet received,

+    the state transfers to Dhcp6Bound and the valid and preferred timer restarts.

+    If fails, the state transfers to Dhcp6Bound but the timer continues.

+  - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,

+    it will send a Rebind packet. If a valid Reply packet is received, the state transfers

+    to Dhcp6Bound, and the valid and preferred timer restarts. If it fails, the state

+    transfers to Dhcp6Init, and the IA can't be used.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+  @param[in]  RebindRequest     If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.

+                                Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.

+

+  @retval EFI_SUCCESS           The DHCPv6 renew/rebind exchange process

+                                completed and at least one IPv6 address of the

+                                configured IA was bound again when

+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.

+                                The EFI DHCPv6 Protocol instance has sent Renew

+                                or Rebind packet when

+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.

+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the

+                                state of the configured IA is not in Dhcp6Bound.

+  @retval EFI_ALREADY_STARTED   The state of the configured IA has already entered

+                                Dhcp6Renewing when RebindRequest is FALSE.

+                                The state of the configured IA has already entered

+                                Dhcp6Rebinding when RebindRequest is TRUE.

+  @retval EFI_ABORTED           The DHCPv6 renew/rebind exchange process aborted

+                                by user.

+  @retval EFI_NO_RESPONSE       The DHCPv6 renew/rebind exchange process failed

+                                because of no response.

+  @retval EFI_NO_MAPPING        No IPv6 address has been bound to the configured

+                                IA after the DHCPv6 renew/rebind exchange process.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6RenewRebind (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN BOOLEAN                   RebindRequest

+  );

+

+/**

+  Inform that one or more addresses assigned by a server are already

+  in use by another node.

+

+  The Decline() function is used to manually decline the assignment of

+  IPv6 addresses, which have been already used by another node. If all

+  IPv6 addresses of the configured IA are declined through this function,

+  the state of the IA will switch through Dhcp6Declining to Dhcp6Init.

+  Otherwise, the state of the IA will restore to Dhcp6Bound after the

+  declining process. The Decline() can only be called when the IA is in

+  Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,

+  this function is a blocking operation. It will return after the

+  declining process finishes, or aborted by user.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+  @param[in]  AddressCount      The number of declining addresses.

+  @param[in]  Addresses         The pointer to the buffer stored the declining

+                                addresses.

+

+  @retval EFI_SUCCESS           The DHCPv6 decline exchange process completed

+                                when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.

+                                The Dhcp6 instance has sent Decline packet when

+                                EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.

+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the

+                                state of the configured IA is not in Dhcp6Bound.

+  @retval EFI_ABORTED           The DHCPv6 decline exchange process was aborted by the user.

+  @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with

+                                the configured IA for this instance.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Decline (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN UINT32                    AddressCount,

+  IN EFI_IPv6_ADDRESS          *Addresses

+  );

+

+/**

+  Release one or more addresses associated with the configured Ia

+  for the current instance.

+

+  The Release() function is used to manually release the one or more

+  IPv6 address. If AddressCount is zero, it will release all IPv6

+  addresses of the configured IA. If all IPv6 addresses of the IA are

+  released through this function, the state of the IA will switch

+  through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the

+  IA will restore to Dhcp6Bound after the releasing process.

+  The Release() can only be called when the IA is in a Dhcp6Bound state.

+  If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is

+  a blocking operation. It will return after the releasing process

+  finishes, or aborted by user.

+

+  @param[in]  This              The pointer to the Dhcp6 protocol.

+  @param[in]  AddressCount      The number of releasing addresses.

+  @param[in]  Addresses         The pointer to the buffer stored the releasing

+                                addresses.

+  @retval EFI_SUCCESS           The DHCPv6 release exchange process has

+                                completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent

+                                is NULL. The Dhcp6 instance has sent Release

+                                packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent

+                                is not NULL.

+  @retval EFI_ACCESS_DENIED     The Dhcp6 instance hasn't been configured, or the

+                                state of the configured IA is not in Dhcp6Bound.

+  @retval EFI_ABORTED           The DHCPv6 release exchange process was aborted by the user.

+  @retval EFI_NOT_FOUND         Any specified IPv6 address is not correlated with

+                                the configured IA for this instance.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Release (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN UINT32                    AddressCount,

+  IN EFI_IPv6_ADDRESS          *Addresses

+  );

+

+/**

+  Parse the option data in the Dhcp6 packet.

+

+  The Parse() function is used to retrieve the option list in the DHCPv6 packet.

+

+  @param[in]      This              The pointer to the Dhcp6 protocol.

+  @param[in]      Packet            The pointer to the Dhcp6 packet.

+  @param[in, out] OptionCount       The number of option in the packet.

+  @param[out]     PacketOptionList  The array of pointers to the each option in the packet.

+

+  @retval EFI_SUCCESS           The packet was successfully parsed.

+  @retval EFI_INVALID_PARAMETER Some parameter is NULL.

+  @retval EFI_BUFFER_TOO_SMALL  *OptionCount is smaller than the number of options

+                                that were found in the Packet.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiDhcp6Parse (

+  IN EFI_DHCP6_PROTOCOL        *This,

+  IN EFI_DHCP6_PACKET          *Packet,

+  IN OUT UINT32                *OptionCount,

+  OUT EFI_DHCP6_PACKET_OPTION  *PacketOptionList[]  OPTIONAL

+  );

+

+#endif

diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c
new file mode 100644
index 0000000..761f9c2
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c
@@ -0,0 +1,2965 @@
+/** @file

+  Dhcp6 internal functions implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Dhcp6Impl.h"

+

+

+/**

+  Enqueue the packet into the retry list in case of timeout.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  Packet          The pointer to the Dhcp6 packet to retry.

+  @param[in]  Elapsed         The pointer to the elapsed time value in the packet.

+  @param[in]  RetryCtl        The pointer to the transmission control of the packet.

+                              This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS           Successfully enqueued the packet into the retry list according

+                                to its message type.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected message type.

+

+**/

+EFI_STATUS

+Dhcp6EnqueueRetry (

+  IN DHCP6_INSTANCE            *Instance,

+  IN EFI_DHCP6_PACKET          *Packet,

+  IN UINT16                    *Elapsed,

+  IN EFI_DHCP6_RETRANSMISSION  *RetryCtl     OPTIONAL

+  )

+{

+  DHCP6_TX_CB                  *TxCb;

+  DHCP6_IA_CB                  *IaCb;

+

+  ASSERT (Packet != NULL);

+

+  IaCb = &Instance->IaCb;

+  TxCb = AllocateZeroPool (sizeof (DHCP6_TX_CB));

+

+  if (TxCb == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Save tx packet pointer, and it will be destoryed when reply received.

+  //

+  TxCb->TxPacket = Packet;

+  TxCb->Xid      = Packet->Dhcp6.Header.TransactionId;

+

+  //

+  // Save pointer to elapsed-time value so we can update it on retransmits.

+  //

+  TxCb->Elapsed  = Elapsed;

+

+  //

+  // Calculate the retransmission according to the the message type.

+  //

+  switch (Packet->Dhcp6.Header.MessageType) {

+  case Dhcp6MsgSolicit:

+    //

+    // Calculate the retransmission threshold value for solicit packet.

+    // Use the default value by rfc-3315 if user doesn't configure.

+    //

+    if (RetryCtl == NULL) {

+      TxCb->RetryCtl.Irt = DHCP6_SOL_IRT;

+      TxCb->RetryCtl.Mrc = DHCP6_SOL_MRC;

+      TxCb->RetryCtl.Mrt = DHCP6_SOL_MRT;

+      TxCb->RetryCtl.Mrd = DHCP6_SOL_MRD;

+    } else {

+      TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_SOL_IRT;

+      TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_SOL_MRC;

+      TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_SOL_MRT;

+      TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_SOL_MRD;

+    }

+

+    TxCb->RetryExp       = Dhcp6CalculateExpireTime (

+                             TxCb->RetryCtl.Irt,

+                             TRUE,

+                             FALSE

+                             );

+    break;

+

+  case Dhcp6MsgRequest:

+    //

+    // Calculate the retransmission threshold value for request packet.

+    //

+    TxCb->RetryCtl.Irt = DHCP6_REQ_IRT;

+    TxCb->RetryCtl.Mrc = DHCP6_REQ_MRC;

+    TxCb->RetryCtl.Mrt = DHCP6_REQ_MRT;

+    TxCb->RetryCtl.Mrd = DHCP6_REQ_MRD;

+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (

+                           TxCb->RetryCtl.Irt,

+                           TRUE,

+                           TRUE

+                           );

+    break;

+

+  case Dhcp6MsgConfirm:

+    //

+    // Calculate the retransmission threshold value for confirm packet.

+    //

+    TxCb->RetryCtl.Irt = DHCP6_CNF_IRT;

+    TxCb->RetryCtl.Mrc = DHCP6_CNF_MRC;

+    TxCb->RetryCtl.Mrt = DHCP6_CNF_MRT;

+    TxCb->RetryCtl.Mrd = DHCP6_CNF_MRD;

+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (

+                           TxCb->RetryCtl.Irt,

+                           TRUE,

+                           TRUE

+                           );

+    break;

+

+  case Dhcp6MsgRenew:

+    //

+    // Calculate the retransmission threshold value for renew packet.

+    //

+    TxCb->RetryCtl.Irt = DHCP6_REB_IRT;

+    TxCb->RetryCtl.Mrc = DHCP6_REB_MRC;

+    TxCb->RetryCtl.Mrt = DHCP6_REB_MRT;

+    TxCb->RetryCtl.Mrd = IaCb->T2 - IaCb->T1;

+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (

+                           TxCb->RetryCtl.Irt,

+                           TRUE,

+                           TRUE

+                           );

+    break;

+

+  case Dhcp6MsgRebind:

+    //

+    // Calculate the retransmission threshold value for rebind packet.

+    //

+    TxCb->RetryCtl.Irt = DHCP6_REN_IRT;

+    TxCb->RetryCtl.Mrc = DHCP6_REN_MRC;

+    TxCb->RetryCtl.Mrt = DHCP6_REN_MRT;

+    TxCb->RetryCtl.Mrd = IaCb->AllExpireTime - IaCb->T2;

+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (

+                           TxCb->RetryCtl.Irt,

+                           TRUE,

+                           TRUE

+                           );

+    break;

+

+  case Dhcp6MsgDecline:

+    //

+    // Calculate the retransmission threshold value for decline packet.

+    //

+    TxCb->RetryCtl.Irt = DHCP6_DEC_IRT;

+    TxCb->RetryCtl.Mrc = DHCP6_DEC_MRC;

+    TxCb->RetryCtl.Mrt = DHCP6_DEC_MRT;

+    TxCb->RetryCtl.Mrd = DHCP6_DEC_MRD;

+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (

+                           TxCb->RetryCtl.Irt,

+                           TRUE,

+                           TRUE

+                           );

+    break;

+

+  case Dhcp6MsgRelease:

+    //

+    // Calculate the retransmission threshold value for release packet.

+    //

+    TxCb->RetryCtl.Irt = DHCP6_REL_IRT;

+    TxCb->RetryCtl.Mrc = DHCP6_REL_MRC;

+    TxCb->RetryCtl.Mrt = DHCP6_REL_MRT;

+    TxCb->RetryCtl.Mrd = DHCP6_REL_MRD;

+    TxCb->RetryExp     = Dhcp6CalculateExpireTime (

+                           TxCb->RetryCtl.Irt,

+                           TRUE,

+                           TRUE

+                           );

+    break;

+

+  case Dhcp6MsgInfoRequest:

+    //

+    // Calculate the retransmission threshold value for info-request packet.

+    // Use the default value by rfc-3315 if user doesn't configure.

+    //

+    if (RetryCtl == NULL) {

+      TxCb->RetryCtl.Irt = DHCP6_INF_IRT;

+      TxCb->RetryCtl.Mrc = DHCP6_INF_MRC;

+      TxCb->RetryCtl.Mrt = DHCP6_INF_MRT;

+      TxCb->RetryCtl.Mrd = DHCP6_INF_MRD;

+    } else {

+      TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_INF_IRT;

+      TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_INF_MRC;

+      TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_INF_MRT;

+      TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_INF_MRD;

+    }

+

+    TxCb->RetryExp       = Dhcp6CalculateExpireTime (

+                             TxCb->RetryCtl.Irt,

+                             TRUE,

+                             TRUE

+                             );

+    break;

+

+  default:

+    //

+    // Unexpected message type.

+    //

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // Insert into the retransmit list of the instance.

+  //

+  InsertTailList (&Instance->TxList, &TxCb->Link);

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Dequeue the packet from retry list if reply received or timeout at last.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  PacketXid       The packet transaction id to match.

+  @param[in]  NeedSignal      If TRUE, then an timeout event need be signaled when it is existed.

+                              Otherwise, this parameter is ignored.

+

+  @retval EFI_SUCCESS         Successfully dequeued the packet into retry list .

+  @retval EFI_NOT_FOUND       There is no xid matched in retry list.

+

+**/

+EFI_STATUS

+Dhcp6DequeueRetry (

+  IN DHCP6_INSTANCE         *Instance,

+  IN UINT32                 PacketXid,

+  IN BOOLEAN                NeedSignal

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *NextEntry;

+  DHCP6_TX_CB               *TxCb;

+  DHCP6_INF_CB              *InfCb;

+

+  //

+  // Seek the retransmit node in the retransmit list by packet xid.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {

+

+    TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);

+    ASSERT(TxCb->TxPacket);

+

+    if (TxCb->Xid == PacketXid) {

+

+      if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {

+

+        //

+        // Seek the info-request node in the info-request list by packet xid.

+        //

+        NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {

+

+          InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);

+

+          if (InfCb->Xid == PacketXid) {

+            //

+            // Remove the info-request node, and signal the event if timeout.

+            //

+            if (InfCb->TimeoutEvent != NULL && NeedSignal) {

+              gBS->SignalEvent (InfCb->TimeoutEvent);

+            }

+

+            RemoveEntryList (&InfCb->Link);

+            FreePool (InfCb);

+          }

+        }

+      }

+      //

+      // Remove the retransmit node.

+      //

+      RemoveEntryList (&TxCb->Link);

+      ASSERT(TxCb->TxPacket);

+      FreePool (TxCb->TxPacket);

+      FreePool (TxCb);

+      return EFI_SUCCESS;

+    }

+  }

+

+  return EFI_NOT_FOUND;

+}

+

+

+/**

+  Clean up the specific nodes in the retry list.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  Scope           The scope of cleanup nodes.

+

+**/

+VOID

+Dhcp6CleanupRetry (

+  IN DHCP6_INSTANCE         *Instance,

+  IN UINT32                 Scope

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *NextEntry;

+  DHCP6_TX_CB               *TxCb;

+  DHCP6_INF_CB              *InfCb;

+

+  //

+  // Clean up all the stateful messages from the retransmit list.

+  //

+  if (Scope == DHCP6_PACKET_STATEFUL || Scope == DHCP6_PACKET_ALL) {

+

+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {

+

+      TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);

+      ASSERT(TxCb->TxPacket);

+

+      if (TxCb->TxPacket->Dhcp6.Header.MessageType != Dhcp6MsgInfoRequest) {

+        RemoveEntryList (&TxCb->Link);

+        FreePool (TxCb->TxPacket);

+        FreePool (TxCb);

+      }

+    }

+  }

+

+  //

+  // Clean up all the stateless messages from the retransmit list.

+  //

+  if (Scope == DHCP6_PACKET_STATELESS || Scope == DHCP6_PACKET_ALL) {

+

+    //

+    // Clean up all the retransmit list for stateless messages.

+    //

+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {

+

+      TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);

+      ASSERT(TxCb->TxPacket);

+

+      if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {

+        RemoveEntryList (&TxCb->Link);

+        FreePool (TxCb->TxPacket);

+        FreePool (TxCb);

+      }

+    }

+

+    //

+    // Clean up all the info-request messages list.

+    //

+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {

+

+      InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);

+

+      if (InfCb->TimeoutEvent != NULL) {

+        gBS->SignalEvent (InfCb->TimeoutEvent);

+      }

+      RemoveEntryList (&InfCb->Link);

+      FreePool (InfCb);

+    }

+  }

+}

+

+

+/**

+  Clean up the session of the instance stateful exchange.

+

+  @param[in, out]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]       Status          The return status from udp.

+

+**/

+VOID

+Dhcp6CleanupSession (

+  IN OUT DHCP6_INSTANCE          *Instance,

+  IN     EFI_STATUS              Status

+  )

+{

+  UINTN                          Index;

+  EFI_DHCP6_IA                   *Ia;

+

+  ASSERT(Instance->Config);

+  ASSERT(Instance->IaCb.Ia);

+

+  //

+  // Clean up the retransmit list for stateful messages.

+  //

+  Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATEFUL);

+

+  if (Instance->Unicast != NULL) {

+    FreePool (Instance->Unicast);

+  }

+

+  if (Instance->AdSelect != NULL) {

+    FreePool (Instance->AdSelect);

+  }

+

+  if (Instance->IaCb.Ia->ReplyPacket != NULL) {

+    FreePool (Instance->IaCb.Ia->ReplyPacket);

+  }

+

+  //

+  // Reinitialize the Ia fields of the instance.

+  //

+  Instance->UdpSts                  = Status;

+  Instance->AdSelect                = NULL;

+  Instance->AdPref                  = 0;

+  Instance->Unicast                 = NULL;

+  Instance->IaCb.T1                 = 0;

+  Instance->IaCb.T2                 = 0;

+  Instance->IaCb.AllExpireTime      = 0;

+  Instance->IaCb.LeaseTime          = 0;

+

+  //

+  // Clear start time

+  //

+  Instance->StartTime               = 0;

+

+  Ia                                = Instance->IaCb.Ia;

+  Ia->State                         = Dhcp6Init;

+  Ia->ReplyPacket                   = NULL;

+

+  //

+  // Set the addresses as zero lifetime, and then the notify

+  // function in Ip6Config will remove these timeout address.

+  //

+  for (Index = 0; Index < Ia->IaAddressCount; Index++) {

+    Ia->IaAddress[Index].PreferredLifetime = 0;

+    Ia->IaAddress[Index].ValidLifetime     = 0;

+  }

+

+  //

+  //

+  // Signal the Ia information updated event to informal user.

+  //

+  if (Instance->Config->IaInfoEvent != NULL) {

+    gBS->SignalEvent (Instance->Config->IaInfoEvent);

+  }

+}

+

+

+/**

+  Callback to user when Dhcp6 transmit/receive occurs.

+

+  @param[in]      Instance        The pointer to the Dhcp6 instance.

+  @param[in]      Event           The current Dhcp6 event.

+  @param[in, out] Packet          The pointer to the packet sending or received.

+

+  @retval EFI_SUCCESS           The user function returns success.

+  @retval EFI_NOT_READY         Direct the caller to continue collecting the offer.

+  @retval EFI_ABORTED           The user function ask it to abort.

+

+**/

+EFI_STATUS

+EFIAPI

+Dhcp6CallbackUser (

+  IN     DHCP6_INSTANCE       *Instance,

+  IN     EFI_DHCP6_EVENT      Event,

+  IN OUT EFI_DHCP6_PACKET     **Packet

+  )

+{

+  EFI_STATUS                  Status;

+  EFI_DHCP6_PACKET            *NewPacket;

+  EFI_DHCP6_CALLBACK          Callback;

+  VOID                        *Context;

+

+  ASSERT (Packet != NULL);

+  ASSERT (Instance->Config != NULL);

+  ASSERT (Instance->IaCb.Ia != NULL);

+

+  NewPacket = NULL;

+  Status    = EFI_SUCCESS;

+  Callback  = Instance->Config->Dhcp6Callback;

+  Context   = Instance->Config->CallbackContext;

+

+  //

+  // Callback to user with the new message if has.

+  //

+  if (Callback != NULL) {

+

+    Status = Callback (

+               &Instance->Dhcp6,

+               Context,

+               Instance->IaCb.Ia->State,

+               Event,

+               *Packet,

+               &NewPacket

+               );

+    //

+    // Updated the new packet from user to replace the original one.

+    //

+    if (NewPacket != NULL) {

+      ASSERT (*Packet != NULL);

+      FreePool (*Packet);

+      *Packet = NewPacket;

+    }

+  }

+

+  return Status;

+}

+

+

+/**

+  Update Ia according to the new reply message.

+

+  @param[in, out]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]       Packet          The pointer to reply messages.

+

+  @retval EFI_SUCCESS         Updated the Ia information successfully.

+  @retval EFI_DEVICE_ERROR    An unexpected error.

+

+**/

+EFI_STATUS

+Dhcp6UpdateIaInfo (

+  IN OUT DHCP6_INSTANCE           *Instance,

+  IN     EFI_DHCP6_PACKET         *Packet

+  )

+{

+  EFI_STATUS                  Status;

+  EFI_DHCP6_STATE             State;

+  UINT8                       *Option;

+  UINT8                       *IaInnerOpt;

+  UINT16                      IaInnerLen;

+  UINT16                      StsCode;

+  UINT32                      T1;

+  UINT32                      T2;

+

+  ASSERT (Instance->Config != NULL);

+  //

+  // If the reply was received in reponse to a solicit with rapid commit option,

+  // request, renew or rebind message, the client updates the information it has

+  // recorded about IAs from the IA options contained in the reply message:

+  //   1. record the T1 and T2 times

+  //   2. add any new addresses in the IA

+  //   3. discard any addresses from the IA, that have a valid lifetime of 0

+  //   4. update lifetimes for any addresses that alread recorded

+  //   5. leave unchanged any information about addresses

+  //

+  // See details in the section-18.1.8 of rfc-3315.

+  //

+  State  = Dhcp6Init;

+  Option = Dhcp6SeekIaOption (

+             Packet->Dhcp6.Option,

+             Packet->Length - sizeof (EFI_DHCP6_HEADER),

+             &Instance->Config->IaDescriptor

+             );

+  if (Option == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // The format of the IA_NA option is:

+  //

+  //     0                   1                   2                   3

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |          OPTION_IA_NA         |          option-len           |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                        IAID (4 octets)                        |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                              T1                               |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                              T2                               |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                                                               |

+  //    .                         IA_NA-options                         .

+  //    .                                                               .

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+  // The format of the IA_TA option is:

+  //

+  //     0                   1                   2                   3

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |         OPTION_IA_TA          |          option-len           |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                        IAID (4 octets)                        |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                                                               |

+  //    .                         IA_TA-options                         .

+  //    .                                                               .

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  //

+  // sizeof (option-code + option-len + IaId)           = 8

+  // sizeof (option-code + option-len + IaId + T1)      = 12

+  // sizeof (option-code + option-len + IaId + T1 + T2) = 16

+  //

+  // The inner options still start with 2 bytes option-code and 2 bytes option-len.

+  //

+  if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {

+    T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8)));

+    T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12)));

+    IaInnerOpt = Option + 16;

+    IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12);

+  } else {

+    T1 = 0;

+    T2 = 0;

+    IaInnerOpt = Option + 8;

+    IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 4);

+  }

+

+  //

+  // The format of the Status Code option is:

+  //

+  //   0                   1                   2                   3

+  //   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

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //  |       OPTION_STATUS_CODE      |         option-len            |

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //  |          status-code          |                               |

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |

+  //  .                                                               .

+  //  .                        status-message                         .

+  //  .                                                               .

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  //

+  // sizeof (option-code + option-len) = 4

+  //

+  StsCode = Dhcp6StsSuccess;

+  Option  = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);

+

+  if (Option != NULL) {

+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));

+    if (StsCode != Dhcp6StsSuccess) {

+      return EFI_DEVICE_ERROR;

+    }

+  }

+

+  //

+  // Generate control block for the Ia.

+  //

+  Status = Dhcp6GenerateIaCb (

+             Instance,

+             IaInnerOpt,

+             IaInnerLen,

+             T1,

+             T2

+             );

+

+  return Status;

+}

+

+

+

+/**

+  Seek StatusCode Option in package. A Status Code option may appear in the

+  options field of a DHCP message and/or in the options field of another option.

+  See details in section 22.13, RFC3315.

+

+  @param[in]       Instance        The pointer to the Dhcp6 instance.

+  @param[in]       Packet          The pointer to reply messages.

+  @param[out]      Option          The pointer to status code option.

+

+  @retval EFI_SUCCESS              Seek status code option successfully.

+  @retval EFI_DEVICE_ERROR         An unexpected error.

+

+**/

+EFI_STATUS

+Dhcp6SeekStsOption (

+  IN     DHCP6_INSTANCE           *Instance,

+  IN     EFI_DHCP6_PACKET         *Packet,

+  OUT    UINT8                    **Option

+  )

+{

+  UINT8                       *IaInnerOpt;

+  UINT16                      IaInnerLen;

+  UINT16                      StsCode;

+

+  //

+  // Seek StatusCode option directly in DHCP message body. That is, search in

+  // non-encapsulated option fields.

+  //

+  *Option = Dhcp6SeekOption (

+              Packet->Dhcp6.Option,

+              Packet->Length - 4,

+              Dhcp6OptStatusCode

+              );

+

+  if (*Option != NULL) {

+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));

+    if (StsCode != Dhcp6StsSuccess) {

+      return EFI_DEVICE_ERROR;

+    }

+  }

+

+  //

+  // Seek in encapsulated options, IA_NA and IA_TA.

+  //

+  *Option = Dhcp6SeekIaOption (

+              Packet->Dhcp6.Option,

+              Packet->Length - sizeof (EFI_DHCP6_HEADER),

+              &Instance->Config->IaDescriptor

+              );

+  if (*Option == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // The format of the IA_NA option is:

+  //

+  //     0                   1                   2                   3

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |          OPTION_IA_NA         |          option-len           |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                        IAID (4 octets)                        |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                              T1                               |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                              T2                               |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                                                               |

+  //    .                         IA_NA-options                         .

+  //    .                                                               .

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+  // The format of the IA_TA option is:

+  //

+  //     0                   1                   2                   3

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |         OPTION_IA_TA          |          option-len           |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                        IAID (4 octets)                        |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                                                               |

+  //    .                         IA_TA-options                         .

+  //    .                                                               .

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  //

+  // sizeof (option-code + option-len + IaId)           = 8

+  // sizeof (option-code + option-len + IaId + T1)      = 12

+  // sizeof (option-code + option-len + IaId + T1 + T2) = 16

+  //

+  // The inner options still start with 2 bytes option-code and 2 bytes option-len.

+  //

+  if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {

+    IaInnerOpt = *Option + 16;

+    IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 12);

+  } else {

+    IaInnerOpt = *Option + 8;

+    IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 4);

+  }

+

+  //

+  // The format of the Status Code option is:

+  //

+  //   0                   1                   2                   3

+  //   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

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //  |       OPTION_STATUS_CODE      |         option-len            |

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //  |          status-code          |                               |

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |

+  //  .                                                               .

+  //  .                        status-message                         .

+  //  .                                                               .

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  //

+  // sizeof (option-code + option-len) = 4

+  //

+  *Option  = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);

+  if (*Option != NULL) {

+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));

+    if (StsCode != Dhcp6StsSuccess) {

+      return EFI_DEVICE_ERROR;

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Transmit Dhcp6 message by udpio.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  Packet          The pointer to transmit message.

+  @param[in]  Elapsed         The pointer to the elapsed time value to fill in.

+

+  @retval EFI_SUCCESS           Successfully transmitted the packet.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval Others                Failed to transmit the packet.

+

+**/

+EFI_STATUS

+Dhcp6TransmitPacket (

+  IN DHCP6_INSTANCE         *Instance,

+  IN EFI_DHCP6_PACKET       *Packet,

+  IN UINT16                 *Elapsed

+  )

+{

+  EFI_STATUS                Status;

+  NET_BUF                   *Wrap;

+  NET_FRAGMENT              Frag;

+  UDP_END_POINT             EndPt;

+  DHCP6_SERVICE             *Service;

+

+  Service = Instance->Service;

+

+  //

+  // Wrap it into a netbuf then send it.

+  //

+  Frag.Bulk = (UINT8 *) &Packet->Dhcp6.Header;

+  Frag.Len  = Packet->Length;

+

+  //

+  // Do not register free packet here, which will be handled in retry list.

+  //

+  Wrap = NetbufFromExt (&Frag, 1, 0, 0, Dhcp6DummyExtFree, NULL);

+

+  if (Wrap == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Multicast the Dhcp6 message, unless get the unicast server address by option.

+  //

+  ZeroMem (&EndPt, sizeof (UDP_END_POINT));

+

+  if (Instance->Unicast != NULL) {

+    CopyMem (

+      &EndPt.RemoteAddr,

+      Instance->Unicast,

+      sizeof (EFI_IPv6_ADDRESS)

+      );

+  } else {

+    CopyMem (

+      &EndPt.RemoteAddr,

+      &mAllDhcpRelayAndServersAddress,

+      sizeof (EFI_IPv6_ADDRESS)

+      );

+  }

+

+  EndPt.RemotePort = DHCP6_PORT_SERVER;

+  EndPt.LocalPort  = DHCP6_PORT_CLIENT;

+

+  //

+  // Update the elapsed time value.

+  //

+  if (Elapsed != NULL) {

+    SetElapsedTime (Elapsed, Instance);

+  }

+

+  //

+  // Send out the message by the configured Udp6Io.

+  //

+  Status = UdpIoSendDatagram (

+             Service->UdpIo,

+             Wrap,

+             &EndPt,

+             NULL,

+             Dhcp6OnTransmitted,

+             NULL

+             );

+

+  if (EFI_ERROR (Status)) {

+    NetbufFree (Wrap);

+    return Status;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Create the solicit message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+

+  @retval EFI_SUCCESS           Created and sent the solicit message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval Others                Failed to send the solicit message.

+

+**/

+EFI_STATUS

+Dhcp6SendSolicitMsg   (

+  IN DHCP6_INSTANCE            *Instance

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_DHCP6_PACKET             *Packet;

+  EFI_DHCP6_PACKET_OPTION      *UserOpt;

+  EFI_DHCP6_DUID               *ClientId;

+  DHCP6_SERVICE                *Service;

+  UINT8                        *Cursor;

+  UINT16                       *Elapsed;

+  UINT32                       UserLen;

+  UINTN                        Index;

+  UINT16                       Length;

+

+  Service  = Instance->Service;

+  ClientId = Service->ClientId;

+  UserLen  = 0;

+

+  ASSERT (Service->ClientId != NULL);

+  ASSERT (Instance->Config != NULL);

+  ASSERT (Instance->IaCb.Ia != NULL);

+

+  //

+  // Calculate the added length of customized option list.

+  //

+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {

+    UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);

+  }

+

+  //

+  // Create the Dhcp6 packet and initialize commone fields.

+  //

+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;

+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);

+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgSolicit;

+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;

+

+  //

+  // Assembly Dhcp6 options for solicit message.

+  //

+  Cursor = Packet->Dhcp6.Option;

+

+  Length = HTONS (ClientId->Length);

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             HTONS (Dhcp6OptClientId),

+             Length,

+             ClientId->Duid

+             );

+

+  Cursor = Dhcp6AppendETOption (

+             Cursor,

+             Instance,

+             &Elapsed

+             );

+

+  Cursor = Dhcp6AppendIaOption (

+             Cursor,

+             Instance->IaCb.Ia,

+             Instance->IaCb.T1,

+             Instance->IaCb.T2

+             );

+

+  //

+  // Append user-defined when configurate Dhcp6 service.

+  //

+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {

+

+    UserOpt = Instance->Config->OptionList[Index];

+    Cursor  = Dhcp6AppendOption(

+                Cursor,

+                UserOpt->OpCode,

+                UserOpt->OpLen,

+                UserOpt->Data

+                );

+  }

+

+  //

+  // Determine the size/length of packet.

+  //

+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);

+  ASSERT (Packet->Size > Packet->Length + 8);

+

+  //

+  // Callback to user with the packet to be sent and check the user's feedback.

+  //

+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendSolicit, &Packet);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Send solicit packet with the state transition from Dhcp6init to

+  // Dhcp6selecting.

+  //

+  Instance->IaCb.Ia->State = Dhcp6Selecting;

+

+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Enqueue the sent packet for the retransmission in case reply timeout.

+  //

+  return Dhcp6EnqueueRetry (

+           Instance,

+           Packet,

+           Elapsed,

+           Instance->Config->SolicitRetransmission

+           );

+}

+

+/**

+  Configure some parameter to initiate SolicitMsg.

+

+  @param[in]  Instance          The pointer to the Dhcp6 instance.

+

+  @retval EFI_SUCCESS           Created and sent the solicit message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval Others                Failed to send the solicit message.

+

+**/

+EFI_STATUS

+Dhcp6InitSolicitMsg   (

+  IN DHCP6_INSTANCE            *Instance

+  )

+{

+  Instance->IaCb.T1 = 0;

+  Instance->IaCb.T2 = 0;

+  Instance->IaCb.Ia->IaAddressCount = 0;

+

+  return Dhcp6SendSolicitMsg (Instance);

+}

+

+

+/**

+  Create the request message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+

+  @retval EFI_SUCCESS           Created and sent the request message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to send the request message.

+

+**/

+EFI_STATUS

+Dhcp6SendRequestMsg (

+  IN DHCP6_INSTANCE            *Instance

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_DHCP6_PACKET             *Packet;

+  EFI_DHCP6_PACKET_OPTION      *UserOpt;

+  EFI_DHCP6_DUID               *ClientId;

+  EFI_DHCP6_DUID               *ServerId;

+  DHCP6_SERVICE                *Service;

+  UINT8                        *Option;

+  UINT8                        *Cursor;

+  UINT16                       *Elapsed;

+  UINT32                       UserLen;

+  UINTN                        Index;

+  UINT16                       Length;

+

+  ASSERT(Instance->AdSelect != NULL);

+  ASSERT(Instance->Config != NULL);

+  ASSERT(Instance->IaCb.Ia != NULL);

+  ASSERT(Instance->Service != NULL);

+

+  Service  = Instance->Service;

+  ClientId = Service->ClientId;

+

+  ASSERT(ClientId != NULL);

+

+  //

+  // Get the server Id from the selected advertisement message.

+  //

+  Option = Dhcp6SeekOption (

+             Instance->AdSelect->Dhcp6.Option,

+             Instance->AdSelect->Length - 4,

+             Dhcp6OptServerId

+             );

+  if (Option == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  ServerId = (EFI_DHCP6_DUID *) (Option + 2);

+

+  //

+  // Calculate the added length of customized option list.

+  //

+  UserLen = 0;

+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {

+    UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);

+  }

+

+  //

+  // Create the Dhcp6 packet and initialize commone fields.

+  //

+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;

+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);

+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgRequest;

+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;

+

+  //

+  // Assembly Dhcp6 options for request message.

+  //

+  Cursor = Packet->Dhcp6.Option;

+

+  Length = HTONS (ClientId->Length);

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             HTONS (Dhcp6OptClientId),

+             Length,

+             ClientId->Duid

+             );

+

+  Cursor = Dhcp6AppendETOption (

+             Cursor,

+             Instance,

+             &Elapsed

+             );

+

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             HTONS (Dhcp6OptServerId),

+             ServerId->Length,

+             ServerId->Duid

+             );

+

+  Cursor = Dhcp6AppendIaOption (

+             Cursor,

+             Instance->IaCb.Ia,

+             Instance->IaCb.T1,

+             Instance->IaCb.T2

+             );

+

+  //

+  // Append user-defined when configurate Dhcp6 service.

+  //

+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {

+

+    UserOpt = Instance->Config->OptionList[Index];

+    Cursor  = Dhcp6AppendOption(

+                Cursor,

+                UserOpt->OpCode,

+                UserOpt->OpLen,

+                UserOpt->Data

+                );

+  }

+

+  //

+  // Determine the size/length of packet.

+  //

+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);

+  ASSERT (Packet->Size > Packet->Length + 8);

+

+  //

+  // Callback to user with the packet to be sent and check the user's feedback.

+  //

+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendRequest, &Packet);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Send request packet with the state transition from Dhcp6selecting to

+  // Dhcp6requesting.

+  //

+  Instance->IaCb.Ia->State = Dhcp6Requesting;

+

+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Enqueue the sent packet for the retransmission in case reply timeout.

+  //

+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);

+}

+

+

+/**

+  Create the decline message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  DecIa           The pointer to the decline Ia.

+

+  @retval EFI_SUCCESS           Created and sent the decline message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to send the decline message.

+

+**/

+EFI_STATUS

+Dhcp6SendDeclineMsg (

+  IN DHCP6_INSTANCE            *Instance,

+  IN EFI_DHCP6_IA              *DecIa

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_DHCP6_PACKET             *Packet;

+  EFI_DHCP6_PACKET             *LastReply;

+  EFI_DHCP6_DUID               *ClientId;

+  EFI_DHCP6_DUID               *ServerId;

+  DHCP6_SERVICE                *Service;

+  UINT8                        *Option;

+  UINT8                        *Cursor;

+  UINT16                       *Elapsed;

+  UINT16                       Length;

+

+  ASSERT (Instance->Config != NULL);

+  ASSERT (Instance->IaCb.Ia != NULL);

+  ASSERT (Instance->Service != NULL);

+

+  Service   = Instance->Service;

+  ClientId  = Service->ClientId;

+  LastReply = Instance->IaCb.Ia->ReplyPacket;

+

+  ASSERT (ClientId != NULL);

+  ASSERT (LastReply != NULL);

+

+  //

+  // Get the server Id from the last reply message.

+  //

+  Option = Dhcp6SeekOption (

+             LastReply->Dhcp6.Option,

+             LastReply->Length - 4,

+             Dhcp6OptServerId

+             );

+  if (Option == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // EFI_DHCP6_DUID contains a length field of 2 bytes.

+  //

+  ServerId = (EFI_DHCP6_DUID *) (Option + 2);

+

+  //

+  // Create the Dhcp6 packet and initialize commone fields.

+  //

+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE;

+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);

+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgDecline;

+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;

+

+  //

+  // Assembly Dhcp6 options for rebind/renew message.

+  //

+  Cursor = Packet->Dhcp6.Option;

+

+  Length = HTONS (ClientId->Length);

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             HTONS (Dhcp6OptClientId),

+             Length,

+             ClientId->Duid

+             );

+

+  Cursor = Dhcp6AppendETOption (

+             Cursor,

+             Instance,

+             &Elapsed

+             );

+

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             HTONS (Dhcp6OptServerId),

+             ServerId->Length,

+             ServerId->Duid

+             );

+

+  Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0);

+

+  //

+  // Determine the size/length of packet.

+  //

+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);

+  ASSERT (Packet->Size > Packet->Length + 8);

+

+  //

+  // Callback to user with the packet to be sent and check the user's feedback.

+  //

+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendDecline, &Packet);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Send decline packet with the state transition from Dhcp6bound to

+  // Dhcp6declining.

+  //

+  Instance->IaCb.Ia->State = Dhcp6Declining;

+

+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Enqueue the sent packet for the retransmission in case reply timeout.

+  //

+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);

+}

+

+

+/**

+  Create the release message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  RelIa           The pointer to the release Ia.

+

+  @retval EFI_SUCCESS           Created and sent the release message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to send the release message.

+

+**/

+EFI_STATUS

+Dhcp6SendReleaseMsg (

+  IN DHCP6_INSTANCE            *Instance,

+  IN EFI_DHCP6_IA              *RelIa

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_DHCP6_PACKET             *Packet;

+  EFI_DHCP6_PACKET             *LastReply;

+  EFI_DHCP6_DUID               *ClientId;

+  EFI_DHCP6_DUID               *ServerId;

+  DHCP6_SERVICE                *Service;

+  UINT8                        *Option;

+  UINT8                        *Cursor;

+  UINT16                       *Elapsed;

+  UINT16                       Length;

+

+  ASSERT(Instance->Config);

+  ASSERT(Instance->IaCb.Ia);

+

+  Service   = Instance->Service;

+  ClientId  = Service->ClientId;

+  LastReply = Instance->IaCb.Ia->ReplyPacket;

+

+  ASSERT(ClientId);

+  ASSERT(LastReply);

+

+  //

+  // Get the server Id from the last reply message.

+  //

+  Option = Dhcp6SeekOption (

+             LastReply->Dhcp6.Option,

+             LastReply->Length - 4,

+             Dhcp6OptServerId

+             );

+  if (Option == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  ServerId = (EFI_DHCP6_DUID *) (Option + 2);

+

+  //

+  // Create the Dhcp6 packet and initialize commone fields.

+  //

+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE;

+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);

+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgRelease;

+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;

+

+  //

+  // Assembly Dhcp6 options for rebind/renew message

+  //

+  Cursor = Packet->Dhcp6.Option;

+

+  Length = HTONS (ClientId->Length);

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             HTONS (Dhcp6OptClientId),

+             Length,

+             ClientId->Duid

+             );

+

+  //

+  // ServerId is extracted from packet, it's network order.

+  //

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             HTONS (Dhcp6OptServerId),

+             ServerId->Length,

+             ServerId->Duid

+             );

+

+  Cursor = Dhcp6AppendETOption (

+             Cursor,

+             Instance,

+             &Elapsed

+             );

+

+  Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0);

+

+  //

+  // Determine the size/length of packet

+  //

+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);

+  ASSERT (Packet->Size > Packet->Length + 8);

+

+  //

+  // Callback to user with the packet to be sent and check the user's feedback.

+  //

+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendRelease, &Packet);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Send release packet with the state transition from Dhcp6bound to

+  // Dhcp6releasing.

+  //

+  Instance->IaCb.Ia->State = Dhcp6Releasing;

+

+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Enqueue the sent packet for the retransmission in case reply timeout.

+  //

+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);

+}

+

+

+/**

+  Create the renew/rebind message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  RebindRequest   If TRUE, it is a Rebind type message.

+                              Otherwise, it is a Renew type message.

+

+  @retval EFI_SUCCESS           Created and sent the renew/rebind message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to send the renew/rebind message.

+

+**/

+EFI_STATUS

+Dhcp6SendRenewRebindMsg (

+  IN DHCP6_INSTANCE         *Instance,

+  IN BOOLEAN                RebindRequest

+  )

+{

+  EFI_STATUS                Status;

+  EFI_DHCP6_PACKET          *Packet;

+  EFI_DHCP6_PACKET          *LastReply;

+  EFI_DHCP6_PACKET_OPTION   *UserOpt;

+  EFI_DHCP6_DUID            *ClientId;

+  EFI_DHCP6_DUID            *ServerId;

+  EFI_DHCP6_STATE           State;

+  EFI_DHCP6_EVENT           Event;

+  DHCP6_SERVICE             *Service;

+  UINT8                     *Option;

+  UINT8                     *Cursor;

+  UINT16                    *Elapsed;

+  UINT32                    UserLen;

+  UINTN                     Index;

+  UINT16                    Length;

+

+  ASSERT(Instance->Config);

+  ASSERT(Instance->IaCb.Ia);

+

+  Service   = Instance->Service;

+  ClientId  = Service->ClientId;

+

+  ASSERT(ClientId);

+

+  //

+  // Calculate the added length of customized option list.

+  //

+  UserLen = 0;

+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {

+    UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);

+  }

+

+  //

+  // Create the Dhcp6 packet and initialize commone fields.

+  //

+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;

+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);

+  Packet->Dhcp6.Header.MessageType   = RebindRequest ? Dhcp6MsgRebind : Dhcp6MsgRenew;

+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;

+

+  //

+  // Assembly Dhcp6 options for rebind/renew message.

+  //

+  Cursor = Packet->Dhcp6.Option;

+

+  Length = HTONS (ClientId->Length);

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             HTONS (Dhcp6OptClientId),

+             Length,

+             ClientId->Duid

+             );

+

+  Cursor = Dhcp6AppendETOption (

+             Cursor,

+             Instance,

+             &Elapsed

+             );

+

+  Cursor = Dhcp6AppendIaOption (

+             Cursor,

+             Instance->IaCb.Ia,

+             Instance->IaCb.T1,

+             Instance->IaCb.T2

+             );

+

+  if (!RebindRequest) {

+    //

+    // Get the server Id from the last reply message and

+    // insert it for rebind request.

+    //

+    LastReply = Instance->IaCb.Ia->ReplyPacket;

+    ASSERT (LastReply);

+

+    Option = Dhcp6SeekOption (

+               LastReply->Dhcp6.Option,

+               LastReply->Length - 4,

+               Dhcp6OptServerId

+               );

+    if (Option == NULL) {

+      FreePool (Packet);

+      return EFI_DEVICE_ERROR;

+    }

+

+    ServerId = (EFI_DHCP6_DUID *) (Option + 2);

+

+    Cursor = Dhcp6AppendOption (

+               Cursor,

+               HTONS (Dhcp6OptServerId),

+               ServerId->Length,

+               ServerId->Duid

+               );

+  }

+

+  //

+  // Append user-defined when configurate Dhcp6 service.

+  //

+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {

+

+    UserOpt = Instance->Config->OptionList[Index];

+    Cursor  = Dhcp6AppendOption(

+                Cursor,

+                UserOpt->OpCode,

+                UserOpt->OpLen,

+                UserOpt->Data

+                );

+  }

+

+  //

+  // Determine the size/length of packet.

+  //

+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);

+  ASSERT (Packet->Size > Packet->Length + 8);

+

+  //

+  // Callback to user with the packet to be sent and check the user's feedback.

+  //

+  State  = (RebindRequest) ? Dhcp6Rebinding : Dhcp6Renewing;

+  Event  = (RebindRequest) ? Dhcp6EnterRebinding : Dhcp6EnterRenewing;

+

+  Status = Dhcp6CallbackUser (Instance, Event, &Packet);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Send renew/rebind packet with the state transition from Dhcp6bound to

+  // Dhcp6renew/rebind.

+  // And sync the lease time when send renew/rebind, in case that user send

+  // renew/rebind actively.

+  //

+  Instance->IaCb.Ia->State = State;

+  Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1;

+

+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Enqueue the sent packet for the retransmission in case reply timeout.

+  //

+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);

+}

+

+

+/**

+  Create the information request message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  InfCb           The pointer to the information request control block.

+  @param[in]  SendClientId    If TRUE, the client identifier option will be included in

+                              information request message. Otherwise, the client identifier

+                              option will not be included.

+  @param[in]  OptionRequest   The pointer to the option request option.

+  @param[in]  OptionCount     The number options in the OptionList.

+  @param[in]  OptionList      The array pointers to the appended options.

+  @param[in]  Retransmission  The pointer to the retransmission control.

+

+  @retval EFI_SUCCESS           Created and sent the info-request message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval Others                Failed to send the info-request message.

+

+**/

+EFI_STATUS

+Dhcp6SendInfoRequestMsg (

+  IN DHCP6_INSTANCE            *Instance,

+  IN DHCP6_INF_CB              *InfCb,

+  IN BOOLEAN                   SendClientId,

+  IN EFI_DHCP6_PACKET_OPTION   *OptionRequest,

+  IN UINT32                    OptionCount,

+  IN EFI_DHCP6_PACKET_OPTION   *OptionList[],

+  IN EFI_DHCP6_RETRANSMISSION  *Retransmission

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_DHCP6_PACKET             *Packet;

+  EFI_DHCP6_PACKET_OPTION      *UserOpt;

+  EFI_DHCP6_DUID               *ClientId;

+  DHCP6_SERVICE                *Service;

+  UINT8                        *Cursor;

+  UINT16                       *Elapsed;

+  UINT32                       UserLen;

+  UINTN                        Index;

+  UINT16                       Length;

+

+  ASSERT(OptionRequest);

+

+  Service  = Instance->Service;

+  ClientId = Service->ClientId;

+  UserLen  = NTOHS (OptionRequest->OpLen) + 4;

+

+  ASSERT(ClientId);

+

+  //

+  // Calculate the added length of customized option list.

+  //

+  for (Index = 0; Index < OptionCount; Index++) {

+    UserLen += (NTOHS (OptionList[Index]->OpLen) + 4);

+  }

+

+  //

+  // Create the Dhcp6 packet and initialize commone fields.

+  //

+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;

+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);

+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgInfoRequest;

+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;

+

+  InfCb->Xid                         = Packet->Dhcp6.Header.TransactionId;

+

+  //

+  // Assembly Dhcp6 options for info-request message.

+  //

+  Cursor = Packet->Dhcp6.Option;

+

+  if (SendClientId) {

+    Length = HTONS (ClientId->Length);

+    Cursor = Dhcp6AppendOption (

+               Cursor,

+               HTONS (Dhcp6OptClientId),

+               Length,

+               ClientId->Duid

+               );

+  }

+

+  Cursor = Dhcp6AppendETOption (

+             Cursor,

+             Instance,

+             &Elapsed

+             );

+

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             OptionRequest->OpCode,

+             OptionRequest->OpLen,

+             OptionRequest->Data

+             );

+

+  //

+  // Append user-defined when configurate Dhcp6 service.

+  //

+  for (Index = 0; Index < OptionCount; Index++) {

+

+    UserOpt = OptionList[Index];

+    Cursor  = Dhcp6AppendOption(

+                Cursor,

+                UserOpt->OpCode,

+                UserOpt->OpLen,

+                UserOpt->Data

+                );

+  }

+

+  //

+  // Determine the size/length of packet.

+  //

+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);

+  ASSERT (Packet->Size > Packet->Length + 8);

+

+  //

+  // Send info-request packet with no state.

+  //

+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Enqueue the sent packet for the retransmission in case reply timeout.

+  //

+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, Retransmission);

+}

+

+

+/**

+  Create the Confirm message and send it.

+

+  @param[in]  Instance          The pointer to the Dhcp6 instance.

+

+  @retval EFI_SUCCESS           Created and sent the confirm message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to send the confirm message.

+

+**/

+EFI_STATUS

+Dhcp6SendConfirmMsg (

+  IN DHCP6_INSTANCE            *Instance

+  )

+{

+  UINT8                        *Cursor;

+  UINTN                        Index;

+  UINT16                       Length;

+  UINT32                       UserLen;

+  EFI_STATUS                   Status;

+  DHCP6_SERVICE                *Service;

+  EFI_DHCP6_DUID               *ClientId;

+  EFI_DHCP6_PACKET             *Packet;

+  EFI_DHCP6_PACKET_OPTION      *UserOpt;

+  UINT16                       *Elapsed;

+

+  ASSERT (Instance->Config != NULL);

+  ASSERT (Instance->IaCb.Ia != NULL);

+  ASSERT (Instance->Service != NULL);

+

+  Service  = Instance->Service;

+  ClientId = Service->ClientId;

+  ASSERT (ClientId != NULL);

+

+  //

+  // Calculate the added length of customized option list.

+  //

+  UserLen = 0;

+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {

+    UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);

+  }

+

+  //

+  // Create the Dhcp6 packet and initialize common fields.

+  //

+  Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Packet->Size                       = DHCP6_BASE_PACKET_SIZE + UserLen;

+  Packet->Length                     = sizeof (EFI_DHCP6_HEADER);

+  Packet->Dhcp6.Header.MessageType   = Dhcp6MsgConfirm;

+  Packet->Dhcp6.Header.TransactionId = Service->Xid++;

+

+  //

+  // Assembly Dhcp6 options for solicit message.

+  //

+  Cursor = Packet->Dhcp6.Option;

+

+  Length = HTONS (ClientId->Length);

+  Cursor = Dhcp6AppendOption (

+             Cursor,

+             HTONS (Dhcp6OptClientId),

+             Length,

+             ClientId->Duid

+             );

+

+  Cursor = Dhcp6AppendETOption (

+             Cursor,

+             Instance,

+             &Elapsed

+             );

+

+  Cursor = Dhcp6AppendIaOption (

+             Cursor,

+             Instance->IaCb.Ia,

+             Instance->IaCb.T1,

+             Instance->IaCb.T2

+             );

+

+  //

+  // Append user-defined when configurate Dhcp6 service.

+  //

+  for (Index = 0; Index < Instance->Config->OptionCount; Index++) {

+    UserOpt = Instance->Config->OptionList[Index];

+    Cursor  = Dhcp6AppendOption (

+                Cursor,

+                UserOpt->OpCode,

+                UserOpt->OpLen,

+                UserOpt->Data

+                );

+  }

+

+  //

+  // Determine the size/length of packet.

+  //

+  Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);

+  ASSERT (Packet->Size > Packet->Length + 8);

+

+  //

+  // Callback to user with the packet to be sent and check the user's feedback.

+  //

+  Status = Dhcp6CallbackUser (Instance, Dhcp6SendConfirm, &Packet);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Send confirm packet with the state transition from Dhcp6Bound to

+  // Dhcp6Confirming.

+  //

+  Instance->IaCb.Ia->State = Dhcp6Confirming;

+

+  Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Packet);

+    return Status;

+  }

+

+  //

+  // Enqueue the sent packet for the retransmission in case reply timeout.

+  //

+  return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);

+}

+

+

+

+/**

+  Handle with the Dhcp6 reply message.

+

+  @param[in]  Instance        The pointer to Dhcp6 instance.

+  @param[in]  Packet          The pointer to the Dhcp6 reply message.

+

+  @retval EFI_SUCCESS           Processed the reply message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to process the reply message.

+

+**/

+EFI_STATUS

+Dhcp6HandleReplyMsg (

+  IN DHCP6_INSTANCE           *Instance,

+  IN EFI_DHCP6_PACKET         *Packet

+  )

+{

+  EFI_STATUS                  Status;

+  UINT8                       *Option;

+  UINT16                      StsCode;

+

+  ASSERT (Instance->Config != NULL);

+  ASSERT (Instance->IaCb.Ia != NULL);

+  ASSERT (Packet != NULL);

+

+  if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // If the client subsequently receives a valid reply message that includes a

+  // rapid commit option since send a solicit with rapid commit option before,

+  // preocess the reply message and discard any reply messages received in

+  // response to the request message.

+  // See details in the section-17.1.4 of rfc-3315.

+  //

+  Option = Dhcp6SeekOption (

+             Packet->Dhcp6.Option,

+             Packet->Length - 4,

+             Dhcp6OptRapidCommit

+             );

+

+  if ((Option != NULL && !Instance->Config->RapidCommit) || (Option == NULL && Instance->Config->RapidCommit)) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // As to a valid reply packet in response to a request/renew/rebind packet,

+  // ignore the packet if not contains the Ia option

+  //

+  if (Instance->IaCb.Ia->State == Dhcp6Requesting ||

+      Instance->IaCb.Ia->State == Dhcp6Renewing ||

+      Instance->IaCb.Ia->State == Dhcp6Rebinding

+      ) {

+

+    Option = Dhcp6SeekIaOption (

+               Packet->Dhcp6.Option,

+               Packet->Length,

+               &Instance->Config->IaDescriptor

+               );

+    if (Option == NULL) {

+      return EFI_DEVICE_ERROR;

+    }

+  }

+

+  //

+  // Callback to user with the received packet and check the user's feedback.

+  //

+  Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdReply, &Packet);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Dequeue the sent packet from retransmit list since reply received.

+  //

+  Status = Dhcp6DequeueRetry (

+             Instance,

+             Packet->Dhcp6.Header.TransactionId,

+             FALSE

+             );

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // When receive a valid reply packet in response to a decline/release packet,

+  // the client considers the decline/release event completed regardless of the

+  // status code.

+  //

+  if (Instance->IaCb.Ia->State == Dhcp6Declining || Instance->IaCb.Ia->State == Dhcp6Releasing) {

+

+    if (Instance->IaCb.Ia->IaAddressCount != 0) {

+      Instance->IaCb.Ia->State       = Dhcp6Bound;

+    } else {

+      ASSERT (Instance->IaCb.Ia->ReplyPacket);

+      FreePool (Instance->IaCb.Ia->ReplyPacket);

+      Instance->IaCb.Ia->ReplyPacket = NULL;

+      Instance->IaCb.Ia->State       = Dhcp6Init;

+    }

+

+    //

+    // For sync, set the success flag out of polling in decline/release.

+    //

+    Instance->UdpSts = EFI_SUCCESS;

+

+    //

+    // For async, signal the Ia event to inform Ia infomation update.

+    //

+    if (Instance->Config->IaInfoEvent != NULL) {

+      gBS->SignalEvent (Instance->Config->IaInfoEvent);

+    }

+

+    //

+    // Reset start time for next exchange.

+    //

+    Instance->StartTime       = 0;

+

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Upon the receipt of a valid reply packet in response to a solicit, request,

+  // confirm, renew and rebind, the behavior depends on the status code option.

+  // See the details in the section-18.1.8 of rfc-3315.

+  //

+  Option = NULL;

+  Status = Dhcp6SeekStsOption (

+             Instance,

+             Packet,

+             &Option

+             );

+

+  if (!EFI_ERROR (Status)) {

+    //

+    // Reset start time for next exchange.

+    //

+    Instance->StartTime       = 0;

+

+    //

+    // No status code or no error status code means succeed to reply.

+    //

+    Status = Dhcp6UpdateIaInfo (Instance, Packet);

+

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    //

+    // Set bound state and store the reply packet.

+    //

+    if (Instance->IaCb.Ia->ReplyPacket != NULL) {

+      FreePool (Instance->IaCb.Ia->ReplyPacket);

+    }

+

+    Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size);

+

+    if (Instance->IaCb.Ia->ReplyPacket == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size);

+

+    Instance->IaCb.Ia->State = Dhcp6Bound;

+

+    //

+    // For sync, set the success flag out of polling in start/renewrebind.

+    //

+    Instance->UdpSts         = EFI_SUCCESS;

+

+    //

+    // Maybe this is a new round DHCP process due to some reason, such as NotOnLink

+    // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that

+    // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP

+    // consumers can be notified to flush old address.

+    //

+    Dhcp6AppendCacheIa (Instance);

+

+    //

+    // For async, signal the Ia event to inform Ia infomation update.

+    //

+    if (Instance->Config->IaInfoEvent != NULL) {

+      gBS->SignalEvent (Instance->Config->IaInfoEvent);

+    }

+  } else if (Option != NULL) {

+    //

+    // Any error status code option is found.

+    //

+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));

+    switch (StsCode) {

+    case Dhcp6StsUnspecFail:

+      //

+      // It indicates the server is unable to process the message due to an

+      // unspecified failure condition, so just retry if possible.

+      //

+      break;

+

+    case Dhcp6StsUseMulticast:

+      //

+      // It indicates the server receives a message via unicast from a client

+      // to which the server has not sent a unicast option, so retry it by

+      // multi-cast address.

+      //

+      if (Instance->Unicast != NULL) {

+        FreePool (Instance->Unicast);

+        Instance->Unicast = NULL;

+      }

+      break;

+

+    case Dhcp6StsNotOnLink:

+      if (Instance->IaCb.Ia->State == Dhcp6Confirming) {

+        //

+        // Before initiate new round DHCP, cache the current IA.

+        //

+        Status = Dhcp6CacheIa (Instance);

+        if (EFI_ERROR (Status)) {

+          return  Status;

+        }

+

+        //

+        // Restart S.A.R.R process to acquire new address.

+        //

+        Status = Dhcp6InitSolicitMsg (Instance);

+        if (EFI_ERROR (Status)) {

+          return Status;

+        }

+      }

+      break;

+

+    default:

+      //

+      // The other status code, just restart solicitation.

+      //

+      break;

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Select the appointed Dhcp6 advertisement message.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  AdSelect        The pointer to the selected Dhcp6 advertisement message.

+

+  @retval EFI_SUCCESS           Selected the right advertisement message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval Others                Failed to select the advertise message.

+

+**/

+EFI_STATUS

+Dhcp6SelectAdvertiseMsg (

+  IN DHCP6_INSTANCE           *Instance,

+  IN EFI_DHCP6_PACKET         *AdSelect

+  )

+{

+  EFI_STATUS                  Status;

+  UINT8                       *Option;

+

+  ASSERT (AdSelect != NULL);

+

+  //

+  // Callback to user with the selected advertisement packet, and the user

+  // might overwrite it.

+  //

+  Status = Dhcp6CallbackUser (Instance, Dhcp6SelectAdvertise, &AdSelect);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Instance->AdSelect = AdSelect;

+

+  //

+  // Dequeue the sent packet for the retransmission since advertisement selected.

+  //

+  Status = Dhcp6DequeueRetry (

+             Instance,

+             AdSelect->Dhcp6.Header.TransactionId,

+             FALSE

+             );

+

+  if (EFI_ERROR(Status)) {

+    return Status;

+  }

+

+  //

+  // Check whether there is server unicast option in the selected advertise

+  // packet, and update it.

+  //

+  Option = Dhcp6SeekOption(

+             AdSelect->Dhcp6.Option,

+             AdSelect->Length - 4,

+             Dhcp6OptServerUnicast

+             );

+

+  if (Option != NULL) {

+

+    Instance->Unicast = AllocateZeroPool (sizeof(EFI_IPv6_ADDRESS));

+

+    if (Instance->Unicast == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    CopyMem (Instance->Unicast, Option + 4, sizeof(EFI_IPv6_ADDRESS));

+  }

+

+  //

+  // Update the information of the Ia by the selected advertisement message.

+  //

+  Status = Dhcp6UpdateIaInfo (Instance, AdSelect);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Send the request message to continue the S.A.R.R. process.

+  //

+  return Dhcp6SendRequestMsg (Instance);

+}

+

+

+/**

+  Handle with the Dhcp6 advertisement message.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  Packet          The pointer to the Dhcp6 advertisement message.

+

+  @retval EFI_SUCCESS           Processed the advertisement message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to process the advertise message.

+

+**/

+EFI_STATUS

+Dhcp6HandleAdvertiseMsg (

+  IN DHCP6_INSTANCE           *Instance,

+  IN EFI_DHCP6_PACKET         *Packet

+  )

+{

+  EFI_STATUS                  Status;

+  UINT8                       *Option;

+  UINT16                      StsCode;

+  BOOLEAN                     Timeout;

+

+  ASSERT(Instance->Config);

+  ASSERT(Instance->IaCb.Ia);

+

+  Timeout = FALSE;

+  StsCode = Dhcp6StsSuccess;

+

+  //

+  // If the client does receives a valid reply message that includes a rapid

+  // commit option since a solicit with rapid commit optioin sent before, select

+  // this reply message. Or else, process the advertise messages as normal.

+  // See details in the section-17.1.4 of rfc-3315.

+  //

+  Option = Dhcp6SeekOption(

+             Packet->Dhcp6.Option,

+             Packet->Length - 4,

+             Dhcp6OptRapidCommit

+             );

+

+  if (Option != NULL && Instance->Config->RapidCommit && Packet->Dhcp6.Header.MessageType == Dhcp6MsgReply) {

+

+    return Dhcp6HandleReplyMsg (Instance, Packet);

+  }

+

+  if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // Client must ignore any advertise message that includes a status code option

+  // containing the value noaddrsavail, with the exception that the client may

+  // display the associated status message to the user.

+  // See the details in the section-17.1.3 of rfc-3315.

+  //

+  Option = Dhcp6SeekOption(

+             Packet->Dhcp6.Option,

+             Packet->Length - 4,

+             Dhcp6OptStatusCode

+             );

+

+  if (Option != NULL) {

+    StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));

+    if (StsCode != Dhcp6StsSuccess) {

+      return EFI_DEVICE_ERROR;

+    }

+  }

+

+  //

+  // Callback to user with the received packet and check the user's feedback.

+  //

+  Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdAdvertise, &Packet);

+

+  if (!EFI_ERROR (Status)) {

+    //

+    // Success means user choose the current advertisement packet.

+    //

+    if (Instance->AdSelect != NULL) {

+      FreePool (Instance->AdSelect);

+    }

+

+    //

+    // Store the selected advertisement packet and set a flag.

+    //

+    Instance->AdSelect = AllocateZeroPool (Packet->Size);

+

+    if (Instance->AdSelect == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    CopyMem (Instance->AdSelect, Packet, Packet->Size);

+

+    Instance->AdPref = 0xff;

+

+  } else if (Status == EFI_NOT_READY) {

+    //

+    // Not_ready means user wants to continue to receive more advertise packets.

+    //

+    if (Instance->AdPref == 0xff && Instance->AdSelect == NULL) {

+      //

+      // It's a tricky point. The timer routine set adpref as 0xff if the first

+      // rt timeout and no advertisement received, which means any advertisement

+      // received will be selected after the first rt.

+      //

+      Timeout = TRUE;

+    }

+

+    //

+    // Check whether the current packet has a 255 preference option or not.

+    // Take non-preference option as 0 value.

+    //

+    Option = Dhcp6SeekOption(

+               Packet->Dhcp6.Option,

+               Packet->Length - 4,

+               Dhcp6OptPreference

+               );

+

+    if (Instance->AdSelect == NULL || (Option != NULL && *(Option + 4) > Instance->AdPref)) {

+      //

+      // No advertisements received before or preference is more than other

+      // advertisements received before. Then store the new packet and the

+      // preference value.

+      //

+      if (Instance->AdSelect != NULL) {

+        FreePool (Instance->AdSelect);

+      }

+

+      Instance->AdSelect = AllocateZeroPool (Packet->Size);

+

+      if (Instance->AdSelect == NULL) {

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      CopyMem (Instance->AdSelect, Packet, Packet->Size);

+

+      if (Option != NULL) {

+        Instance->AdPref = *(Option + 4);

+      }

+    } else {

+      //

+      // Non-preference and other advertisements received before or current

+      // preference is less than other advertisements received before.

+      // Leave the packet alone.

+    }

+

+  } else {

+    //

+    // Other error status means termination.

+    //

+    return Status;

+  }

+

+  //

+  // Client must collect advertise messages as more as possible until the first

+  // RT has elapsed, or get a highest preference 255 advertise.

+  // See details in the section-17.1.2 of rfc-3315.

+  //

+  if (Instance->AdPref == 0xff || Timeout) {

+    Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);

+  }

+

+  return Status;

+}

+

+

+/**

+  The Dhcp6 stateful exchange process routine.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  Packet          The pointer to the received Dhcp6 message.

+

+**/

+VOID

+Dhcp6HandleStateful (

+  IN DHCP6_INSTANCE         *Instance,

+  IN EFI_DHCP6_PACKET       *Packet

+  )

+{

+  EFI_STATUS                Status;

+  EFI_DHCP6_DUID            *ClientId;

+  DHCP6_SERVICE             *Service;

+  UINT8                     *Option;

+

+  Service  = Instance->Service;

+  ClientId = Service->ClientId;

+  Status   = EFI_SUCCESS;

+

+  if (Instance->InDestory || Instance->Config == NULL) {

+    goto ON_CONTINUE;

+  }

+

+  ASSERT (ClientId);

+  ASSERT (Instance->Config);

+  ASSERT (Instance->IaCb.Ia);

+

+  //

+  // Discard the packet if not advertisement or reply packet.

+  //

+  if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise && Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {

+    goto ON_CONTINUE;

+  }

+

+  //

+  // Check whether include client Id or not.

+  //

+  Option = Dhcp6SeekOption(

+             Packet->Dhcp6.Option,

+             Packet->Length - 4,

+             Dhcp6OptClientId

+             );

+

+  if (Option == NULL || CompareMem (Option + 4, ClientId->Duid, ClientId->Length) != 0) {

+    goto ON_CONTINUE;

+  }

+

+  //

+  // Check whether include server Id or not.

+  //

+  Option = Dhcp6SeekOption(

+             Packet->Dhcp6.Option,

+             Packet->Length - 4,

+             Dhcp6OptServerId

+             );

+

+  if (Option == NULL) {

+    goto ON_CONTINUE;

+  }

+

+  switch (Instance->IaCb.Ia->State) {

+  case Dhcp6Selecting:

+    //

+    // Handle the advertisement message when in the Dhcp6Selecting state.

+    // Do not need check return status, if failed, just continue to the next.

+    //

+    Dhcp6HandleAdvertiseMsg (Instance, Packet);

+    break;

+

+  case Dhcp6Requesting:

+  case Dhcp6Confirming:

+  case Dhcp6Renewing:

+  case Dhcp6Rebinding:

+  case Dhcp6Releasing:

+  case Dhcp6Declining:

+    //

+    // Handle the reply message when in the Dhcp6Requesting,  Dhcp6Renewing

+    // Dhcp6Rebinding, Dhcp6Releasing and Dhcp6Declining state.

+    // If failed here, it should reset the current session.

+    //

+    Status = Dhcp6HandleReplyMsg (Instance, Packet);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+    break;

+  default:

+    //

+    // Other state has not supported yet.

+    //

+    break;

+  }

+

+ON_CONTINUE:

+  //

+  // Continue to receive the following Dhcp6 message.

+  //

+  Status = UdpIoRecvDatagram (

+             Service->UdpIo,

+             Dhcp6ReceivePacket,

+             Service,

+             0

+             );

+ON_EXIT:

+  if (EFI_ERROR (Status)) {

+    Dhcp6CleanupSession (Instance, Status);

+  }

+}

+

+

+/**

+  The Dhcp6 stateless exchange process routine.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  Packet          The pointer to the received Dhcp6 message.

+

+**/

+VOID

+Dhcp6HandleStateless (

+  IN DHCP6_INSTANCE         *Instance,

+  IN EFI_DHCP6_PACKET       *Packet

+  )

+{

+  EFI_STATUS                Status;

+  DHCP6_SERVICE             *Service;

+  DHCP6_INF_CB              *InfCb;

+  UINT8                     *Option;

+  BOOLEAN                   IsMatched;

+

+  Service   = Instance->Service;

+  Status    = EFI_SUCCESS;

+  IsMatched = FALSE;

+  InfCb     = NULL;

+

+  if (Instance->InDestory) {

+    goto ON_EXIT;

+  }

+

+  if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Check whether it's a desired Info-request message by Xid.

+  //

+  while (!IsListEmpty (&Instance->InfList)) {

+    InfCb = NET_LIST_HEAD (&Instance->InfList, DHCP6_INF_CB, Link);

+    if (InfCb->Xid == Packet->Dhcp6.Header.TransactionId) {

+      IsMatched = TRUE;

+      break;

+    }

+  }

+

+  if (!IsMatched) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Check whether include server Id or not.

+  //

+  Option = Dhcp6SeekOption (

+             Packet->Dhcp6.Option,

+             Packet->Length - 4,

+             Dhcp6OptServerId

+             );

+

+  if (Option == NULL) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Callback to user with the received packet and check the user's feedback.

+  //

+  Status = InfCb->ReplyCallback (

+                    &Instance->Dhcp6,

+                    InfCb->CallbackContext,

+                    Packet

+                    );

+

+  if (Status == EFI_NOT_READY) {

+    //

+    // Success or aborted will both stop this info-request exchange process,

+    // but not ready means user wants to continue to receive reply.

+    //

+    goto ON_EXIT;

+  }

+

+  //

+  // Dequeue the sent packet from the txlist if the xid matched, and ignore

+  // if no xid matched.

+  //

+  Dhcp6DequeueRetry (

+    Instance,

+    Packet->Dhcp6.Header.TransactionId,

+    FALSE

+    );

+

+  //

+  // For sync, set the status out of polling for info-request.

+  //

+  Instance->UdpSts = Status;

+

+ON_EXIT:

+

+  Status = UdpIoRecvDatagram (

+             Service->UdpIo,

+             Dhcp6ReceivePacket,

+             Service,

+             0

+             );

+

+  if (EFI_ERROR (Status)) {

+    Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATELESS);

+  }

+}

+

+

+/**

+  The receive callback function for Dhcp6 exchange process.

+

+  @param[in]  Udp6Wrap        The pointer to the received net buffer.

+  @param[in]  EndPoint        The pointer to the udp end point.

+  @param[in]  IoStatus        The return status from udp io.

+  @param[in]  Context         The opaque parameter to the function.

+

+**/

+VOID

+EFIAPI

+Dhcp6ReceivePacket (

+  IN NET_BUF                *Udp6Wrap,

+  IN UDP_END_POINT          *EndPoint,

+  IN EFI_STATUS             IoStatus,

+  IN VOID                   *Context

+  )

+{

+  EFI_DHCP6_HEADER          *Head;

+  EFI_DHCP6_PACKET          *Packet;

+  DHCP6_SERVICE             *Service;

+  DHCP6_INSTANCE            *Instance;

+  DHCP6_TX_CB               *TxCb;

+  UINT32                    Size;

+  BOOLEAN                   IsDispatched;

+  BOOLEAN                   IsStateless;

+  LIST_ENTRY                *Entry1;

+  LIST_ENTRY                *Next1;

+  LIST_ENTRY                *Entry2;

+  LIST_ENTRY                *Next2;

+

+  ASSERT (Udp6Wrap != NULL);

+  ASSERT (Context != NULL);

+

+  Service      = (DHCP6_SERVICE *) Context;

+  Instance     = NULL;

+  Packet       = NULL;

+  IsDispatched = FALSE;

+  IsStateless  = FALSE;

+

+  if (EFI_ERROR (IoStatus)) {

+    return ;

+  }

+

+  //

+  // Copy the net buffer received from upd6 to a Dhcp6 packet.

+  //

+  Size   = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize;

+  Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size);

+

+  if (Packet == NULL) {

+    goto ON_CONTINUE;

+  }

+

+  Packet->Size   = Size;

+  Head           = &Packet->Dhcp6.Header;

+  Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head);

+

+  if (Packet->Length == 0) {

+    goto ON_CONTINUE;

+  }

+

+  //

+  // Dispatch packet to right instance by transaction id.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {

+

+    Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);

+

+    NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) {

+

+      TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link);

+

+      if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) {

+        //

+        // Find the corresponding packet in tx list, and check it whether belongs

+        // to stateful exchange process.

+        //

+        if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {

+          IsStateless = TRUE;

+        }

+        IsDispatched  = TRUE;

+        break;

+      }

+    }

+

+    if (IsDispatched) {

+      break;

+    }

+  }

+

+  //

+  // Skip this packet if not dispatched to any instance.

+  //

+  if (!IsDispatched) {

+    goto ON_CONTINUE;

+  }

+

+  //

+  // Dispatch the received packet ot the right instance.

+  //

+  if (IsStateless) {

+    Dhcp6HandleStateless (Instance, Packet);

+  } else {

+    Dhcp6HandleStateful (Instance, Packet);

+  }

+

+ON_CONTINUE:

+

+  NetbufFree (Udp6Wrap);

+

+  if (Packet != NULL) {

+    FreePool (Packet);

+  }

+}

+

+/**

+  Detect Link movement for specified network device.

+

+  This routine will try to invoke Snp->GetStatus() to get the media status.

+  If media present status switches from unpresent to present, a link movement

+  is detected. Note that the underlying UNDI driver may not support reporting

+  media status from GET_STATUS command. If that, fail to detect link movement.

+

+  @param[in]  Instance       The pointer to DHCP6_INSTANCE.

+

+  @retval     TRUE           A link movement is detected.

+  @retval     FALSE          A link movement is not detected.

+

+**/

+BOOLEAN

+Dhcp6LinkMovDetect (

+  IN  DHCP6_INSTANCE            *Instance

+  )

+{

+  UINT32                       InterruptStatus;

+  BOOLEAN                      MediaPresent;

+  EFI_STATUS                   Status;

+  EFI_SIMPLE_NETWORK_PROTOCOL  *Snp;

+

+  ASSERT (Instance != NULL);

+  Snp = Instance->Service->Snp;

+  MediaPresent = Instance->MediaPresent;

+

+  //

+  // Check whether SNP support media detection

+  //

+  if (!Snp->Mode->MediaPresentSupported) {

+    return FALSE;

+  }

+

+  //

+  // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data

+  //

+  Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);

+  if (EFI_ERROR (Status)) {

+    return FALSE;

+  }

+

+  Instance->MediaPresent = Snp->Mode->MediaPresent;

+  //

+  // Media transimit Unpresent to Present means new link movement is detected.

+  //

+  if (!MediaPresent && Instance->MediaPresent) {

+    return TRUE;

+  }

+  return FALSE;

+}

+

+

+/**

+  The timer routine of the Dhcp6 instance for each second.

+

+  @param[in]  Event           The timer event.

+  @param[in]  Context         The opaque parameter to the function.

+

+**/

+VOID

+EFIAPI

+Dhcp6OnTimerTick (

+  IN EFI_EVENT              Event,

+  IN VOID                   *Context

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *NextEntry;

+  DHCP6_INSTANCE            *Instance;

+  DHCP6_TX_CB               *TxCb;

+  DHCP6_IA_CB               *IaCb;

+  UINT32                    LossTime;

+

+  ASSERT (Context != NULL);

+

+  Instance = (DHCP6_INSTANCE *) Context;

+

+  //

+  // 1. Loop the tx list, count live time of every tx packet to check whether

+  //    need re-transmit or not.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {

+

+    TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);

+

+    TxCb->TickTime++;

+

+    if (TxCb->TickTime > TxCb->RetryExp) {

+      //

+      // Handle the first rt in the transmission of solicit specially.

+      //

+      if (TxCb->RetryCnt == 0 && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {

+        if (Instance->AdSelect == NULL) {

+          //

+          // Set adpref as 0xff here to indicate select any advertisement

+          // afterwards.

+          //

+          Instance->AdPref = 0xff;

+        } else {

+          //

+          // Select the advertisement received before.

+          //

+          Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);

+          return;

+        }

+      }

+      //

+      // Increase the retry count for the packet and add up the total loss time.

+      //

+      TxCb->RetryCnt++;

+      TxCb->RetryLos += TxCb->RetryExp;

+

+      //

+      // Check whether overflow the max retry count limit for this packet

+      //

+      if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) {

+        goto ON_CLOSE;

+      }

+

+      //

+      // Check whether overflow the max retry duration for this packet

+      //

+      if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) {

+        goto ON_CLOSE;

+      }

+

+      //

+      // Re-calculate retry expire timeout for the next time.

+      //

+      // Firstly, Check the new calculated time whether overflow the max retry

+      // expire time.

+      //

+      TxCb->RetryExp = Dhcp6CalculateExpireTime (

+                         TxCb->RetryExp,

+                         FALSE,

+                         TRUE

+                         );

+

+      if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) {

+        TxCb->RetryExp = Dhcp6CalculateExpireTime (

+                           TxCb->RetryCtl.Mrt,

+                           TRUE,

+                           TRUE

+                           );

+      }

+

+      //

+      // Secondly, Check the new calculated time whether overflow the max retry

+      // duration time.

+      //

+      LossTime = TxCb->RetryLos + TxCb->RetryExp;

+      if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) {

+        TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos;

+      }

+

+      //

+      // Reset the tick time for the next retransmission

+      //

+      TxCb->TickTime = 0;

+

+      //

+      // Retransmit the last sent packet again.

+      //

+      Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed);

+    }

+  }

+

+  //

+  // 2. Check the configured Ia, count lease time of every valid Ia to check

+  // whether need to renew or rebind this Ia.

+  //

+  IaCb = &Instance->IaCb;

+

+  if (Instance->Config == NULL || IaCb->Ia == NULL) {

+    return;

+  }

+

+  if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) {

+

+    IaCb->LeaseTime++;

+

+    if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) {

+      //

+      // Exceed t2, send rebind packet to extend the Ia lease.

+      //

+      Dhcp6SendRenewRebindMsg (Instance, TRUE);

+

+    } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) {

+

+      //

+      // Exceed t1, send renew packet to extend the Ia lease.

+      //

+      Dhcp6SendRenewRebindMsg (Instance, FALSE);

+    }

+  }

+

+  //

+  // 3. In any situation when a client may have moved to a new link, the

+  //    client MUST initiate a Confirm/Reply message exchange.

+  //

+  if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) {

+    Dhcp6SendConfirmMsg (Instance);

+  }

+

+  return;

+

+ ON_CLOSE:

+

+  if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest ||

+      TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew       ||

+      TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm

+      ) {

+    //

+    // The failure of renew/Confirm will still switch to the bound state.

+    //

+    if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) ||

+        (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) {

+      ASSERT (Instance->IaCb.Ia);

+      Instance->IaCb.Ia->State = Dhcp6Bound;

+    }

+    //

+    // The failure of info-request will return no response.

+    //

+    if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {

+      Instance->UdpSts = EFI_NO_RESPONSE;

+    }

+    Dhcp6DequeueRetry (

+      Instance,

+      TxCb->Xid,

+      TRUE

+      );

+  } else {

+    //

+    // The failure of the others will terminate current state machine if timeout.

+    //

+    Dhcp6CleanupSession (Instance, EFI_NO_RESPONSE);

+  }

+}

diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h
new file mode 100644
index 0000000..31459c9
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h
@@ -0,0 +1,193 @@
+/** @file

+  Dhcp6 internal functions declaration.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_DHCP6_IO_H__

+#define __EFI_DHCP6_IO_H__

+

+

+/**

+  Clean up the specific nodes in the retry list.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  Scope           The scope of cleanup nodes.

+

+**/

+VOID

+Dhcp6CleanupRetry (

+  IN DHCP6_INSTANCE         *Instance,

+  IN UINT32                 Scope

+  );

+

+/**

+  Clean up the session of the instance stateful exchange.

+

+  @param[in, out]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]       Status          The return status from udp.

+

+**/

+VOID

+Dhcp6CleanupSession (

+  IN OUT DHCP6_INSTANCE          *Instance,

+  IN     EFI_STATUS              Status

+  );

+

+/**

+  Create the solicit message and send it.

+

+  @param[in]  Instance        The pointer to Dhcp6 instance.

+

+  @retval EFI_SUCCESS           Create and send the solicit message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval Others                Failed to send the solicit message.

+

+**/

+EFI_STATUS

+Dhcp6SendSolicitMsg (

+  IN DHCP6_INSTANCE         *Instance

+  );

+

+/**

+  Create the request message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+

+  @retval EFI_SUCCESS           Create and send the request message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to send the request message.

+

+**/

+EFI_STATUS

+Dhcp6SendRequestMsg (

+  IN DHCP6_INSTANCE         *Instance

+  );

+

+/**

+  Create the renew/rebind message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  RebindRequest   If TRUE, it is a Rebind type message.

+                              Otherwise, it is a Renew type message.

+

+  @retval EFI_SUCCESS           Create and send the renew/rebind message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to send the renew/rebind message.

+

+**/

+EFI_STATUS

+Dhcp6SendRenewRebindMsg (

+  IN DHCP6_INSTANCE         *Instance,

+  IN BOOLEAN                RebindRequest

+  );

+

+/**

+  Create the decline message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  DecIa           The pointer to the decline Ia.

+

+  @retval EFI_SUCCESS           Create and send the decline message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to send the decline message.

+

+**/

+EFI_STATUS

+Dhcp6SendDeclineMsg (

+  IN DHCP6_INSTANCE            *Instance,

+  IN EFI_DHCP6_IA              *DecIa

+  );

+

+/**

+  Create the release message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  RelIa           The pointer to the release Ia.

+

+  @retval EFI_SUCCESS           Create and send the release message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected error.

+  @retval Others                Failed to send the release message.

+

+**/

+EFI_STATUS

+Dhcp6SendReleaseMsg (

+  IN DHCP6_INSTANCE            *Instance,

+  IN EFI_DHCP6_IA              *RelIa

+  );

+

+/**

+  Create the information request message and send it.

+

+  @param[in]  Instance        The pointer to the Dhcp6 instance.

+  @param[in]  InfCb           The pointer to the information request control block.

+  @param[in]  SendClientId    If TRUE, the client identifier option will be included in

+                              information request message. Otherwise, the client identifier

+                              option will not be included.

+  @param[in]  OptionRequest   The pointer to the option request option.

+  @param[in]  OptionCount     The number options in the OptionList.

+  @param[in]  OptionList      The array pointers to the appended options.

+  @param[in]  Retransmission  The pointer to the retransmission control.

+

+  @retval EFI_SUCCESS           Create and send the info-request message successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval Others                Failed to send the info-request message.

+

+**/

+EFI_STATUS

+Dhcp6SendInfoRequestMsg (

+  IN DHCP6_INSTANCE            *Instance,

+  IN DHCP6_INF_CB              *InfCb,

+  IN BOOLEAN                   SendClientId,

+  IN EFI_DHCP6_PACKET_OPTION   *OptionRequest,

+  IN UINT32                    OptionCount,

+  IN EFI_DHCP6_PACKET_OPTION   *OptionList[],

+  IN EFI_DHCP6_RETRANSMISSION  *Retransmission

+  );

+

+/**

+  The receive callback function for the Dhcp6 exchange process.

+

+  @param[in]  Udp6Wrap        The pointer to the received net buffer.

+  @param[in]  EndPoint        The pointer to the udp end point.

+  @param[in]  IoStatus        The return status from udp io.

+  @param[in]  Context         The opaque parameter to the function.

+

+**/

+VOID

+EFIAPI

+Dhcp6ReceivePacket (

+  IN NET_BUF                *Udp6Wrap,

+  IN UDP_END_POINT          *EndPoint,

+  IN EFI_STATUS             IoStatus,

+  IN VOID                   *Context

+  );

+

+/**

+  The timer routine of the Dhcp6 instance for each second.

+

+  @param[in]  Event           The timer event.

+  @param[in]  Context         The opaque parameter to the function.

+

+**/

+VOID

+EFIAPI

+Dhcp6OnTimerTick (

+  IN EFI_EVENT              Event,

+  IN VOID                   *Context

+  );

+

+#endif

diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c
new file mode 100644
index 0000000..be7a985
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c
@@ -0,0 +1,1146 @@
+/** @file

+  Dhcp6 support functions implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Dhcp6Impl.h"

+

+

+/**

+  Generate client Duid in the format of Duid-llt.

+

+  @param[in]  Mode          The pointer to the mode of SNP.

+

+  @retval     NULL          If it failed to generate a client Id.

+  @retval     others        The pointer to the new client id.

+

+**/

+EFI_DHCP6_DUID *

+Dhcp6GenerateClientId (

+  IN EFI_SIMPLE_NETWORK_MODE   *Mode

+  )

+{

+  EFI_STATUS                Status;

+  EFI_DHCP6_DUID            *Duid;

+  EFI_TIME                  Time;

+  UINT32                    Stamp;

+

+  //

+  // Attempt to get client Id from variable to keep it constant.

+  // See details in section-9 of rfc-3315.

+  //

+  Duid = GetVariable (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid);

+  if (Duid != NULL) {

+    return Duid;

+  }

+

+  //

+  // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month.

+  //

+  gRT->GetTime (&Time, NULL);

+  Stamp = (UINT32)

+    (

+      (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) *

+      60 +

+      Time.Second

+    );

+

+  //

+  //  The format of client identifier option:

+  //

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |        OPTION_CLIENTID        |          option-len           |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    .                                                               .

+  //    .                              DUID                             .

+  //    .                        (variable length)                      .

+  //    .                                                               .

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+  //

+  //  The format of DUID-LLT:

+  //

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |          Duid type (1)        |    hardware type (16 bits)    |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                        time (32 bits)                         |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    .                                                               .

+  //    .             link-layer address (variable length)              .

+  //    .                                                               .

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  //

+  // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes

+  //

+  Duid = AllocateZeroPool (10 + Mode->HwAddressSize);

+  if (Duid == NULL) {

+    return NULL;

+  }

+

+  //

+  // sizeof (Duid-type + hardware-type + time) = 8 bytes

+  //

+  Duid->Length = (UINT16) (Mode->HwAddressSize + 8);

+

+  //

+  // Set the Duid-type, hardware-type, time and copy the hardware address.

+  //

+  WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeLlt));

+  WriteUnaligned16 ((UINT16 *) (Duid->Duid + 2), HTONS (NET_IFTYPE_ETHERNET));

+  WriteUnaligned32 ((UINT32 *) (Duid->Duid + 4), HTONL (Stamp));

+

+  CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize);

+

+  Status = gRT->SetVariable (

+                  L"ClientId",

+                  &gEfiDhcp6ServiceBindingProtocolGuid,

+                  (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),

+                  Duid->Length + 2,

+                  (VOID *) Duid

+                  );

+  ASSERT_EFI_ERROR (Status);

+

+  return Duid;

+}

+

+

+/**

+  Copy the Dhcp6 configure data.

+

+  @param[in]  DstCfg        The pointer to the destination configure data.

+  @param[in]  SorCfg        The pointer to the source configure data.

+

+  @retval EFI_SUCCESS           Copy the content from SorCfg from DstCfg successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+

+**/

+EFI_STATUS

+Dhcp6CopyConfigData (

+  IN EFI_DHCP6_CONFIG_DATA      *DstCfg,

+  IN EFI_DHCP6_CONFIG_DATA      *SorCfg

+  )

+{

+  UINTN                     Index;

+  UINTN                     OptionListSize;

+  UINTN                     OptionSize;

+

+  CopyMem (DstCfg, SorCfg, sizeof (EFI_DHCP6_CONFIG_DATA));

+

+  //

+  // Allocate another buffer for solicitretransmission, and copy it.

+  //

+  if (SorCfg->SolicitRetransmission != NULL) {

+

+    DstCfg->SolicitRetransmission = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));

+

+    if (DstCfg->SolicitRetransmission == NULL) {

+      //

+      // Error will be handled out of this function.

+      //

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    CopyMem (

+      DstCfg->SolicitRetransmission,

+      SorCfg->SolicitRetransmission,

+      sizeof (EFI_DHCP6_RETRANSMISSION)

+      );

+  }

+

+  if (SorCfg->OptionList != NULL && SorCfg->OptionCount != 0) {

+

+    OptionListSize     = SorCfg->OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *);

+    DstCfg->OptionList = AllocateZeroPool (OptionListSize);

+

+    if (DstCfg->OptionList == NULL) {

+      //

+      // Error will be handled out of this function.

+      //

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    for (Index = 0; Index < SorCfg->OptionCount; Index++) {

+

+      OptionSize                = NTOHS (SorCfg->OptionList[Index]->OpLen) + 4;

+      DstCfg->OptionList[Index] = AllocateZeroPool (OptionSize);

+

+      if (DstCfg->OptionList[Index] == NULL) {

+        //

+        // Error will be handled out of this function.

+        //

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      CopyMem (

+        DstCfg->OptionList[Index],

+        SorCfg->OptionList[Index],

+        OptionSize

+        );

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Clean up the configure data.

+

+  @param[in, out]  CfgData       The pointer to the configure data.

+

+**/

+VOID

+Dhcp6CleanupConfigData (

+  IN OUT EFI_DHCP6_CONFIG_DATA       *CfgData

+  )

+{

+  UINTN                          Index;

+

+  ASSERT (CfgData != NULL);

+  //

+  // Clean up all fields in config data including the reference buffers, but do

+  // not free the config data buffer itself.

+  //

+  if (CfgData->OptionList != NULL) {

+    for (Index = 0; Index < CfgData->OptionCount; Index++) {

+      if (CfgData->OptionList[Index] != NULL) {

+        FreePool (CfgData->OptionList[Index]);

+      }

+    }

+    FreePool (CfgData->OptionList);

+  }

+

+  if (CfgData->SolicitRetransmission != NULL) {

+    FreePool (CfgData->SolicitRetransmission);

+  }

+

+  ZeroMem (CfgData, sizeof (EFI_DHCP6_CONFIG_DATA));

+}

+

+

+/**

+  Clean up the mode data.

+

+  @param[in, out]  ModeData      The pointer to the mode data.

+

+**/

+VOID

+Dhcp6CleanupModeData (

+  IN OUT EFI_DHCP6_MODE_DATA        *ModeData

+  )

+{

+  ASSERT (ModeData != NULL);

+  //

+  // Clean up all fields in mode data including the reference buffers, but do

+  // not free the mode data buffer itself.

+  //

+  if (ModeData->ClientId != NULL) {

+    FreePool (ModeData->ClientId);

+  }

+

+  if (ModeData->Ia != NULL) {

+

+    if (ModeData->Ia->ReplyPacket != NULL) {

+      FreePool (ModeData->Ia->ReplyPacket);

+    }

+    FreePool (ModeData->Ia);

+  }

+

+  ZeroMem (ModeData, sizeof (EFI_DHCP6_MODE_DATA));

+}

+

+

+/**

+  Calculate the expire time by the algorithm defined in rfc.

+

+  @param[in]  Base          The base value of the time.

+  @param[in]  IsFirstRt     If TRUE, it is the first time to calculate expire time.

+  @param[in]  NeedSigned    If TRUE, the the signed factor is needed.

+

+  @return     Expire        The calculated result for the new expire time.

+

+**/

+UINT32

+Dhcp6CalculateExpireTime (

+  IN UINT32                 Base,

+  IN BOOLEAN                IsFirstRt,

+  IN BOOLEAN                NeedSigned

+  )

+{

+  EFI_TIME                  Time;

+  BOOLEAN                   Signed;

+  UINT32                    Seed;

+  UINT32                    Expire;

+

+  //

+  // Take the 10bits of microsecond in system time as a uniform distribution.

+  // Take the 10th bit as a flag to determine it's signed or not.

+  //

+  gRT->GetTime (&Time, NULL);

+  Seed   = ((Time.Nanosecond >> 10) & DHCP6_10_BIT_MASK);

+  Signed = (BOOLEAN) ((((Time.Nanosecond >> 9) & 0x01) != 0) ? TRUE : FALSE);

+  Signed = (BOOLEAN) (NeedSigned ? Signed : FALSE);

+

+  //

+  // Calculate expire by the following algo:

+  //   1. base + base * (-0.1 ~ 0) for the first solicit

+  //   2. base + base * (-0.1 ~ 0.1) for the first other messages

+  //   3. 2 * base + base * (-0.1 ~ 0.1) for the subsequent all messages

+  //   4. base + base * (-0.1 ~ 0) for the more than mrt timeout

+  //

+  // The (Seed / 0x3ff / 10) is used to a random range (0, 0.1).

+  //

+  if (IsFirstRt && Signed) {

+

+    Expire = Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);

+

+  } else if (IsFirstRt && !Signed) {

+

+    Expire = Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);

+

+  } else if (!IsFirstRt && Signed) {

+

+    Expire = 2 * Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);

+

+  } else {

+

+    Expire = 2 * Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);

+  }

+

+  Expire = (Expire != 0) ? Expire : 1;

+

+  return Expire;

+}

+

+

+/**

+  Calculate the lease time by the algorithm defined in rfc.

+

+  @param[in]  IaCb          The pointer to the Ia control block.

+

+**/

+VOID

+Dhcp6CalculateLeaseTime (

+  IN DHCP6_IA_CB              *IaCb

+  )

+{

+  EFI_DHCP6_IA_ADDRESS        *IaAddr;

+  UINT32                      MinLt;

+  UINT32                      MaxLt;

+  UINTN                       Index;

+

+  ASSERT (IaCb->Ia->IaAddressCount > 0);

+

+  MinLt    = (UINT32) (-1);

+  MaxLt    = 0;

+

+  //

+  // Calculate minlt as min of all valid life time, and maxlt as max of all

+  // valid life time.

+  //

+  for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) {

+    IaAddr = IaCb->Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);

+    MinLt  = MIN (MinLt, IaAddr->ValidLifetime);

+    MaxLt  = MAX (MinLt, IaAddr->ValidLifetime);

+  }

+

+  //

+  // Take 50% minlt as t1, and 80% maxlt as t2 if Dhcp6 server doesn't offer

+  // such information.

+  //

+  IaCb->T1            = (IaCb->T1 != 0) ? IaCb->T1 : (UINT32)(MinLt * 5 / 10);

+  IaCb->T2            = (IaCb->T2 != 0) ? IaCb->T2 : (UINT32)(MinLt * 8 / 10);

+  IaCb->AllExpireTime = MaxLt;

+  IaCb->LeaseTime     = 0;

+}

+

+

+/**

+  Check whether the addresses are all included by the configured Ia.

+

+  @param[in]  Ia            The pointer to the Ia.

+  @param[in]  AddressCount  The number of addresses.

+  @param[in]  Addresses     The pointer to the addresses buffer.

+

+  @retval EFI_SUCCESS         The addresses are all included by the configured IA.

+  @retval EFI_NOT_FOUND       The addresses are not included by the configured IA.

+

+**/

+EFI_STATUS

+Dhcp6CheckAddress (

+  IN EFI_DHCP6_IA             *Ia,

+  IN UINT32                   AddressCount,

+  IN EFI_IPv6_ADDRESS         *Addresses

+  )

+{

+  UINTN                       Index1;

+  UINTN                       Index2;

+  BOOLEAN                     Found;

+

+  //

+  // Check whether the addresses are all included by the configured IA. And it

+  // will return success if address count is zero, which means all addresses.

+  //

+  for (Index1 = 0; Index1 < AddressCount; Index1++) {

+

+    Found = FALSE;

+

+    for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {

+

+      if (CompareMem (

+            &Addresses[Index1],

+            &Ia->IaAddress[Index2],

+            sizeof (EFI_IPv6_ADDRESS)

+            ) == 0) {

+

+        Found = TRUE;

+        break;

+      }

+    }

+

+    if (!Found) {

+      return EFI_NOT_FOUND;

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Deprive the addresses from current Ia, and generate another eliminated Ia.

+

+  @param[in]  Ia            The pointer to the Ia.

+  @param[in]  AddressCount  The number of addresses.

+  @param[in]  Addresses     The pointer to the addresses buffer.

+

+  @retval     NULL          If it failed to generate the deprived Ia.

+  @retval     others        The pointer to the deprived Ia.

+

+**/

+EFI_DHCP6_IA *

+Dhcp6DepriveAddress (

+  IN EFI_DHCP6_IA             *Ia,

+  IN UINT32                   AddressCount,

+  IN EFI_IPv6_ADDRESS         *Addresses

+  )

+{

+  EFI_DHCP6_IA                *IaCopy;

+  UINTN                       IaCopySize;

+  UINTN                       Index1;

+  UINTN                       Index2;

+  BOOLEAN                     Found;

+

+  if (AddressCount == 0) {

+    //

+    // It means release all Ia addresses if address count is zero.

+    //

+    AddressCount = Ia->IaAddressCount;

+  }

+

+  ASSERT (AddressCount != 0);

+

+  IaCopySize = sizeof (EFI_DHCP6_IA) + (AddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);

+  IaCopy     = AllocateZeroPool (IaCopySize);

+

+  if (IaCopy == NULL) {

+    return NULL;

+  }

+

+  if (AddressCount == Ia->IaAddressCount) {

+    //

+    // If release all Ia addresses, just copy the configured Ia and then set

+    // its address count as zero.

+    // We may decline/release part of addresses at the begining. So it's a

+    // forwarding step to update address infor for decline/release, while the

+    // other infor such as Ia state will be updated when receiving reply.

+    //

+    CopyMem (IaCopy, Ia, IaCopySize);

+    Ia->IaAddressCount = 0;

+    return IaCopy;

+  }

+

+  CopyMem (IaCopy, Ia, sizeof (EFI_DHCP6_IA));

+

+  //

+  // Move the addresses from the Ia of instance to the deprived Ia.

+  //

+  for (Index1 = 0; Index1 < AddressCount; Index1++) {

+

+    Found = FALSE;

+

+    for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {

+

+      if (CompareMem (

+            &Addresses[Index1],

+            &Ia->IaAddress[Index2],

+            sizeof (EFI_IPv6_ADDRESS)

+            ) == 0) {

+        //

+        // Copy the deprived address to the copy of Ia

+        //

+        CopyMem (

+          &IaCopy->IaAddress[Index1],

+          &Ia->IaAddress[Index2],

+          sizeof (EFI_DHCP6_IA_ADDRESS)

+          );

+        //

+        // Delete the deprived address from the instance Ia

+        //

+        if (Index2 + 1 < Ia->IaAddressCount) {

+          CopyMem (

+            &Ia->IaAddress[Index2],

+            &Ia->IaAddress[Index2 + 1],

+            (Ia->IaAddressCount - Index2 - 1) * sizeof (EFI_DHCP6_IA_ADDRESS)

+            );

+        }

+        Found = TRUE;

+        break;

+      }

+    }

+    ASSERT (Found == TRUE);

+  }

+

+  Ia->IaAddressCount    -= AddressCount;

+  IaCopy->IaAddressCount = AddressCount;

+

+  return IaCopy;

+}

+

+

+/**

+  The dummy ext buffer free callback routine.

+

+  @param[in]  Arg           The pointer to the parameter.

+

+**/

+VOID

+EFIAPI

+Dhcp6DummyExtFree (

+  IN VOID                      *Arg

+  )

+{

+}

+

+

+/**

+  The callback routine once message transmitted.

+

+  @param[in]  Udp6Wrap      The pointer to the received net buffer.

+  @param[in]  EndPoint      The pointer to the udp end point.

+  @param[in]  IoStatus      The return status from udp io.

+  @param[in]  Context       The opaque parameter to the function.

+

+**/

+VOID

+EFIAPI

+Dhcp6OnTransmitted (

+  IN NET_BUF                   *Wrap,

+  IN UDP_END_POINT             *EndPoint,

+  IN EFI_STATUS                IoStatus,

+  IN VOID                      *Context

+  )

+{

+  NetbufFree (Wrap);

+}

+

+

+/**

+  Append the option to Buf, and move Buf to the end.

+

+  @param[in, out] Buf           The pointer to the buffer.

+  @param[in]      OptType       The option type.

+  @param[in]      OptLen        The length of option contents.

+  @param[in]      Data          The pointer to the option content.

+

+  @return         Buf           The position to append the next option.

+

+**/

+UINT8 *

+Dhcp6AppendOption (

+  IN OUT UINT8               *Buf,

+  IN     UINT16              OptType,

+  IN     UINT16              OptLen,

+  IN     UINT8               *Data

+  )

+{

+  //

+  //  The format of Dhcp6 option:

+  //

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |          option-code          |   option-len (option data)    |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                          option-data                          |

+  //    |                      (option-len octets)                      |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  ASSERT (OptLen != 0);

+

+  WriteUnaligned16 ((UINT16 *) Buf, OptType);

+  Buf            += 2;

+  WriteUnaligned16 ((UINT16 *) Buf, OptLen);

+  Buf            += 2;

+  CopyMem (Buf, Data, NTOHS (OptLen));

+  Buf            += NTOHS (OptLen);

+

+  return Buf;

+}

+

+

+/**

+  Append the appointed Ia option to Buf, and move Buf to the end.

+

+  @param[in, out] Buf           The pointer to the position to append.

+  @param[in]      Ia            The pointer to the Ia.

+  @param[in]      T1            The time of T1.

+  @param[in]      T2            The time of T2.

+

+  @return         Buf           The position to append the next Ia option.

+

+**/

+UINT8 *

+Dhcp6AppendIaOption (

+  IN OUT UINT8                  *Buf,

+  IN     EFI_DHCP6_IA           *Ia,

+  IN     UINT32                 T1,

+  IN     UINT32                 T2

+  )

+{

+  UINT8                     *AddrOpt;

+  UINT16                    *Len;

+  UINTN                     Index;

+  UINT16                    Length;

+

+  //

+  //  The format of IA_NA and IA_TA option:

+  //

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |          OPTION_IA_NA         |          option-len           |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                        IAID (4 octets)                        |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                        T1 (only for IA_NA)                    |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                        T2 (only for IA_NA)                    |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                                                               |

+  //    .                  IA_NA-options/IA_TA-options                  .

+  //    .                                                               .

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  //

+  // Fill the value of Ia option type

+  //

+  WriteUnaligned16 ((UINT16 *) Buf, HTONS (Ia->Descriptor.Type));

+  Buf                     += 2;

+

+  //

+  // Fill the len of Ia option later, keep the pointer first

+  //

+  Len                      = (UINT16 *) Buf;

+  Buf                     += 2;

+

+  //

+  // Fill the value of iaid

+  //

+  WriteUnaligned32 ((UINT32 *) Buf, HTONL (Ia->Descriptor.IaId));

+  Buf                     += 4;

+

+  //

+  // Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified.

+  //

+  if (Ia->Descriptor.Type == Dhcp6OptIana) {

+    WriteUnaligned32 ((UINT32 *) Buf, ((T1 != 0) ? T1 : 0xffffffff));

+    Buf                   += 4;

+    WriteUnaligned32 ((UINT32 *) Buf, ((T2 != 0) ? T2 : 0xffffffff));

+    Buf                   += 4;

+  }

+

+  //

+  // Fill all the addresses belong to the Ia

+  //

+  for (Index = 0; Index < Ia->IaAddressCount; Index++) {

+

+     AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);

+     Length  = HTONS ((UINT16) sizeof (EFI_DHCP6_IA_ADDRESS));

+     Buf     = Dhcp6AppendOption (

+                 Buf,

+                 HTONS (Dhcp6OptIaAddr),

+                 Length,

+                 AddrOpt

+                 );

+  }

+

+  //

+  // Fill the value of Ia option length

+  //

+  *Len = HTONS ((UINT16) (Buf - (UINT8 *) Len - 2));

+

+  return Buf;

+}

+

+/**

+  Append the appointed Elapsed time option to Buf, and move Buf to the end.

+

+  @param[in, out] Buf           The pointer to the position to append.

+  @param[in]      Instance      The pointer to the Dhcp6 instance.

+  @param[out]     Elapsed       The pointer to the elapsed time value in

+                                  the generated packet.

+

+  @return         Buf           The position to append the next Ia option.

+

+**/

+UINT8 *

+Dhcp6AppendETOption (

+  IN OUT UINT8                  *Buf,

+  IN     DHCP6_INSTANCE         *Instance,

+  OUT    UINT16                 **Elapsed

+  )

+{

+  //

+  //  The format of elapsed time option:

+  //

+  //   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

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //  |      OPTION_ELAPSED_TIME      |           option-len          |

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //  |          elapsed-time         |

+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  //

+  // Fill the value of elapsed-time option type.

+  //

+  WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptElapsedTime));

+  Buf                     += 2;

+

+  //

+  // Fill the len of elapsed-time option, which is fixed.

+  //

+  WriteUnaligned16 ((UINT16 *) Buf, HTONS(2));

+  Buf                     += 2;

+

+  //

+  // Fill in elapsed time value with 0 value for now.  The actual value is

+  // filled in later just before the packet is transmitted.

+  //

+  WriteUnaligned16 ((UINT16 *) Buf, HTONS(0));

+  *Elapsed                  = (UINT16 *) Buf;

+  Buf                     += 2;

+

+  return Buf;

+}

+

+/**

+  Set the elapsed time based on the given instance and the pointer to the

+  elapsed time option.

+

+  @param[in]      Elapsed       The pointer to the position to append.

+  @param[in]      Instance      The pointer to the Dhcp6 instance.

+

+**/

+VOID

+SetElapsedTime (

+  IN     UINT16                 *Elapsed,

+  IN     DHCP6_INSTANCE         *Instance

+  )

+{

+  EFI_TIME          Time;

+  UINT64            CurrentStamp;

+  UINT64            ElapsedTimeValue;

+

+  //

+  // Generate a time stamp of the centiseconds from 2000/1/1, assume 30day/month.

+  //

+  gRT->GetTime (&Time, NULL);

+  CurrentStamp = (UINT64)

+    (

+      ((((((Time.Year - 2000) * 360 +

+       (Time.Month - 1)) * 30 +

+       (Time.Day - 1)) * 24 + Time.Hour) * 60 +

+       Time.Minute) * 60 + Time.Second) * 100

+       + DivU64x32(Time.Nanosecond, 10000000)

+    );

+

+  //

+  // Sentinel value of 0 means that this is the first DHCP packet that we are

+  // sending and that we need to initialize the value.  First DHCP Solicit

+  // gets 0 elapsed-time.  Otherwise, calculate based on StartTime.

+  //

+  if (Instance->StartTime == 0) {

+    ElapsedTimeValue = 0;

+    Instance->StartTime = CurrentStamp;

+  } else {

+    ElapsedTimeValue = CurrentStamp - Instance->StartTime;

+

+    //

+    // If elapsed time cannot fit in two bytes, set it to 0xffff.

+    //

+    if (ElapsedTimeValue > 0xffff) {

+      ElapsedTimeValue = 0xffff;

+    }

+  }

+  WriteUnaligned16 (Elapsed, HTONS((UINT16) ElapsedTimeValue));

+}

+

+

+/**

+  Seek the address of the first byte of the option header.

+

+  @param[in]  Buf           The pointer to the buffer.

+  @param[in]  SeekLen       The length to seek.

+  @param[in]  OptType       The option type.

+

+  @retval     NULL          If it failed to seek the option.

+  @retval     others        The position to the option.

+

+**/

+UINT8 *

+Dhcp6SeekOption (

+  IN UINT8           *Buf,

+  IN UINT32          SeekLen,

+  IN UINT16          OptType

+  )

+{

+  UINT8              *Cursor;

+  UINT8              *Option;

+  UINT16             DataLen;

+  UINT16             OpCode;

+

+  Option = NULL;

+  Cursor = Buf;

+

+  //

+  // The format of Dhcp6 option refers to Dhcp6AppendOption().

+  //

+  while (Cursor < Buf + SeekLen) {

+    OpCode = ReadUnaligned16 ((UINT16 *) Cursor);

+    if (OpCode == HTONS (OptType)) {

+      Option = Cursor;

+      break;

+    }

+    DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));

+    Cursor += (DataLen + 4);

+  }

+

+  return Option;

+}

+

+

+/**

+  Seek the address of the first byte of the Ia option header.

+

+  @param[in]  Buf           The pointer to the buffer.

+  @param[in]  SeekLen       The length to seek.

+  @param[in]  IaDesc        The pointer to the Ia descriptor.

+

+  @retval     NULL          If it failed to seek the Ia option.

+  @retval     others        The position to the Ia option.

+

+**/

+UINT8 *

+Dhcp6SeekIaOption (

+  IN UINT8                    *Buf,

+  IN UINT32                   SeekLen,

+  IN EFI_DHCP6_IA_DESCRIPTOR  *IaDesc

+  )

+{

+  UINT8              *Cursor;

+  UINT8              *Option;

+  UINT16             DataLen;

+  UINT16             OpCode;

+  UINT32             IaId;

+

+  //

+  // The format of IA_NA and IA_TA option refers to Dhcp6AppendIaOption().

+  //

+  Option = NULL;

+  Cursor = Buf;

+

+  while (Cursor < Buf + SeekLen) {

+    OpCode = ReadUnaligned16 ((UINT16 *) Cursor);

+    IaId   = ReadUnaligned32 ((UINT32 *) (Cursor + 4));

+    if (OpCode == HTONS (IaDesc->Type) && IaId == HTONL (IaDesc->IaId)) {

+      Option = Cursor;

+      break;

+    }

+    DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));

+    Cursor += (DataLen + 4);

+  }

+

+  return Option;

+}

+

+

+/**

+  Parse the address option and update the address infomation.

+

+  @param[in]      IaInnerOpt    The pointer to the buffer.

+  @param[in]      IaInnerLen    The length to parse.

+  @param[out]     AddrNum       The number of addresses.

+  @param[in, out] AddrBuf       The pointer to the address buffer.

+

+**/

+VOID

+Dhcp6ParseAddrOption (

+  IN     UINT8                   *IaInnerOpt,

+  IN     UINT16                  IaInnerLen,

+     OUT UINT32                  *AddrNum,

+  IN OUT EFI_DHCP6_IA_ADDRESS    *AddrBuf

+  )

+{

+  UINT8                       *Cursor;

+  UINT16                      DataLen;

+  UINT16                      OpCode;

+  UINT32                      ValidLt;

+

+  //

+  //  The format of the IA Address option:

+  //

+  //     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

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |          OPTION_IAADDR        |          option-len           |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                                                               |

+  //    |                         IPv6 address                          |

+  //    |                                                               |

+  //    |                                                               |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                      preferred-lifetime                       |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    |                        valid-lifetime                         |

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //    .                                                               .

+  //    .                        IAaddr-options                         .

+  //    .                                                               .

+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  //

+  //  Two usage model:

+  //

+  //    1. Pass addrbuf == null, to get the addrnum over the Ia inner options.

+  //    2. Pass addrbuf != null, to resolve the addresses over the Ia inner

+  //       options to the addrbuf.

+  //

+

+  Cursor   = IaInnerOpt;

+  *AddrNum = 0;

+

+  while (Cursor < IaInnerOpt + IaInnerLen) {

+    //

+    // Count the Ia address option with non-0 valid time.

+    //

+    OpCode  = ReadUnaligned16 ((UINT16 *) Cursor);

+    ValidLt = ReadUnaligned32 ((UINT32 *) (Cursor + 24));

+    if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt != 0) {

+

+      if (AddrBuf != NULL) {

+        CopyMem (AddrBuf, Cursor + 4, sizeof (EFI_DHCP6_IA_ADDRESS));

+        AddrBuf->PreferredLifetime = NTOHL (AddrBuf->PreferredLifetime);

+        AddrBuf->ValidLifetime     = NTOHL (AddrBuf->ValidLifetime);

+        AddrBuf = (EFI_DHCP6_IA_ADDRESS *) ((UINT8 *) AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS));

+      }

+

+      (*AddrNum)++;

+    }

+    DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));

+    Cursor += (DataLen + 4);

+  }

+}

+

+

+/**

+  Create a control blcok for the Ia according to the corresponding options.

+

+  @param[in]  Instance              The pointer to DHCP6 Instance.

+  @param[in]  IaInnerOpt            The pointer to the inner options in the Ia option.

+  @param[in]  IaInnerLen            The length of all the inner options in the Ia option.

+  @param[in]  T1                    T1 time in the Ia option.

+  @param[in]  T2                    T2 time in the Ia option.

+

+  @retval     EFI_NOT_FOUND         No valid IA option is found.

+  @retval     EFI_SUCCESS           Create an IA control block successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+

+**/

+EFI_STATUS

+Dhcp6GenerateIaCb (

+  IN  DHCP6_INSTANCE           *Instance,

+  IN  UINT8                    *IaInnerOpt,

+  IN  UINT16                   IaInnerLen,

+  IN  UINT32                   T1,

+  IN  UINT32                   T2

+  )

+{

+  UINT32                       AddrNum;

+  UINT32                       IaSize;

+  EFI_DHCP6_IA                 *Ia;

+

+  if (Instance->IaCb.Ia == NULL) {

+    return EFI_NOT_FOUND;

+  }

+

+  //

+  // Calculate the number of addresses for this Ia, excluding the addresses with

+  // the value 0 of valid lifetime.

+  //

+  Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, NULL);

+

+  if (AddrNum == 0) {

+    return EFI_NOT_FOUND;

+  }

+

+  //

+  // Allocate for new IA.

+  //

+  IaSize = sizeof (EFI_DHCP6_IA) + (AddrNum - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);

+  Ia = AllocateZeroPool (IaSize);

+

+  if (Ia == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Fill up this new IA fields.

+  //

+  Ia->State          = Instance->IaCb.Ia->State;

+  Ia->IaAddressCount = AddrNum;

+  CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR));

+  Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress);

+

+  //

+  // Free original IA resource.

+  //

+  if (Instance->IaCb.Ia->ReplyPacket != NULL) {

+    FreePool (Instance->IaCb.Ia->ReplyPacket);

+  }

+  FreePool (Instance->IaCb.Ia);

+

+

+  ZeroMem (&Instance->IaCb, sizeof (DHCP6_IA_CB));

+

+  //

+  // Update IaCb to use new IA.

+  //

+  Instance->IaCb.Ia   = Ia;

+

+  //

+

+ // Fill in IaCb fields. Such as T1, T2, AllExpireTime and LeaseTime.

+  //

+  Instance->IaCb.T1 = T1;

+  Instance->IaCb.T2 = T2;

+  Dhcp6CalculateLeaseTime (&Instance->IaCb);

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Cache the current IA configuration information.

+

+  @param[in] Instance           The pointer to DHCP6 Instance.

+

+  @retval EFI_SUCCESS           Cache the current IA successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+

+**/

+EFI_STATUS

+Dhcp6CacheIa (

+  IN DHCP6_INSTANCE           *Instance

+  )

+{

+  UINTN                        IaSize;

+  EFI_DHCP6_IA                 *Ia;

+

+  Ia = Instance->IaCb.Ia;

+

+  if ((Instance->CacheIa == NULL) && (Ia != NULL)) {

+    //

+    // Cache the current IA.

+    //

+    IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);

+

+    Instance->CacheIa = AllocateZeroPool (IaSize);

+    if (Instance->CacheIa == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+    CopyMem (Instance->CacheIa, Ia, IaSize);

+  }

+  return EFI_SUCCESS;

+}

+

+/**

+  Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.

+

+  @param[in]  Instance            The pointer to DHCP6 instance.

+

+**/

+VOID

+Dhcp6AppendCacheIa (

+  IN DHCP6_INSTANCE           *Instance

+  )

+{

+  UINT8                        *Ptr;

+  UINTN                        Index;

+  UINTN                        IaSize;

+  UINTN                        NewIaSize;

+  EFI_DHCP6_IA                 *Ia;

+  EFI_DHCP6_IA                 *NewIa;

+  EFI_DHCP6_IA                 *CacheIa;

+

+  Ia      = Instance->IaCb.Ia;

+  CacheIa = Instance->CacheIa;

+

+  if ((CacheIa != NULL) && (CacheIa->IaAddressCount != 0)) {

+    //

+    // There are old addresses existing. Merge with current addresses.

+    //

+    NewIaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount + CacheIa->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);

+    NewIa     = AllocateZeroPool (NewIaSize);

+    if (NewIa == NULL) {

+      return;

+    }

+

+    IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);

+    CopyMem (NewIa, Ia, IaSize);

+

+    //

+    // Clear old address.ValidLifetime

+    //

+    for (Index = 0; Index < CacheIa->IaAddressCount; Index++) {

+      CacheIa->IaAddress[Index].ValidLifetime  = 0;

+    }

+

+    NewIa->IaAddressCount += CacheIa->IaAddressCount;

+    Ptr   = (UINT8*)&NewIa->IaAddress[Ia->IaAddressCount];

+    CopyMem (Ptr, CacheIa->IaAddress, CacheIa->IaAddressCount * sizeof (EFI_DHCP6_IA_ADDRESS));

+

+    //

+    // Migrate to the NewIa and free previous.

+    //

+    FreePool (Instance->CacheIa);

+    FreePool (Instance->IaCb.Ia);

+    Instance->CacheIa  = NULL;

+    Instance->IaCb.Ia  = NewIa;

+  }

+}

diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h
new file mode 100644
index 0000000..62985a3
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h
@@ -0,0 +1,340 @@
+/** @file

+  Dhcp6 support functions declaration.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_DHCP6_UTILITY_H__

+#define __EFI_DHCP6_UTILITY_H__

+

+

+#define  DHCP6_10_BIT_MASK     0x3ff

+

+/**

+  Generate client Duid in the format of Duid-llt.

+

+  @param[in]  Mode          The pointer to the mode of SNP.

+

+  @retval     NULL          if failed to generate client Id.

+  @retval     Others        The pointer to the new client id.

+

+**/

+EFI_DHCP6_DUID *

+Dhcp6GenerateClientId (

+  IN EFI_SIMPLE_NETWORK_MODE    *Mode

+  );

+

+/**

+  Copy the Dhcp6 configure data.

+

+  @param[in]  DstCfg        The pointer to the destination configure data.

+  @param[in]  SorCfg        The pointer to the source configure data.

+

+  @retval EFI_SUCCESS           Copy the content from SorCfg from DstCfg successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+

+**/

+EFI_STATUS

+Dhcp6CopyConfigData (

+  IN EFI_DHCP6_CONFIG_DATA     *DstCfg,

+  IN EFI_DHCP6_CONFIG_DATA     *SorCfg

+  );

+

+/**

+  Clean up the configure data.

+

+  @param[in, out]  CfgData       The pointer to the configure data.

+

+**/

+VOID

+Dhcp6CleanupConfigData (

+  IN OUT EFI_DHCP6_CONFIG_DATA       *CfgData

+  );

+

+/**

+  Clean up the mode data.

+

+  @param[in, out]  ModeData      The pointer to the mode data.

+

+**/

+VOID

+Dhcp6CleanupModeData (

+  IN OUT EFI_DHCP6_MODE_DATA        *ModeData

+  );

+

+/**

+  Calculate the expire time by the algorithm defined in rfc.

+

+  @param[in]  Base          The base value of the time.

+  @param[in]  IsFirstRt     If TRUE, it is the first time to calculate expire time.

+  @param[in]  NeedSigned    If TRUE, the the signed factor is needed.

+

+  @return     Expire        The calculated result for the new expire time.

+

+**/

+UINT32

+Dhcp6CalculateExpireTime (

+  IN UINT32                    Base,

+  IN BOOLEAN                   IsFirstRt,

+  IN BOOLEAN                   NeedSigned

+  );

+

+/**

+  Calculate the lease time by the algorithm defined in rfc.

+

+  @param[in]  IaCb          The pointer to the Ia control block.

+

+**/

+VOID

+Dhcp6CalculateLeaseTime (

+  IN DHCP6_IA_CB               *IaCb

+  );

+

+/**

+  Check whether the addresses are all included by the configured Ia.

+

+  @param[in]  Ia            The pointer to the Ia.

+  @param[in]  AddressCount  The number of addresses.

+  @param[in]  Addresses     The pointer to the addresses buffer.

+

+  @retval EFI_SUCCESS         The addresses are all included by the configured IA.

+  @retval EFI_NOT_FOUND       The addresses are not included by the configured IA.

+

+**/

+EFI_STATUS

+Dhcp6CheckAddress (

+  IN EFI_DHCP6_IA              *Ia,

+  IN UINT32                    AddressCount,

+  IN EFI_IPv6_ADDRESS          *Addresses

+  );

+

+/**

+  Deprive the addresses from current Ia, and generate another eliminated Ia.

+

+  @param[in]  Ia            The pointer to the Ia.

+  @param[in]  AddressCount  The number of addresses.

+  @param[in]  Addresses     The pointer to the addresses buffer.

+

+  @retval     NULL          If failed to generate the deprived Ia.

+  @retval     others        The pointer to the deprived Ia.

+

+**/

+EFI_DHCP6_IA *

+Dhcp6DepriveAddress (

+  IN EFI_DHCP6_IA              *Ia,

+  IN UINT32                    AddressCount,

+  IN EFI_IPv6_ADDRESS          *Addresses

+  );

+

+/**

+  The dummy ext buffer free callback routine.

+

+  @param[in]  Arg           The pointer to the parameter.

+

+**/

+VOID

+EFIAPI

+Dhcp6DummyExtFree (

+  IN VOID                      *Arg

+  );

+

+/**

+  The callback routine once message transmitted.

+

+  @param[in]  Udp6Wrap      The pointer to the received net buffer.

+  @param[in]  EndPoint      The pointer to the udp end point.

+  @param[in]  IoStatus      The return status from udp io.

+  @param[in]  Context       The opaque parameter to the function.

+

+**/

+VOID

+EFIAPI

+Dhcp6OnTransmitted (

+  IN NET_BUF                   *Wrap,

+  IN UDP_END_POINT             *EndPoint,

+  IN EFI_STATUS                IoStatus,

+  IN VOID                      *Context

+  );

+

+/**

+  Append the appointed option to the buf, and move the buf to the end.

+

+  @param[in, out] Buf           The pointer to buffer.

+  @param[in]      OptType       The option type.

+  @param[in]      OptLen        The lenght of option content.s

+  @param[in]      Data          The pointer to the option content.

+

+  @return         Buf           The position to append the next option.

+

+**/

+UINT8 *

+Dhcp6AppendOption (

+  IN OUT UINT8                 *Buf,

+  IN     UINT16                OptType,

+  IN     UINT16                OptLen,

+  IN     UINT8                 *Data

+  );

+

+/**

+  Append the Ia option to Buf, and move Buf to the end.

+

+  @param[in, out] Buf           The pointer to the position to append.

+  @param[in]      Ia            The pointer to the Ia.

+  @param[in]      T1            The time of T1.

+  @param[in]      T2            The time of T2.

+

+  @return         Buf           The position to append the next Ia option.

+

+**/

+UINT8 *

+Dhcp6AppendIaOption (

+  IN OUT UINT8                  *Buf,

+  IN     EFI_DHCP6_IA           *Ia,

+  IN     UINT32                 T1,

+  IN     UINT32                 T2

+  );

+

+/**

+  Append the appointed Elapsed time option to Buf, and move Buf to the end.

+

+  @param[in, out] Buf           The pointer to the position to append.

+  @param[in]      Instance      The pointer to the Dhcp6 instance.

+  @param[out]     Elapsed       The pointer to the elapsed time value in

+                                  the generated packet.

+

+  @return         Buf           The position to append the next Ia option.

+

+**/

+UINT8 *

+Dhcp6AppendETOption (

+  IN OUT UINT8                  *Buf,

+  IN     DHCP6_INSTANCE         *Instance,

+  OUT    UINT16                 **Elapsed

+  );

+

+/**

+  Set the elapsed time based on the given instance and the pointer to the

+  elapsed time option.

+

+  @param[in]      Elapsed       The pointer to the position to append.

+  @param[in]      Instance      The pointer to the Dhcp6 instance.

+**/

+VOID

+SetElapsedTime (

+  IN     UINT16                 *Elapsed,

+  IN     DHCP6_INSTANCE         *Instance

+  );

+

+/**

+  Seek the address of the first byte of the option header.

+

+  @param[in]  Buf           The pointer to buffer.

+  @param[in]  SeekLen       The length to seek.

+  @param[in]  OptType       The option type.

+

+  @retval     NULL          If failed to seek the option.

+  @retval     others        The position to the option.

+

+**/

+UINT8 *

+Dhcp6SeekOption (

+  IN UINT8                     *Buf,

+  IN UINT32                    SeekLen,

+  IN UINT16                    OptType

+  );

+

+/**

+  Seek the address of the first byte of the Ia option header.

+

+  @param[in]  Buf           The pointer to the buffer.

+  @param[in]  SeekLen       The length to seek.

+  @param[in]  IaDesc        The pointer to the Ia descriptor.

+

+  @retval     NULL          If failed to seek the Ia option.

+  @retval     others        The position to the Ia option.

+

+**/

+UINT8 *

+Dhcp6SeekIaOption (

+  IN UINT8                     *Buf,

+  IN UINT32                    SeekLen,

+  IN EFI_DHCP6_IA_DESCRIPTOR   *IaDesc

+  );

+

+/**

+  Parse the address option and update the address info.

+

+  @param[in]      IaInnerOpt    The pointer to the buffer.

+  @param[in]      IaInnerLen    The length to parse.

+  @param[out]     AddrNum       The number of addresses.

+  @param[in, out] AddrBuf       The pointer to the address buffer.

+

+**/

+VOID

+Dhcp6ParseAddrOption (

+  IN     UINT8                   *IaInnerOpt,

+  IN     UINT16                  IaInnerLen,

+     OUT UINT32                  *AddrNum,

+  IN OUT EFI_DHCP6_IA_ADDRESS    *AddrBuf

+  );

+

+/**

+  Create a control blcok for the Ia according to the corresponding options.

+

+  @param[in]  Instance              The pointer to DHCP6 Instance.

+  @param[in]  IaInnerOpt            The pointer to the inner options in the Ia option.

+  @param[in]  IaInnerLen            The length of all the inner options in the Ia option.

+  @param[in]  T1                    T1 time in the Ia option.

+  @param[in]  T2                    T2 time in the Ia option.

+

+  @retval     EFI_NOT_FOUND         No valid IA option is found.

+  @retval     EFI_SUCCESS           Create an IA control block successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+

+**/

+EFI_STATUS

+Dhcp6GenerateIaCb (

+  IN  DHCP6_INSTANCE           *Instance,

+  IN  UINT8                    *IaInnerOpt,

+  IN  UINT16                   IaInnerLen,

+  IN  UINT32                   T1,

+  IN  UINT32                   T2

+  );

+

+

+/**

+  Cache the current IA configuration information.

+

+  @param[in] Instance           The pointer to DHCP6 Instance.

+

+  @retval EFI_SUCCESS           Cache the current IA successfully.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+

+**/

+EFI_STATUS

+Dhcp6CacheIa (

+  IN DHCP6_INSTANCE           *Instance

+  );

+

+

+/**

+  Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.

+

+  @param[in]  Instance            The pointer to DHCP6 instance.

+

+**/

+VOID

+Dhcp6AppendCacheIa (

+  IN DHCP6_INSTANCE           *Instance

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/ComponentName.c b/NetworkPkg/Ip6Dxe/ComponentName.c
new file mode 100644
index 0000000..c8382f7
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/ComponentName.c
@@ -0,0 +1,313 @@
+/** @file

+  Implementation of EFI_COMPONENT_NAME_PROTOCOL and

+  EFI_COMPONENT_NAME2_PROTOCOL protocol.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+//

+// EFI Component Name Functions

+//

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,

+  IN  EFI_HANDLE                                      ControllerHandle,

+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,

+  IN  CHAR8                                           *Language,

+  OUT CHAR16                                          **ControllerName

+  );

+

+//

+// EFI Component Name Protocol.

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL   gIp6ComponentName = {

+  Ip6ComponentNameGetDriverName,

+  Ip6ComponentNameGetControllerName,

+  "eng"

+};

+

+//

+// EFI Component Name 2 Protocol.

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL  gIp6ComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ip6ComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ip6ComponentNameGetControllerName,

+  "en"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE      mIp6DriverNameTable[] = {

+  {

+    "eng;en",

+    L"IP6 Network Service Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+          Language,

+          This->SupportedLanguages,

+          mIp6DriverNameTable,

+          DriverName,

+          (BOOLEAN) (This == &gIp6ComponentName)

+          );

+

+}

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,

+  IN  EFI_HANDLE                                      ControllerHandle,

+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,

+  IN  CHAR8                                           *Language,

+  OUT CHAR16                                          **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

diff --git a/NetworkPkg/Ip6Dxe/Ip6Common.c b/NetworkPkg/Ip6Dxe/Ip6Common.c
new file mode 100644
index 0000000..2ae14a9
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Common.c
@@ -0,0 +1,796 @@
+/** @file

+  The implementation of common functions shared by IP6 driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+/**

+  Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number

+  of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL,

+  only the address count is returned.

+

+  @param[in]  IpSb              The IP6 service binding instance.

+  @param[out] AddressCount      The number of returned addresses.

+  @param[out] AddressList       The pointer to the array of EFI_IP6_ADDRESS_INFO.

+                                This is an optional parameter.

+

+

+  @retval EFI_SUCCESS           The address array successfully built.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the address info.

+  @retval EFI_INVALID_PARAMETER Any input parameter is invalid.

+

+**/

+EFI_STATUS

+Ip6BuildEfiAddressList (

+  IN IP6_SERVICE            *IpSb,

+  OUT UINT32                *AddressCount,

+  OUT EFI_IP6_ADDRESS_INFO  **AddressList OPTIONAL

+  )

+{

+  UINT32                Count;

+  LIST_ENTRY            *Entry;

+  EFI_IP6_ADDRESS_INFO  *EfiAddrInfo;

+  IP6_ADDRESS_INFO      *AddrInfo;

+

+  if (AddressCount == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (IpSb->LinkLocalOk) {

+    Count = 1 + IpSb->DefaultInterface->AddressCount;

+  } else {

+    Count = 0;

+  }

+

+  *AddressCount = Count;

+

+  if ((AddressList == NULL) || (Count == 0)) {

+    return EFI_SUCCESS;

+  }

+

+  if (*AddressList == NULL) {

+    *AddressList = AllocatePool (sizeof (EFI_IP6_ADDRESS_INFO) * Count);

+    if (*AddressList == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  }

+

+  EfiAddrInfo = *AddressList;

+

+  IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &IpSb->LinkLocalAddr);

+  EfiAddrInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;

+

+  EfiAddrInfo++;

+  Count = 1;

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->DefaultInterface->AddressList) {

+    AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);

+

+    IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &AddrInfo->Address);

+    EfiAddrInfo->PrefixLength = AddrInfo->PrefixLength;

+

+    EfiAddrInfo++;

+    Count++;

+  }

+

+  ASSERT (Count == *AddressCount);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Generate the multicast addresses identify the group of all IPv6 nodes or IPv6

+  routers defined in RFC4291.

+

+  All Nodes Addresses: FF01::1, FF02::1.

+  All Router Addresses: FF01::2, FF02::2, FF05::2.

+

+  @param[in]  Router            If TRUE, generate all routers addresses,

+                                else generate all node addresses.

+  @param[in]  Scope             interface-local(1), link-local(2), or site-local(5)

+  @param[out] Ip6Addr           The generated multicast address.

+

+  @retval EFI_INVALID_PARAMETER Any input parameter is invalid.

+  @retval EFI_SUCCESS           The address is generated.

+

+**/

+EFI_STATUS

+Ip6SetToAllNodeMulticast (

+  IN  BOOLEAN          Router,

+  IN  UINT8            Scope,

+  OUT EFI_IPv6_ADDRESS *Ip6Addr

+  )

+{

+  if (Ip6Addr == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (!Router && Scope == IP6_SITE_LOCAL_SCOPE) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  ZeroMem (Ip6Addr, sizeof (EFI_IPv6_ADDRESS));

+  Ip6Addr->Addr[0] = 0xFF;

+  Ip6Addr->Addr[1] = Scope;

+

+  if (!Router) {

+    Ip6Addr->Addr[15] = 0x1;

+  } else {

+    Ip6Addr->Addr[15] = 0x2;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function converts MAC address to 64 bits interface ID according to RFC4291

+  and returns the interface ID. Currently only 48-bit MAC address is supported by

+  this function.

+

+  @param[in, out]  IpSb      The IP6 service binding instance.

+

+  @retval          NULL      The operation fails.

+  @return                    Pointer to the generated interface ID.

+

+**/

+UINT8 *

+Ip6CreateInterfaceID (

+  IN OUT IP6_SERVICE         *IpSb

+  )

+{

+  UINT8                      InterfaceId[8];

+  UINT8                      Byte;

+  EFI_MAC_ADDRESS            *MacAddr;

+  UINT32                     AddrLen;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  AddrLen = IpSb->SnpMode.HwAddressSize;

+

+  //

+  // Currently only IEEE 802 48-bit MACs are supported to create link local address.

+  //

+  if (AddrLen != IP6_MAC_LEN || IpSb->InterfaceIdLen != IP6_IF_ID_LEN) {

+    return NULL;

+  }

+

+  MacAddr = &IpSb->SnpMode.CurrentAddress;

+

+  //

+  // Convert MAC address to 64 bits interface ID according to Appendix A of RFC4291:

+  // 1. Insert 0xFFFE to the middle

+  // 2. Invert the universal/local bit - bit 6 in network order

+  //

+  CopyMem (InterfaceId, MacAddr, 3);

+  InterfaceId[3] = 0xFF;

+  InterfaceId[4] = 0xFE;

+  CopyMem (&InterfaceId[5], &MacAddr->Addr[3], 3);

+

+  Byte = (UINT8) (InterfaceId[0] & IP6_U_BIT);

+  if (Byte == IP6_U_BIT) {

+    InterfaceId[0] &= ~IP6_U_BIT;

+  } else {

+    InterfaceId[0] |= IP6_U_BIT;

+  }

+

+  //

+  // Return the interface ID.

+  //

+  return AllocateCopyPool (IpSb->InterfaceIdLen, InterfaceId);

+}

+

+/**

+  This function creates link-local address from interface identifier. The

+  interface identifier is normally created from MAC address. It might be manually

+  configured by administrator if the link-local address created from MAC address

+  is a duplicate address.

+

+  @param[in, out]  IpSb      The IP6 service binding instance.

+

+  @retval          NULL      If the operation fails.

+  @return                    The generated Link Local address, in network order.

+

+**/

+EFI_IPv6_ADDRESS *

+Ip6CreateLinkLocalAddr (

+  IN OUT IP6_SERVICE           *IpSb

+  )

+{

+  EFI_IPv6_ADDRESS             *Ip6Addr;

+  EFI_IP6_CONFIG_PROTOCOL      *Ip6Config;

+  UINTN                        DataSize;

+  EFI_IP6_CONFIG_INTERFACE_ID  InterfaceId;

+  EFI_STATUS                   Status;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  if (IpSb->InterfaceId != NULL) {

+    FreePool (IpSb->InterfaceId);

+  }

+

+  //

+  // Get the interface id if it is manully configured.

+  //

+  Ip6Config = &IpSb->Ip6ConfigInstance.Ip6Config;

+  DataSize  = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);

+  ZeroMem (&InterfaceId, DataSize);

+

+  Status = Ip6Config->GetData (

+                        Ip6Config,

+                        Ip6ConfigDataTypeAltInterfaceId,

+                        &DataSize,

+                        &InterfaceId

+                        );

+  if (Status == EFI_NOT_FOUND) {

+    //

+    // Since the interface id is not configured, generate the interface id from

+    // MAC address.

+    //

+    IpSb->InterfaceId = Ip6CreateInterfaceID (IpSb);

+    if (IpSb->InterfaceId == NULL) {

+      return NULL;

+    }

+

+    CopyMem (&InterfaceId, IpSb->InterfaceId, IpSb->InterfaceIdLen);

+    //

+    // Record the interface id.

+    //

+    Status = Ip6Config->SetData (

+                          Ip6Config,

+                          Ip6ConfigDataTypeAltInterfaceId,

+                          DataSize,

+                          &InterfaceId

+                          );

+    if (EFI_ERROR (Status)) {

+      FreePool (IpSb->InterfaceId);

+      IpSb->InterfaceId = NULL;

+      return NULL;

+    }

+  } else if (!EFI_ERROR (Status)) {

+    IpSb->InterfaceId = AllocateCopyPool (DataSize, &InterfaceId);

+    if (IpSb->InterfaceId == NULL) {

+      return NULL;

+    }

+  } else {

+    return NULL;

+  }

+

+  //

+  // Append FE80::/64 to the left of IPv6 address then return.

+  //

+  Ip6Addr = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS));

+  if (Ip6Addr == NULL) {

+    FreePool (IpSb->InterfaceId);

+    IpSb->InterfaceId = NULL;

+    return NULL;

+  }

+

+  CopyMem (&Ip6Addr->Addr[8], IpSb->InterfaceId, IpSb->InterfaceIdLen);

+  Ip6Addr->Addr[1] = 0x80;

+  Ip6Addr->Addr[0] = 0xFE;

+

+  return Ip6Addr;

+}

+

+/**

+  Compute the solicited-node multicast address for an unicast or anycast address,

+  by taking the low-order 24 bits of this address, and appending those bits to

+  the prefix FF02:0:0:0:0:1:FF00::/104.

+

+  @param[in]  Ip6Addr               The unicast or anycast address, in network order.

+  @param[out] MulticastAddr         The generated solicited-node multicast address,

+                                    in network order.

+

+**/

+VOID

+Ip6CreateSNMulticastAddr (

+  IN EFI_IPv6_ADDRESS  *Ip6Addr,

+  OUT EFI_IPv6_ADDRESS *MulticastAddr

+  )

+{

+  ASSERT (Ip6Addr != NULL && MulticastAddr != NULL);

+

+  ZeroMem (MulticastAddr, sizeof (EFI_IPv6_ADDRESS));

+

+  MulticastAddr->Addr[0]  = 0xFF;

+  MulticastAddr->Addr[1]  = 0x02;

+  MulticastAddr->Addr[11] = 0x1;

+  MulticastAddr->Addr[12] = 0xFF;

+

+  CopyMem (&MulticastAddr->Addr[13], &Ip6Addr->Addr[13], 3);

+}

+

+/**

+  Insert a node IP6_ADDRESS_INFO to an IP6 interface.

+

+  @param[in, out]  IpIf             Points to an IP6 interface.

+  @param[in]       AddrInfo         Points to IP6_ADDRESS_INFO

+

+**/

+VOID

+Ip6AddAddr (

+  IN OUT IP6_INTERFACE *IpIf,

+  IN IP6_ADDRESS_INFO  *AddrInfo

+  )

+{

+  InsertHeadList (&IpIf->AddressList, &AddrInfo->Link);

+  IpIf->AddressCount++;

+}

+

+/**

+  Destroy the IP instance if its StationAddress is removed. It is the help function

+  for Ip6RemoveAddr().

+

+  @param[in, out]  IpSb             Points to an IP6 service binding instance.

+  @param[in]       Address          The to be removed address

+

+**/

+VOID

+Ip6DestroyInstanceByAddress (

+  IN OUT IP6_SERVICE   *IpSb,

+  IN EFI_IPv6_ADDRESS  *Address

+  )

+{

+  BOOLEAN                       OneDestroyed;

+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;

+  LIST_ENTRY                    *Entry;

+  IP6_PROTOCOL                  *Instance;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  ServiceBinding = &IpSb->ServiceBinding;

+

+  //

+  // Upper layer IP protocol consumers may have tight relationship between several

+  // IP protocol instances, in other words, calling ServiceBinding->DestroyChild to

+  // destroy one IP child may cause other related IP children destroyed too. This

+  // will probably leave hole in the children list when we iterate it. So everytime

+  // we just destroy one child then back to the start point to iterate the list.

+  //

+  do {

+    OneDestroyed = FALSE;

+

+    NET_LIST_FOR_EACH (Entry, &IpSb->Children) {

+      Instance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);

+

+      if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, Address)) {

+        ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);

+        OneDestroyed = TRUE;

+        break;

+      }

+    }

+  } while (OneDestroyed);

+}

+

+/**

+  Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO.

+

+  This function removes the matching IPv6 addresses from the address list and

+  adjusts the address count of the address list. If IpSb is not NULL, this function

+  calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the

+  its solicited-node multicast MAC address from the filter list and sends out

+  a Multicast Listener Done. If Prefix is NULL, all address in the address list

+  will be removed. If Prefix is not NULL, the address that matching the Prefix

+  with PrefixLength in the address list will be removed.

+

+  @param[in]       IpSb             NULL or points to IP6 service binding instance.

+  @param[in, out]  AddressList      Address list array.

+  @param[in, out]  AddressCount     The count of addresses in address list array.

+  @param[in]       Prefix           NULL or an IPv6 address prefix.

+  @param[in]       PrefixLength     The length of Prefix.

+

+  @retval    EFI_SUCCESS            The operation completed successfully.

+  @retval    EFI_NOT_FOUND          The address matching the Prefix with PrefixLength

+                                    cannot be found in the address list.

+  @retval    EFI_INVALID_PARAMETER  Any input parameter is invalid.

+

+**/

+EFI_STATUS

+Ip6RemoveAddr (

+  IN IP6_SERVICE       *IpSb          OPTIONAL,

+  IN OUT LIST_ENTRY    *AddressList,

+  IN OUT UINT32        *AddressCount,

+  IN EFI_IPv6_ADDRESS  *Prefix        OPTIONAL,

+  IN UINT8             PrefixLength

+  )

+{

+  EFI_STATUS           Status;

+  LIST_ENTRY           *Entry;

+  LIST_ENTRY           *Next;

+  IP6_ADDRESS_INFO     *AddrInfo;

+  EFI_IPv6_ADDRESS     SnMCastAddr;

+

+  if (IsListEmpty (AddressList) || *AddressCount < 1 || PrefixLength > IP6_PREFIX_NUM) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = EFI_NOT_FOUND;

+

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, AddressList) {

+    AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);

+

+    if (Prefix == NULL ||

+        (PrefixLength == 128 && EFI_IP6_EQUAL (Prefix, &AddrInfo->Address)) ||

+        (PrefixLength == AddrInfo->PrefixLength && NetIp6IsNetEqual (Prefix, &AddrInfo->Address, PrefixLength))

+        ) {

+      if (IpSb != NULL) {

+        NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+        Ip6CreateSNMulticastAddr (&AddrInfo->Address, &SnMCastAddr);

+        Ip6LeaveGroup (IpSb, &SnMCastAddr);

+

+        //

+        // Destroy any instance who is using the dying address as the source address.

+        //

+        Ip6DestroyInstanceByAddress (IpSb, &AddrInfo->Address);

+      }

+

+      RemoveEntryList (Entry);

+      FreePool (AddrInfo);

+      (*AddressCount)--;

+

+      Status = EFI_SUCCESS;

+    }

+  }

+

+  return Status;

+}

+

+/**

+  Check whether the incoming Ipv6 address is a solicited-node multicast address.

+

+  @param[in]  Ip6               Ip6 address, in network order.

+

+  @retval TRUE                  Yes, solicited-node multicast address

+  @retval FALSE                 No

+

+**/

+BOOLEAN

+Ip6IsSNMulticastAddr (

+  IN EFI_IPv6_ADDRESS *Ip6

+  )

+{

+  EFI_IPv6_ADDRESS    Sn;

+  BOOLEAN             Flag;

+

+  Ip6CreateSNMulticastAddr (Ip6, &Sn);

+  Flag = FALSE;

+

+  if (CompareMem (Sn.Addr, Ip6->Addr, 13) == 0) {

+    Flag = TRUE;

+  }

+

+  return Flag;

+}

+

+/**

+  Check whether the incoming IPv6 address is one of the maintained addresses in

+  the IP6 service binding instance.

+

+  @param[in]  IpSb              Points to a IP6 service binding instance.

+  @param[in]  Address           The IP6 address to be checked.

+  @param[out] Interface         If not NULL, output the IP6 interface which

+                                maintains the Address.

+  @param[out] AddressInfo       If not NULL, output the IP6 address information

+                                of the Address.

+

+  @retval TRUE                  Yes, it is one of the maintained address.

+  @retval FALSE                 No, it is not one of the maintained address.

+

+**/

+BOOLEAN

+Ip6IsOneOfSetAddress (

+  IN  IP6_SERVICE           *IpSb,

+  IN  EFI_IPv6_ADDRESS      *Address,

+  OUT IP6_INTERFACE         **Interface   OPTIONAL,

+  OUT IP6_ADDRESS_INFO      **AddressInfo OPTIONAL

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Entry2;

+  IP6_INTERFACE             *IpIf;

+  IP6_ADDRESS_INFO          *TmpAddressInfo;

+

+  //

+  // Check link-local address first

+  //

+  if (IpSb->LinkLocalOk && EFI_IP6_EQUAL (&IpSb->LinkLocalAddr, Address)) {

+    if (Interface != NULL) {

+      *Interface = IpSb->DefaultInterface;

+    }

+

+    if (AddressInfo != NULL) {

+      *AddressInfo = NULL;

+    }

+

+    return TRUE;

+  }

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+    IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);

+

+    NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {

+      TmpAddressInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);

+

+      if (EFI_IP6_EQUAL (&TmpAddressInfo->Address, Address)) {

+        if (Interface != NULL) {

+          *Interface = IpIf;

+        }

+

+        if (AddressInfo != NULL) {

+          *AddressInfo = TmpAddressInfo;

+        }

+

+        return TRUE;

+      }

+    }

+  }

+

+  return FALSE;

+}

+

+/**

+  Check whether the incoming MAC address is valid.

+

+  @param[in]  IpSb              Points to a IP6 service binding instance.

+  @param[in]  LinkAddress       The MAC address.

+

+  @retval TRUE                  Yes, it is valid.

+  @retval FALSE                 No, it is not valid.

+

+**/

+BOOLEAN

+Ip6IsValidLinkAddress (

+  IN  IP6_SERVICE      *IpSb,

+  IN  EFI_MAC_ADDRESS  *LinkAddress

+  )

+{

+  UINT32               Index;

+

+  //

+  // TODO: might be updated later to be more acceptable.

+  //

+  for (Index = IpSb->SnpMode.HwAddressSize; Index < sizeof (EFI_MAC_ADDRESS); Index++) {

+    if (LinkAddress->Addr[Index] != 0) {

+      return FALSE;

+    }

+  }

+

+  return TRUE;

+}

+

+/**

+  Copy the PrefixLength bits from Src to Dest.

+

+  @param[out] Dest              A pointer to the buffer to copy to.

+  @param[in]  Src               A pointer to the buffer to copy from.

+  @param[in]  PrefixLength      The number of bits to copy.

+

+**/

+VOID

+Ip6CopyAddressByPrefix (

+  OUT EFI_IPv6_ADDRESS *Dest,

+  IN  EFI_IPv6_ADDRESS *Src,

+  IN  UINT8            PrefixLength

+  )

+{

+  UINT8 Byte;

+  UINT8 Bit;

+  UINT8 Mask;

+

+  ASSERT (Dest != NULL && Src != NULL);

+  ASSERT (PrefixLength < IP6_PREFIX_NUM);

+

+  Byte = (UINT8) (PrefixLength / 8);

+  Bit  = (UINT8) (PrefixLength % 8);

+

+  ZeroMem (Dest, sizeof (EFI_IPv6_ADDRESS));

+

+  CopyMem (Dest, Src, Byte);

+

+  if (Bit > 0) {

+    Mask = (UINT8) (0xFF << (8 - Bit));

+    ASSERT (Byte < 16);

+    Dest->Addr[Byte] = (UINT8) (Src->Addr[Byte] & Mask);

+  }

+}

+

+/**

+  Get the MAC address for a multicast IP address. Call

+  Mnp's McastIpToMac to find the MAC address instead of

+  hard-coding the NIC to be Ethernet.

+

+  @param[in]  Mnp                   The Mnp instance to get the MAC address.

+  @param[in]  Multicast             The multicast IP address to translate.

+  @param[out] Mac                   The buffer to hold the translated address.

+

+  @retval EFI_SUCCESS               The multicast IP successfully

+                                    translated to a multicast MAC address.

+  @retval Other                     The address is not converted because an error occurred.

+

+**/

+EFI_STATUS

+Ip6GetMulticastMac (

+  IN  EFI_MANAGED_NETWORK_PROTOCOL *Mnp,

+  IN  EFI_IPv6_ADDRESS             *Multicast,

+  OUT EFI_MAC_ADDRESS              *Mac

+  )

+{

+  EFI_IP_ADDRESS        EfiIp;

+

+  IP6_COPY_ADDRESS (&EfiIp.v6, Multicast);

+

+  return Mnp->McastIpToMac (Mnp, TRUE, &EfiIp, Mac);

+}

+

+/**

+  Set the Ip6 variable data.

+

+  @param[in]  IpSb              Points to an IP6 service binding instance.

+

+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.

+  @retval other                 Set variable failed.

+

+**/

+EFI_STATUS

+Ip6SetVariableData (

+  IN IP6_SERVICE  *IpSb

+  )

+{

+  UINT32                 NumConfiguredInstance;

+  LIST_ENTRY             *Entry;

+  UINTN                  VariableDataSize;

+  EFI_IP6_VARIABLE_DATA  *Ip6VariableData;

+  EFI_IP6_ADDRESS_PAIR   *Ip6AddressPair;

+  IP6_PROTOCOL           *IpInstance;

+  CHAR16                 *NewMacString;

+  EFI_STATUS             Status;

+

+  NumConfiguredInstance = 0;

+

+  //

+  // Go through the children list to count the configured children.

+  //

+  NET_LIST_FOR_EACH (Entry, &IpSb->Children) {

+    IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);

+

+    if (IpInstance->State == IP6_STATE_CONFIGED) {

+      NumConfiguredInstance++;

+    }

+  }

+

+  //

+  // Calculate the size of the Ip6VariableData. As there may be no IP child,

+  // we should add extra buffer for the address paris only if the number of configured

+  // children is more than 1.

+  //

+  VariableDataSize = sizeof (EFI_IP6_VARIABLE_DATA);

+

+  if (NumConfiguredInstance > 1) {

+    VariableDataSize += sizeof (EFI_IP6_ADDRESS_PAIR) * (NumConfiguredInstance - 1);

+  }

+

+  Ip6VariableData = AllocatePool (VariableDataSize);

+  if (Ip6VariableData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Ip6VariableData->DriverHandle = IpSb->Image;

+  Ip6VariableData->AddressCount = NumConfiguredInstance;

+

+  Ip6AddressPair                = &Ip6VariableData->AddressPairs[0];

+

+  //

+  // Go through the children list to fill the configured children's address pairs.

+  //

+  NET_LIST_FOR_EACH (Entry, &IpSb->Children) {

+    IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);

+

+    if (IpInstance->State == IP6_STATE_CONFIGED) {

+      Ip6AddressPair->InstanceHandle = IpInstance->Handle;

+      Ip6AddressPair->PrefixLength   = IpInstance->PrefixLength;

+      IP6_COPY_ADDRESS (&Ip6AddressPair->Ip6Address, &IpInstance->ConfigData.StationAddress);

+

+      Ip6AddressPair++;

+    }

+  }

+

+  //

+  // Get the mac string.

+  //

+  Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &NewMacString);

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  if (IpSb->MacString != NULL) {

+    //

+    // The variable is set already, we're going to update it.

+    //

+    if (StrCmp (IpSb->MacString, NewMacString) != 0) {

+      //

+      // The mac address is changed, delete the previous variable first.

+      //

+      gRT->SetVariable (

+             IpSb->MacString,

+             &gEfiIp6ServiceBindingProtocolGuid,

+             EFI_VARIABLE_BOOTSERVICE_ACCESS,

+             0,

+             NULL

+             );

+    }

+

+    FreePool (IpSb->MacString);

+  }

+

+  IpSb->MacString = NewMacString;

+

+  Status = gRT->SetVariable (

+                  IpSb->MacString,

+                  &gEfiIp6ServiceBindingProtocolGuid,

+                  EFI_VARIABLE_BOOTSERVICE_ACCESS,

+                  VariableDataSize,

+                  (VOID *) Ip6VariableData

+                  );

+

+Exit:

+  FreePool (Ip6VariableData);

+  return Status;

+}

+

+/**

+  Clear the variable and free the resource.

+

+  @param[in]  IpSb                  Ip6 service binding instance.

+

+**/

+VOID

+Ip6ClearVariableData (

+  IN IP6_SERVICE  *IpSb

+  )

+{

+  ASSERT (IpSb->MacString != NULL);

+

+  gRT->SetVariable (

+         IpSb->MacString,

+         &gEfiIp6ServiceBindingProtocolGuid,

+         EFI_VARIABLE_BOOTSERVICE_ACCESS,

+         0,

+         NULL

+         );

+

+  FreePool (IpSb->MacString);

+  IpSb->MacString = NULL;

+}

+

+/**

+  Convert the multibyte field in IP header's byter order.

+  In spite of its name, it can also be used to convert from

+  host to network byte order.

+

+  @param[in, out]  Head                  The IP head to convert.

+

+  @return Point to the converted IP head.

+

+**/

+EFI_IP6_HEADER *

+Ip6NtohHead (

+  IN OUT EFI_IP6_HEADER *Head

+  )

+{

+  Head->FlowLabelL    = NTOHS (Head->FlowLabelL);

+  Head->PayloadLength = NTOHS (Head->PayloadLength);

+

+  return Head;

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Common.h b/NetworkPkg/Ip6Dxe/Ip6Common.h
new file mode 100644
index 0000000..c3755f4
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Common.h
@@ -0,0 +1,338 @@
+/** @file

+  Common definition and functions for IP6 driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_COMMON_H__

+#define __EFI_IP6_COMMON_H__

+

+#define IP6_LINK_EQUAL(Mac1, Mac2) (CompareMem ((Mac1), (Mac2), sizeof (EFI_MAC_ADDRESS)) == 0)

+

+//

+// Convert the Microsecond to second. IP transmit/receive time is

+// in the unit of microsecond. IP ticks once per second.

+//

+#define IP6_US_TO_SEC(Us)              (((Us) + 999999) / 1000000)

+

+#define IP6_ETHER_PROTO                0x86DD

+

+#define IP6_MAC_LEN                    6

+#define IP6_IF_ID_LEN                  8

+

+#define IP6_INTERFACE_LOCAL_SCOPE      1

+#define IP6_LINK_LOCAL_SCOPE           2

+#define IP6_SITE_LOCAL_SCOPE           5

+

+#define IP6_INFINIT_LIFETIME           0xFFFFFFFF

+

+#define IP6_HOP_LIMIT                  255

+//

+// Make it to 64 since all 54 bits are zero.

+//

+#define IP6_LINK_LOCAL_PREFIX_LENGTH   64

+

+#define IP6_TIMER_INTERVAL_IN_MS       100

+#define IP6_ONE_SECOND_IN_MS           1000

+

+//

+// The packet is received as link level broadcast/multicast/promiscuous.

+//

+#define IP6_LINK_BROADCAST             0x00000001

+#define IP6_LINK_MULTICAST             0x00000002

+#define IP6_LINK_PROMISC               0x00000004

+

+#define IP6_U_BIT                      0x02

+

+typedef enum {

+  Ip6Promiscuous                     = 1,

+  Ip6Unicast,

+  Ip6Multicast,

+  Ip6AnyCast

+} IP6_ADDRESS_TYPE;

+

+typedef struct _IP6_INTERFACE    IP6_INTERFACE;

+typedef struct _IP6_PROTOCOL     IP6_PROTOCOL;

+typedef struct _IP6_SERVICE      IP6_SERVICE;

+typedef struct _IP6_ADDRESS_INFO IP6_ADDRESS_INFO;

+

+/**

+  Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number

+  of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL,

+  only the address count is returned.

+

+  @param[in]  IpSb              The IP6 service binding instance.

+  @param[out] AddressCount      The number of returned addresses.

+  @param[out] AddressList       The pointer to the array of EFI_IP6_ADDRESS_INFO.

+                                This is an optional parameter.

+

+

+  @retval EFI_SUCCESS           The address array is successfully build

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the address info.

+  @retval EFI_INVALID_PARAMETER Any input parameter is invalid.

+

+**/

+EFI_STATUS

+Ip6BuildEfiAddressList (

+  IN IP6_SERVICE            *IpSb,

+  OUT UINT32                *AddressCount,

+  OUT EFI_IP6_ADDRESS_INFO  **AddressList OPTIONAL

+  );

+

+/**

+  Generate the multicast addresses identify the group of all IPv6 nodes or IPv6

+  routers defined in RFC4291.

+

+  All Nodes Addresses: FF01::1, FF02::1.

+  All Router Addresses: FF01::2, FF02::2, FF05::2.

+

+  @param[in]  Router            If TRUE, generate all routers addresses,

+                                else generate all node addresses.

+  @param[in]  Scope             interface-local(1), link-local(2), or site-local(5)

+  @param[out] Ip6Addr           The generated multicast address.

+

+  @retval EFI_INVALID_PARAMETER Any input parameter is invalid.

+  @retval EFI_SUCCESS           The address is generated.

+

+**/

+EFI_STATUS

+Ip6SetToAllNodeMulticast (

+  IN  BOOLEAN          Router,

+  IN  UINT8            Scope,

+  OUT EFI_IPv6_ADDRESS *Ip6Addr

+  );

+

+/**

+  This function converts MAC address to 64 bits interface ID according to RFC4291

+  and returns the interface ID. Currently only 48-bit MAC address is supported by

+  this function.

+

+  @param[in, out]  IpSb      The IP6 service binding instance.

+

+  @retval          NULL      The operation fails.

+  @return                    Pointer to the generated interface ID.

+

+**/

+UINT8 *

+Ip6CreateInterfaceID (

+  IN OUT IP6_SERVICE         *IpSb

+  );

+

+/**

+  This function creates link-local address from interface identifier. The

+  interface identifier is normally created from MAC address. It might be manually

+  configured by administrator if the link-local address created from MAC address

+  is a duplicate address.

+

+  @param[in, out]  IpSb      The IP6 service binding instance.

+

+  @retval          NULL      If the operation fails.

+  @return                    The generated Link Local address, in network order.

+

+**/

+EFI_IPv6_ADDRESS *

+Ip6CreateLinkLocalAddr (

+  IN OUT IP6_SERVICE         *IpSb

+  );

+

+/**

+  Compute the solicited-node multicast address for an unicast or anycast address,

+  by taking the low-order 24 bits of this address, and appending those bits to

+  the prefix FF02:0:0:0:0:1:FF00::/104.

+

+  @param  Ip6Addr               The unicast or anycast address, in network order.

+  @param  MulticastAddr         The generated solicited-node multicast address,

+                                in network order.

+

+**/

+VOID

+Ip6CreateSNMulticastAddr (

+  IN EFI_IPv6_ADDRESS  *Ip6Addr,

+  OUT EFI_IPv6_ADDRESS *MulticastAddr

+  );

+

+/**

+  Check whether the incoming Ipv6 address is a solicited-node multicast address.

+

+  @param[in]  Ip6               Ip6 address, in network order.

+

+  @retval TRUE                  Yes, solicited-node multicast address

+  @retval FALSE                 No

+

+**/

+BOOLEAN

+Ip6IsSNMulticastAddr (

+  IN EFI_IPv6_ADDRESS *Ip6

+  );

+

+/**

+  Check whether the incoming IPv6 address is one of the maintained address in

+  the IP6 service binding instance.

+

+  @param[in]  IpSb              Points to a IP6 service binding instance

+  @param[in]  Address           The IP6 address to be checked.

+  @param[out] Interface         If not NULL, output the IP6 interface which

+                                maintains the Address.

+  @param[out] AddressInfo       If not NULL, output the IP6 address information

+                                of the Address.

+

+  @retval TRUE                  Yes, it is one of the maintained addresses.

+  @retval FALSE                 No, it is not one of the maintained addresses.

+

+**/

+BOOLEAN

+Ip6IsOneOfSetAddress (

+  IN  IP6_SERVICE           *IpSb,

+  IN  EFI_IPv6_ADDRESS      *Address,

+  OUT IP6_INTERFACE         **Interface   OPTIONAL,

+  OUT IP6_ADDRESS_INFO      **AddressInfo OPTIONAL

+  );

+

+/**

+  Check whether the incoming MAC address is valid.

+

+  @param[in]  IpSb              Points to a IP6 service binding instance.

+  @param[in]  LinkAddress       The MAC address.

+

+  @retval TRUE                  Yes, it is valid.

+  @retval FALSE                 No, it is not valid.

+

+**/

+BOOLEAN

+Ip6IsValidLinkAddress (

+  IN  IP6_SERVICE      *IpSb,

+  IN  EFI_MAC_ADDRESS  *LinkAddress

+  );

+

+

+/**

+  Copy the PrefixLength bits from Src to Dest.

+

+  @param[out] Dest              A pointer to the buffer to copy to.

+  @param[in]  Src               A pointer to the buffer to copy from.

+  @param[in]  PrefixLength      The number of bits to copy.

+

+**/

+VOID

+Ip6CopyAddressByPrefix (

+  OUT EFI_IPv6_ADDRESS *Dest,

+  IN  EFI_IPv6_ADDRESS *Src,

+  IN  UINT8            PrefixLength

+  );

+

+/**

+  Insert a node IP6_ADDRESS_INFO to an IP6 interface.

+

+  @param[in, out]  IpIf             Points to an IP6 interface.

+  @param[in]       AddrInfo         Points to an IP6_ADDRESS_INFO.

+

+**/

+VOID

+Ip6AddAddr (

+  IN OUT IP6_INTERFACE *IpIf,

+  IN IP6_ADDRESS_INFO  *AddrInfo

+  );

+

+/**

+  Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO.

+

+  This function removes the matching IPv6 addresses from the address list and

+  adjusts the address count of the address list. If IpSb is not NULL, this function

+  calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the

+  its solicited-node multicast MAC address from the filter list and sends out

+  a Multicast Listener Done. If Prefix is NULL, all address in the address list

+  will be removed. If Prefix is not NULL, the address that matching the Prefix

+  with PrefixLength in the address list will be removed.

+

+  @param[in]       IpSb             NULL or points to IP6 service binding instance.

+  @param[in, out]  AddressList      address list array

+  @param[in, out]  AddressCount     the count of addresses in address list array

+  @param[in]       Prefix           NULL or an IPv6 address prefix

+  @param[in]       PrefixLength     the length of Prefix

+

+  @retval    EFI_SUCCESS            The operation completed successfully.

+  @retval    EFI_NOT_FOUND          The address matching the Prefix with PrefixLength

+                                    cannot be found in address list.

+  @retval    EFI_INVALID_PARAMETER  Any input parameter is invalid.

+

+**/

+EFI_STATUS

+Ip6RemoveAddr (

+  IN IP6_SERVICE       *IpSb          OPTIONAL,

+  IN OUT LIST_ENTRY    *AddressList,

+  IN OUT UINT32        *AddressCount,

+  IN EFI_IPv6_ADDRESS  *Prefix        OPTIONAL,

+  IN UINT8             PrefixLength

+  );

+

+/**

+  Set the Ip6 variable data.

+

+  @param[in]  IpSb              Points to an IP6 service binding instance

+

+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.

+  @retval other                 Set variable failed.

+

+**/

+EFI_STATUS

+Ip6SetVariableData (

+  IN IP6_SERVICE  *IpSb

+  );

+

+/**

+  Clear the variable and free the resource.

+

+  @param[in]  IpSb                  Ip6 service binding instance.

+

+**/

+VOID

+Ip6ClearVariableData (

+  IN IP6_SERVICE  *IpSb

+  );

+

+/**

+  Get the MAC address for a multicast IP address. Call

+  Mnp's McastIpToMac to find the MAC address instead of

+  hard-coding the NIC to be Ethernet.

+

+  @param[in]  Mnp                   The Mnp instance to get the MAC address.

+  @param[in]  Multicast             The multicast IP address to translate.

+  @param[out] Mac                   The buffer to hold the translated address.

+

+  @retval EFI_SUCCESS               The multicast IP is successfully

+                                    translated to a multicast MAC address.

+  @retval Other                     The address is not converted because an error occurred.

+

+**/

+EFI_STATUS

+Ip6GetMulticastMac (

+  IN  EFI_MANAGED_NETWORK_PROTOCOL *Mnp,

+  IN  EFI_IPv6_ADDRESS             *Multicast,

+  OUT EFI_MAC_ADDRESS              *Mac

+  );

+

+/**

+  Convert the multibyte field in IP header's byter order.

+  In spite of its name, it can also be used to convert from

+  host to network byte order.

+

+  @param[in, out]  Head                  The IP head to convert.

+

+  @return Point to the converted IP head.

+

+**/

+EFI_IP6_HEADER *

+Ip6NtohHead (

+  IN OUT EFI_IP6_HEADER *Head

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6Config.vfr b/NetworkPkg/Ip6Dxe/Ip6Config.vfr
new file mode 100644
index 0000000..902cef6
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Config.vfr
@@ -0,0 +1,170 @@
+/** @file

+  VFR file used by the IP6 configuration component.

+

+  Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6NvData.h"

+

+#define EFI_NETWORK_DEVICE_CLASS  0x04

+

+formset

+  guid     = IP6_CONFIG_NVDATA_GUID,

+  title    = STRING_TOKEN(STR_IP6_CONFIG_FORM_TITLE),

+  help     = STRING_TOKEN(STR_IP6_CONFIG_FORM_HELP),

+  class    = EFI_NETWORK_DEVICE_CLASS,

+  subclass = 0x03,

+

+  varstore IP6_CONFIG_IFR_NVDATA,

+    name = IP6_CONFIG_IFR_NVDATA,

+    guid = IP6_CONFIG_NVDATA_GUID;

+

+  form formid = FORMID_MAIN_FORM,

+    title  = STRING_TOKEN(STR_IP6_DEVICE_FORM_TITLE);

+

+    text

+      help   = STRING_TOKEN(STR_IP6_INTERFACE_NAME_HELP),

+      text   = STRING_TOKEN(STR_IP6_INTERFACE_NAME),

+        text   = STRING_TOKEN(STR_IP6_INTERFACE_NAME_CONTENT);

+

+    text

+      help   = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_HELP),

+      text   = STRING_TOKEN(STR_IP6_INTERFACE_TYPE),

+        text   = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_CONTENT);

+

+    text

+      help   = STRING_TOKEN(STR_IP6_MAC_ADDRESS_HELP),

+      text   = STRING_TOKEN(STR_IP6_MAC_ADDRESS),

+        text   = STRING_TOKEN(STR_IP6_MAC_ADDRESS_CONTENT);

+

+    text

+      help   = STRING_TOKEN(STR_IP6_HOST_ADDRESS_HELP),

+      text   = STRING_TOKEN(STR_IP6_HOST_ADDRESS),

+        text   = STRING_TOKEN(STR_NULL);

+

+    label HOST_ADDRESS_LABEL;

+    label LABEL_END;

+

+    text

+      help   = STRING_TOKEN(STR_IP6_ROUTE_TABLE_HELP),

+      text   = STRING_TOKEN(STR_IP6_ROUTE_TABLE),

+        text   = STRING_TOKEN(STR_NULL);

+

+    label ROUTE_TABLE_LABEL;

+    label LABEL_END;

+

+    text

+      help   = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS_HELP),

+      text   = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS),

+        text   = STRING_TOKEN(STR_NULL);

+

+    label GATEWAY_ADDRESS_LABEL;

+    label LABEL_END;

+

+    text

+      help   = STRING_TOKEN(STR_IP6_DNS_ADDRESS_HELP),

+      text   = STRING_TOKEN(STR_IP6_DNS_ADDRESS),

+        text   = STRING_TOKEN(STR_NULL);

+

+    label DNS_ADDRESS_LABEL;

+    label LABEL_END;

+

+    string  varid   = IP6_CONFIG_IFR_NVDATA.InterfaceId,

+            prompt  = STRING_TOKEN(STR_IP6_INTERFACE_ID),

+            help    = STRING_TOKEN(STR_IP6_INTERFACE_ID_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_INTERFACE_ID,

+            minsize = INTERFACE_ID_STR_MIN_SIZE,

+            maxsize = INTERFACE_ID_STR_MAX_SIZE,

+    endstring;

+

+    numeric varid   = IP6_CONFIG_IFR_NVDATA.DadTransmitCount,

+            prompt  = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT),

+            help    = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT_HELP),

+            flags   = 0,

+            minimum = 0,

+            maximum = DAD_MAX_TRANSMIT_COUNT,

+            step    = 0,

+    endnumeric;

+

+    oneof varid  = IP6_CONFIG_IFR_NVDATA.Policy,

+          prompt = STRING_TOKEN(STR_POLICY_TYPE_PROMPT),

+          help   = STRING_TOKEN(STR_POLICY_TYPE_HELP),

+          option text = STRING_TOKEN(STR_POLICY_TYPE_AUTO),   value = IP6_POLICY_AUTO,   flags = DEFAULT;

+          option text = STRING_TOKEN(STR_POLICY_TYPE_MANUAL), value = IP6_POLICY_MANUAL, flags = 0;

+    endoneof;

+

+    subtitle text = STRING_TOKEN(STR_NULL);

+

+    suppressif ideqval IP6_CONFIG_IFR_NVDATA.Policy == IP6_POLICY_AUTO;

+    goto FORMID_MANUAL_CONFIG_FORM,

+         prompt = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM),

+         help   = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM_HELP),

+         flags  = 0;

+    subtitle text = STRING_TOKEN(STR_NULL);

+    endif;

+

+    text

+      help   = STRING_TOKEN (STR_SAVE_CHANGES_HELP),

+      text   = STRING_TOKEN (STR_SAVE_CHANGES),

+        text   = STRING_TOKEN (STR_NULL),

+      flags  = INTERACTIVE,

+      key    = KEY_SAVE_CHANGES;

+

+  endform;

+

+  form formid = FORMID_MANUAL_CONFIG_FORM,

+    title  = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM);

+

+    string  varid   = IP6_CONFIG_IFR_NVDATA.ManualAddress,

+            prompt  = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS),

+            help    = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_MANUAL_ADDRESS,

+            minsize = ADDRESS_STR_MIN_SIZE,

+            maxsize = ADDRESS_STR_MAX_SIZE,

+    endstring;

+

+    string  varid   = IP6_CONFIG_IFR_NVDATA.GatewayAddress,

+            prompt  = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDRESS),

+            help    = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDR_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_GATEWAY_ADDRESS,

+            minsize = ADDRESS_STR_MIN_SIZE,

+            maxsize = ADDRESS_STR_MAX_SIZE,

+    endstring;

+

+    string  varid   = IP6_CONFIG_IFR_NVDATA.DnsAddress,

+            prompt  = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS),

+            help    = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_DNS_ADDRESS,

+            minsize = ADDRESS_STR_MIN_SIZE,

+            maxsize = ADDRESS_STR_MAX_SIZE,

+    endstring;

+

+    goto FORMID_MAIN_FORM,

+    prompt = STRING_TOKEN (STR_SAVE_AND_EXIT),

+    help   = STRING_TOKEN (STR_SAVE_AND_EXIT),

+    flags  = INTERACTIVE,

+    key    = KEY_SAVE_CONFIG_CHANGES;

+

+    goto FORMID_MAIN_FORM,

+    prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),

+    help   = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),

+    flags  = INTERACTIVE,

+    key    = KEY_IGNORE_CONFIG_CHANGES;

+

+  endform;

+

+endformset;

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c
new file mode 100644
index 0000000..3cfd1f2
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c
@@ -0,0 +1,2369 @@
+/** @file

+  The implementation of EFI IPv6 Configuration Protocol.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+LIST_ENTRY  mIp6ConfigInstanceList = {&mIp6ConfigInstanceList, &mIp6ConfigInstanceList};

+

+/**

+  The event process routine when the DHCPv6 service binding protocol is installed

+  in the system.

+

+  @param[in]     Event         Not used.

+  @param[in]     Context       Pointer to the IP6 config instance data.

+

+**/

+VOID

+EFIAPI

+Ip6ConfigOnDhcp6SbInstalled (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  );

+

+/**

+  Update the current policy to NewPolicy. During the transition

+  period, the default router list, on-link prefix list, autonomous prefix list

+  and address list in all interfaces will be released.

+

+  @param[in]  IpSb               The IP6 service binding instance.

+  @param[in]  NewPolicy          The new policy to be updated to.

+

+**/

+VOID

+Ip6ConfigOnPolicyChanged (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_CONFIG_POLICY  NewPolicy

+  )

+{

+  LIST_ENTRY      *Entry;

+  LIST_ENTRY      *Entry2;

+  LIST_ENTRY      *Next;

+  IP6_INTERFACE   *IpIf;

+  IP6_DAD_ENTRY   *DadEntry;

+

+  //

+  // Currently there are only two policies: Manual and Automatic. Regardless of

+  // what transition is going on, i.e., Manual -> Automatic and Automatic ->

+  // Manual, we have to free default router list, on-link prefix list, autonomous

+  // prefix list, address list in all the interfaces and destroy any IPv6 child

+  // instance whose local IP is neither 0 nor the link-local address.

+  //

+  Ip6CleanDefaultRouterList (IpSb);

+  Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix);

+  Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix);

+

+  //

+  // It's tricky... If the LinkLocal address is O.K., add back the link-local

+  // prefix to the on-link prefix table.

+  //

+  if (IpSb->LinkLocalOk) {

+    Ip6CreatePrefixListEntry (

+      IpSb,

+      TRUE,

+      (UINT32) IP6_INFINIT_LIFETIME,

+      (UINT32) IP6_INFINIT_LIFETIME,

+      IP6_LINK_LOCAL_PREFIX_LENGTH,

+      &IpSb->LinkLocalAddr

+      );

+  }

+

+  //

+  // All IPv6 children that use global unicast address as it's source address

+  // should be destryoed now. The survivers are those use the link-local address

+  // or the unspecified address as the source address.

+  // TODO: Conduct a check here.

+  Ip6RemoveAddr (

+    IpSb,

+    &IpSb->DefaultInterface->AddressList,

+    &IpSb->DefaultInterface->AddressCount,

+    NULL,

+    0

+    );

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+    //

+    // remove all pending DAD entries for the global addresses.

+    //

+    IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);

+

+    NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {

+      DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);

+

+      if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) {

+        //

+        // Fail this DAD entry if the address is not link-local.

+        //

+        Ip6OnDADFinished (FALSE, IpIf, DadEntry);

+      }

+    }

+  }

+

+  if (NewPolicy == Ip6ConfigPolicyAutomatic) {

+    //

+    // Set paramters to trigger router solicitation sending in timer handler.

+    //

+    IpSb->RouterAdvertiseReceived = FALSE;

+    IpSb->SolicitTimer            = IP6_MAX_RTR_SOLICITATIONS;

+    //

+    // delay 1 second

+    //

+    IpSb->Ticks                   = (UINT32) IP6_GET_TICKS (IP6_ONE_SECOND_IN_MS);

+  }

+

+}

+

+/**

+  The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration.

+

+  @param[in]     Instance      Pointer to the IP6 config instance data.

+  @param[in]     OtherInfoOnly If FALSE, get stateful address and other information

+                               via DHCPv6. Otherwise, only get the other information.

+

+  @retval    EFI_SUCCESS       The operation finished successfully.

+  @retval    EFI_UNSUPPORTED   The DHCP6 driver is not available.

+

+**/

+EFI_STATUS

+Ip6ConfigStartStatefulAutoConfig (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN BOOLEAN              OtherInfoOnly

+  )

+{

+  EFI_STATUS                Status;

+  IP6_SERVICE               *IpSb;

+  EFI_DHCP6_CONFIG_DATA     Dhcp6CfgData;

+  EFI_DHCP6_PROTOCOL        *Dhcp6;

+  EFI_DHCP6_PACKET_OPTION   *OptList[1];

+  UINT16                    OptBuf[4];

+  EFI_DHCP6_PACKET_OPTION   *Oro;

+  EFI_DHCP6_RETRANSMISSION  InfoReqReXmit;

+

+  //

+  // A host must not invoke stateful address configuration if it is already

+  // participating in the statuful protocol as a result of an earlier advertisement.

+  //

+  if (Instance->Dhcp6Handle != NULL) {

+    return EFI_SUCCESS;

+  }

+

+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+

+  Instance->OtherInfoOnly = OtherInfoOnly;

+

+  Status = NetLibCreateServiceChild (

+             IpSb->Controller,

+             IpSb->Image,

+             &gEfiDhcp6ServiceBindingProtocolGuid,

+             &Instance->Dhcp6Handle

+             );

+

+  if (Status == EFI_UNSUPPORTED) {

+    //

+    // No DHCPv6 Service Binding protocol, register a notify.

+    //

+    if (Instance->Dhcp6SbNotifyEvent == NULL) {

+      Instance->Dhcp6SbNotifyEvent = EfiCreateProtocolNotifyEvent (

+                                       &gEfiDhcp6ServiceBindingProtocolGuid,

+                                       TPL_CALLBACK,

+                                       Ip6ConfigOnDhcp6SbInstalled,

+                                       (VOID *) Instance,

+                                       &Instance->Registration

+                                       );

+    }

+  }

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (Instance->Dhcp6SbNotifyEvent != NULL) {

+    gBS->CloseEvent (Instance->Dhcp6SbNotifyEvent);

+  }

+

+  Status = gBS->OpenProtocol (

+                  Instance->Dhcp6Handle,

+                  &gEfiDhcp6ProtocolGuid,

+                  (VOID **) &Instance->Dhcp6,

+                  IpSb->Image,

+                  IpSb->Controller,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  ASSERT_EFI_ERROR (Status);

+

+  Dhcp6 = Instance->Dhcp6;

+  Dhcp6->Configure (Dhcp6, NULL);

+

+  //

+  // Set the exta options to send. Here we only want the option request option

+  // with DNS SERVERS.

+  //

+  Oro                         = (EFI_DHCP6_PACKET_OPTION *) OptBuf;

+  Oro->OpCode                 = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO);

+  Oro->OpLen                  = HTONS (2);

+  *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);

+  OptList[0]                  = Oro;

+

+  Status                      = EFI_SUCCESS;

+

+  if (!OtherInfoOnly) {

+    //

+    // Get stateful address and other information via DHCPv6.

+    //

+    Dhcp6CfgData.Dhcp6Callback         = NULL;

+    Dhcp6CfgData.CallbackContext       = NULL;

+    Dhcp6CfgData.OptionCount           = 1;

+    Dhcp6CfgData.OptionList            = &OptList[0];

+    Dhcp6CfgData.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;

+    Dhcp6CfgData.IaDescriptor.IaId     = Instance->IaId;

+    Dhcp6CfgData.IaInfoEvent           = Instance->Dhcp6Event;

+    Dhcp6CfgData.ReconfigureAccept     = FALSE;

+    Dhcp6CfgData.RapidCommit           = FALSE;

+    Dhcp6CfgData.SolicitRetransmission = NULL;

+

+    Status = Dhcp6->Configure (Dhcp6, &Dhcp6CfgData);

+

+    if (!EFI_ERROR (Status)) {

+

+      if (IpSb->LinkLocalOk) {

+        Status = Dhcp6->Start (Dhcp6);

+      } else {

+        IpSb->Dhcp6NeedStart = TRUE;

+      }

+

+    }

+  } else {

+    //

+    // Only get other information via DHCPv6, this doesn't require a config

+    // action.

+    //

+    InfoReqReXmit.Irt = 4;

+    InfoReqReXmit.Mrc = 64;

+    InfoReqReXmit.Mrt = 60;

+    InfoReqReXmit.Mrd = 0;

+

+    if (IpSb->LinkLocalOk) {

+      Status = Dhcp6->InfoRequest (

+                        Dhcp6,

+                        TRUE,

+                        Oro,

+                        0,

+                        NULL,

+                        &InfoReqReXmit,

+                        Instance->Dhcp6Event,

+                        Ip6ConfigOnDhcp6Reply,

+                        Instance

+                        );

+    } else {

+      IpSb->Dhcp6NeedInfoRequest = TRUE;

+    }

+

+  }

+

+  return Status;

+}

+

+/**

+  Signal the registered event. It is the callback routine for NetMapIterate.

+

+  @param[in]  Map    Points to the list of registered event.

+  @param[in]  Item   The registered event.

+  @param[in]  Arg    Not used.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ConfigSignalEvent (

+  IN NET_MAP                *Map,

+  IN NET_MAP_ITEM           *Item,

+  IN VOID                   *Arg

+  )

+{

+  gBS->SignalEvent ((EFI_EVENT) Item->Key);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Read the configuration data from variable storage according to the VarName and

+  gEfiIp6ConfigProtocolGuid. It checks the integrity of variable data. If the

+  data is corrupted, it clears the variable data to ZERO. Othewise, it outputs the

+  configuration data to IP6_CONFIG_INSTANCE.

+

+  @param[in]      VarName  The pointer to the variable name

+  @param[in, out] Instance The pointer to the IP6 config instance data.

+

+  @retval EFI_NOT_FOUND         The variable can not be found or already corrupted.

+  @retval EFI_OUT_OF_RESOURCES  Fail to allocate resource to complete the operation.

+  @retval EFI_SUCCESS           The configuration data was retrieved successfully.

+

+**/

+EFI_STATUS

+Ip6ConfigReadConfigData (

+  IN     CHAR16               *VarName,

+  IN OUT IP6_CONFIG_INSTANCE  *Instance

+  )

+{

+  EFI_STATUS              Status;

+  UINTN                   VarSize;

+  IP6_CONFIG_VARIABLE     *Variable;

+  IP6_CONFIG_DATA_ITEM    *DataItem;

+  UINTN                   Index;

+  IP6_CONFIG_DATA_RECORD  DataRecord;

+  CHAR8                   *Data;

+

+  //

+  // Try to read the configuration variable.

+  //

+  VarSize = 0;

+  Status  = gRT->GetVariable (

+                   VarName,

+                   &gEfiIp6ConfigProtocolGuid,

+                   NULL,

+                   &VarSize,

+                   NULL

+                   );

+

+  if (Status == EFI_BUFFER_TOO_SMALL) {

+    //

+    // Allocate buffer and read the config variable.

+    //

+    Variable = AllocatePool (VarSize);

+    if (Variable == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    Status = gRT->GetVariable (

+                    VarName,

+                    &gEfiIp6ConfigProtocolGuid,

+                    NULL,

+                    &VarSize,

+                    Variable

+                    );

+    if (EFI_ERROR (Status) || (UINT16) (~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize)) != 0) {

+      //

+      // GetVariable still error or the variable is corrupted.

+      // Fall back to the default value.

+      //

+      FreePool (Variable);

+

+      //

+      // Remove the problematic variable and return EFI_NOT_FOUND, a new

+      // variable will be set again.

+      //

+      gRT->SetVariable (

+             VarName,

+             &gEfiIp6ConfigProtocolGuid,

+             IP6_CONFIG_VARIABLE_ATTRIBUTE,

+             0,

+             NULL

+             );

+

+      return EFI_NOT_FOUND;

+    }

+

+    //

+    // Get the IAID we use.

+    //

+    Instance->IaId = Variable->IaId;

+

+    for (Index = 0; Index < Variable->DataRecordCount; Index++) {

+

+      CopyMem (&DataRecord, &Variable->DataRecord[Index], sizeof (DataRecord));

+

+      DataItem = &Instance->DataItem[DataRecord.DataType];

+      if (DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED) &&

+          (DataItem->DataSize != DataRecord.DataSize)

+          ) {

+        //

+        // Perhaps a corrupted data record...

+        //

+        continue;

+      }

+

+      if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {

+        //

+        // This data item has variable length data.

+        //

+        DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize);

+        if (DataItem->Data.Ptr == NULL) {

+          //

+          // no memory resource

+          //

+          continue;

+        }

+      }

+

+      Data = (CHAR8 *) Variable + DataRecord.Offset;

+      CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize);

+

+      DataItem->DataSize = DataRecord.DataSize;

+      DataItem->Status   = EFI_SUCCESS;

+    }

+

+    FreePool (Variable);

+    return EFI_SUCCESS;

+  }

+

+  return Status;

+}

+

+/**

+  Write the configuration data from IP6_CONFIG_INSTANCE to variable storage.

+

+  @param[in]      VarName  The pointer to the variable name.

+  @param[in]      Instance The pointer to the IP6 configuration instance data.

+

+  @retval EFI_OUT_OF_RESOURCES  Fail to allocate resource to complete the operation.

+  @retval EFI_SUCCESS           The configuration data is written successfully.

+

+**/

+EFI_STATUS

+Ip6ConfigWriteConfigData (

+  IN CHAR16               *VarName,

+  IN IP6_CONFIG_INSTANCE  *Instance

+  )

+{

+  UINTN                   Index;

+  UINTN                   VarSize;

+  IP6_CONFIG_DATA_ITEM    *DataItem;

+  IP6_CONFIG_VARIABLE     *Variable;

+  IP6_CONFIG_DATA_RECORD  *DataRecord;

+  CHAR8                   *Heap;

+  EFI_STATUS              Status;

+

+  VarSize = sizeof (IP6_CONFIG_VARIABLE) - sizeof (IP6_CONFIG_DATA_RECORD);

+

+  for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {

+

+    DataItem = &Instance->DataItem[Index];

+    if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {

+

+      VarSize += sizeof (IP6_CONFIG_DATA_RECORD) + DataItem->DataSize;

+    }

+  }

+

+  Variable = AllocatePool (VarSize);

+  if (Variable == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Variable->IaId            = Instance->IaId;

+  Heap                      = (CHAR8 *) Variable + VarSize;

+  Variable->DataRecordCount = 0;

+

+  for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {

+

+    DataItem = &Instance->DataItem[Index];

+    if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {

+

+      Heap -= DataItem->DataSize;

+      CopyMem (Heap, DataItem->Data.Ptr, DataItem->DataSize);

+

+      DataRecord           = &Variable->DataRecord[Variable->DataRecordCount];

+      DataRecord->DataType = (EFI_IP6_CONFIG_DATA_TYPE) Index;

+      DataRecord->DataSize = DataItem->DataSize;

+      DataRecord->Offset   = (UINT16) (Heap - (CHAR8 *) Variable);

+

+      Variable->DataRecordCount++;

+    }

+  }

+

+  Variable->Checksum = 0;

+  Variable->Checksum = (UINT16) ~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize);

+

+  Status = gRT->SetVariable (

+                  VarName,

+                  &gEfiIp6ConfigProtocolGuid,

+                  IP6_CONFIG_VARIABLE_ATTRIBUTE,

+                  VarSize,

+                  Variable

+                  );

+

+  FreePool (Variable);

+

+  return Status;

+}

+

+/**

+  The work function for EfiIp6ConfigGetData() to get the interface information

+  of the communication device this IP6Config instance manages.

+

+  @param[in]      Instance Pointer to the IP6 config instance data.

+  @param[in, out] DataSize On input, in bytes, the size of Data. On output, in

+                           bytes, the size of buffer required to store the specified

+                           configuration data.

+  @param[in]      Data     The data buffer in which the configuration data is returned.

+                           Ignored if DataSize is ZERO.

+

+  @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified

+                               configuration data, and the required size is

+                               returned in DataSize.

+  @retval EFI_SUCCESS          The specified configuration data was obtained.

+

+**/

+EFI_STATUS

+Ip6ConfigGetIfInfo (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN OUT UINTN            *DataSize,

+  IN VOID                 *Data      OPTIONAL

+  )

+{

+  IP6_SERVICE                    *IpSb;

+  UINTN                          Length;

+  IP6_CONFIG_DATA_ITEM           *Item;

+  EFI_IP6_CONFIG_INTERFACE_INFO  *IfInfo;

+  UINT32                         AddressCount;

+  UINT32                         RouteCount;

+

+  IpSb   = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+  Length = sizeof (EFI_IP6_CONFIG_INTERFACE_INFO);

+

+  //

+  // Calculate the required length, add the buffer size for AddressInfo and

+  // RouteTable

+  //

+  Ip6BuildEfiAddressList (IpSb, &AddressCount, NULL);

+  Ip6BuildEfiRouteTable (IpSb->RouteTable, &RouteCount, NULL);

+

+  Length += AddressCount * sizeof (EFI_IP6_ADDRESS_INFO) + RouteCount * sizeof (EFI_IP6_ROUTE_TABLE);

+

+  if (*DataSize < Length) {

+    *DataSize = Length;

+    return EFI_BUFFER_TOO_SMALL;

+  }

+

+  //

+  // Copy the fixed size part of the interface info.

+  //

+  Item = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo];

+  IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data;

+  CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP6_CONFIG_INTERFACE_INFO));

+

+  //

+  // AddressInfo

+  //

+  IfInfo->AddressInfo = (EFI_IP6_ADDRESS_INFO *) (IfInfo + 1);

+  Ip6BuildEfiAddressList (IpSb, &IfInfo->AddressInfoCount, &IfInfo->AddressInfo);

+

+  //

+  // RouteTable

+  //

+  IfInfo->RouteTable = (EFI_IP6_ROUTE_TABLE *) (IfInfo->AddressInfo + IfInfo->AddressInfoCount);

+  Ip6BuildEfiRouteTable (IpSb->RouteTable, &IfInfo->RouteCount, &IfInfo->RouteTable);

+

+  if (IfInfo->AddressInfoCount == 0) {

+    IfInfo->AddressInfo = NULL;

+  }

+

+  if (IfInfo->RouteCount == 0) {

+    IfInfo->RouteTable = NULL;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  The work function for EfiIp6ConfigSetData() to set the alternative inteface ID

+  for the communication device managed by this IP6Config instance, if the link local

+  IPv6 addresses generated from the interface ID based on the default source the

+  EFI IPv6 Protocol uses is a duplicate address.

+

+  @param[in]     Instance Pointer to the IP6 configuration instance data.

+  @param[in]     DataSize Size of the buffer pointed to by Data in bytes.

+  @param[in]     Data     The data buffer to set.

+

+  @retval EFI_BAD_BUFFER_SIZE  The DataSize does not match the size of the type,

+                               8 bytes.

+  @retval EFI_SUCCESS          The specified configuration data for the EFI IPv6

+                               network stack was set.

+

+**/

+EFI_STATUS

+Ip6ConfigSetAltIfId (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN UINTN                DataSize,

+  IN VOID                 *Data

+  )

+{

+  EFI_IP6_CONFIG_INTERFACE_ID  *OldIfId;

+  EFI_IP6_CONFIG_INTERFACE_ID  *NewIfId;

+  IP6_CONFIG_DATA_ITEM         *DataItem;

+

+  if (DataSize != sizeof (EFI_IP6_CONFIG_INTERFACE_ID)) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId];

+  OldIfId  = DataItem->Data.AltIfId;

+  NewIfId  = (EFI_IP6_CONFIG_INTERFACE_ID *) Data;

+

+  CopyMem (OldIfId, NewIfId, DataSize);

+  DataItem->Status = EFI_SUCCESS;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  The work function for EfiIp6ConfigSetData() to set the general configuration

+  policy for the EFI IPv6 network stack that is running on the communication device

+  managed by this IP6Config instance. The policy will affect other configuration settings.

+

+  @param[in]     Instance Pointer to the IP6 config instance data.

+  @param[in]     DataSize Size of the buffer pointed to by Data in bytes.

+  @param[in]     Data     The data buffer to set.

+

+  @retval EFI_INVALID_PARAMETER The to be set policy is invalid.

+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type.

+  @retval EFI_ABORTED           The new policy equals the current policy.

+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6

+                                network stack was set.

+

+**/

+EFI_STATUS

+Ip6ConfigSetPolicy (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN UINTN                DataSize,

+  IN VOID                 *Data

+  )

+{

+  EFI_IP6_CONFIG_POLICY  NewPolicy;

+  IP6_CONFIG_DATA_ITEM   *DataItem;

+  IP6_SERVICE            *IpSb;

+

+  if (DataSize != sizeof (EFI_IP6_CONFIG_POLICY)) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  NewPolicy = *((EFI_IP6_CONFIG_POLICY *) Data);

+

+  if (NewPolicy > Ip6ConfigPolicyAutomatic) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (NewPolicy == Instance->Policy) {

+

+    return EFI_ABORTED;

+  } else {

+

+    if (NewPolicy == Ip6ConfigPolicyAutomatic) {

+      //

+      // Clean the ManualAddress, Gateway and DnsServers, shrink the variable

+      // data size, and fire up all the related events.

+      //

+      DataItem           = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];

+      if (DataItem->Data.Ptr != NULL) {

+        FreePool (DataItem->Data.Ptr);

+      }

+      DataItem->Data.Ptr = NULL;

+      DataItem->DataSize = 0;

+      DataItem->Status   = EFI_NOT_FOUND;

+      NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);

+

+      DataItem           = &Instance->DataItem[Ip6ConfigDataTypeGateway];

+      if (DataItem->Data.Ptr != NULL) {

+        FreePool (DataItem->Data.Ptr);

+      }

+      DataItem->Data.Ptr = NULL;

+      DataItem->DataSize = 0;

+      DataItem->Status   = EFI_NOT_FOUND;

+      NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);

+

+      DataItem           = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];

+      DataItem->Data.Ptr = NULL;

+      DataItem->DataSize = 0;

+      DataItem->Status   = EFI_NOT_FOUND;

+      NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);

+    } else {

+      //

+      // The policy is changed from automatic to manual. Stop the DHCPv6 process

+      // and destroy the DHCPv6 child.

+      //

+      if (Instance->Dhcp6Handle != NULL) {

+        Ip6ConfigDestroyDhcp6 (Instance);

+      }

+    }

+

+    IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+    Ip6ConfigOnPolicyChanged (IpSb, NewPolicy);

+

+    Instance->Policy = NewPolicy;

+

+    return EFI_SUCCESS;

+  }

+}

+

+/**

+  The work function for EfiIp6ConfigSetData() to set the number of consecutive

+  Neighbor Solicitation messages sent while performing Duplicate Address Detection

+  on a tentative address. A value of ZERO indicates that Duplicate Address Detection

+  will not be performed on a tentative address.

+

+  @param[in]     The Instance Pointer to the IP6 config instance data.

+  @param[in]     DataSize Size of the buffer pointed to by Data in bytes.

+  @param[in]     Data     The data buffer to set.

+

+  @retval EFI_BAD_BUFFER_SIZE  The DataSize does not match the size of the type.

+  @retval EFI_ABORTED          The new transmit count equals the current configuration.

+  @retval EFI_SUCCESS          The specified configuration data for the EFI IPv6

+                               network stack was set.

+

+**/

+EFI_STATUS

+Ip6ConfigSetDadXmits (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN UINTN                DataSize,

+  IN VOID                 *Data

+  )

+{

+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  *OldDadXmits;

+

+  if (DataSize != sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS)) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  OldDadXmits = Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits].Data.DadXmits;

+

+  if ((*(UINT32 *) Data) == OldDadXmits->DupAddrDetectTransmits) {

+

+    return EFI_ABORTED;

+  } else {

+

+    OldDadXmits->DupAddrDetectTransmits = *((UINT32 *) Data);

+    return EFI_SUCCESS;

+  }

+}

+

+/**

+  The callback function for Ip6SetAddr. The prototype is defined

+  as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed

+  for the manual address set by Ip6ConfigSetMaunualAddress.

+

+  @param[in]     IsDadPassed   If TRUE, Duplicate Address Detection passed.

+  @param[in]     TargetAddress The tentative IPv6 address to be checked.

+  @param[in]     Context       Pointer to the IP6 configuration instance data.

+

+**/

+VOID

+Ip6ManualAddrDadCallback (

+  IN BOOLEAN           IsDadPassed,

+  IN EFI_IPv6_ADDRESS  *TargetAddress,

+  IN VOID              *Context

+  )

+{

+  IP6_CONFIG_INSTANCE            *Instance;

+  UINTN                          Index;

+  IP6_CONFIG_DATA_ITEM           *Item;

+  EFI_IP6_CONFIG_MANUAL_ADDRESS  *ManualAddr;

+  EFI_IP6_CONFIG_MANUAL_ADDRESS  *PassedAddr;

+  UINTN                          DadPassCount;

+  UINTN                          DadFailCount;

+  IP6_SERVICE                    *IpSb;

+

+  Instance   = (IP6_CONFIG_INSTANCE *) Context;

+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);

+  Item       = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];

+  ManualAddr = NULL;

+

+  for (Index = 0; Index < Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); Index++) {

+    //

+    // Find the original tag used to place into the NET_MAP.

+    //

+    ManualAddr = Item->Data.ManualAddress + Index;

+    if (EFI_IP6_EQUAL (TargetAddress, &ManualAddr->Address)) {

+      break;

+    }

+  }

+

+  ASSERT (Index != Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));

+

+  if (IsDadPassed) {

+    NetMapInsertTail (&Instance->DadPassedMap, ManualAddr, NULL);

+  } else {

+    NetMapInsertTail (&Instance->DadFailedMap, ManualAddr, NULL);

+  }

+

+  DadPassCount = NetMapGetCount (&Instance->DadPassedMap);

+  DadFailCount = NetMapGetCount (&Instance->DadFailedMap);

+

+  if ((DadPassCount + DadFailCount) == (Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS))) {

+    //

+    // All addresses have finished the configuration process.

+    //

+    if (DadFailCount != 0) {

+      //

+      // There is at least one duplicate address.

+      //

+      FreePool (Item->Data.Ptr);

+

+      Item->DataSize = DadPassCount * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);

+      if (Item->DataSize == 0) {

+        //

+        // All failed, bad luck.

+        //

+        Item->Data.Ptr = NULL;

+        Item->Status   = EFI_NOT_FOUND;

+      } else {

+        //

+        // Part of addresses are detected to be duplicates, so update the

+        // data with those passed.

+        //

+        PassedAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AllocatePool (Item->DataSize);

+        ASSERT (PassedAddr != NULL);

+

+        Item->Data.Ptr = PassedAddr;

+        Item->Status   = EFI_SUCCESS;

+

+        while (!NetMapIsEmpty (&Instance->DadPassedMap)) {

+          ManualAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) NetMapRemoveHead (&Instance->DadPassedMap, NULL);

+          CopyMem (PassedAddr, ManualAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));

+

+          PassedAddr++;

+        }

+

+        ASSERT ((UINTN) PassedAddr - (UINTN) Item->Data.Ptr == Item->DataSize);

+      }

+    } else {

+      //

+      // All addresses are valid.

+      //

+      Item->Status = EFI_SUCCESS;

+    }

+

+    //

+    // Remove the tags we put in the NET_MAPs.

+    //

+    while (!NetMapIsEmpty (&Instance->DadFailedMap)) {

+      NetMapRemoveHead (&Instance->DadFailedMap, NULL);

+    }

+

+    while (!NetMapIsEmpty (&Instance->DadPassedMap)) {

+      NetMapRemoveHead (&Instance->DadPassedMap, NULL);

+    }

+

+    //

+    // Signal the waiting events.

+    //

+    NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL);

+    IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+    Ip6ConfigWriteConfigData (IpSb->MacString, Instance);

+  }

+}

+

+/**

+  The work function for EfiIp6ConfigSetData() to set the station addresses manually

+  for the EFI IPv6 network stack. It is only configurable when the policy is

+  Ip6ConfigPolicyManual.

+

+  @param[in]     Instance Pointer to the IP6 configuration instance data.

+  @param[in]     DataSize Size of the buffer pointed to by Data in bytes.

+  @param[in]     Data     The data buffer to set.

+

+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type.

+  @retval EFI_WRITE_PROTECTED   The specified configuration data cannot be set

+                                under the current policy.

+  @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.

+  @retval EFI_OUT_OF_RESOURCES  Fail to allocate resource to complete the operation.

+  @retval EFI_NOT_READY         An asynchrous process is invoked to set the specified

+                                configuration data, and the process is not finished.

+  @retval EFI_ABORTED           The manual addresses to be set equal current

+                                configuration.

+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6

+                                network stack was set.

+

+**/

+EFI_STATUS

+Ip6ConfigSetMaunualAddress (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN UINTN                DataSize,

+  IN VOID                 *Data

+  )

+{

+  EFI_IP6_CONFIG_MANUAL_ADDRESS  *NewAddress;

+  EFI_IP6_CONFIG_MANUAL_ADDRESS  *TmpAddress;

+  IP6_CONFIG_DATA_ITEM           *DataItem;

+  UINTN                          NewAddressCount;

+  UINTN                          Index1;

+  UINTN                          Index2;

+  IP6_SERVICE                    *IpSb;

+  IP6_ADDRESS_INFO               *CurrentAddrInfo;

+  IP6_ADDRESS_INFO               *Copy;

+  LIST_ENTRY                     CurrentSourceList;

+  UINT32                         CurrentSourceCount;

+  LIST_ENTRY                     *Entry;

+  LIST_ENTRY                     *Entry2;

+  IP6_INTERFACE                  *IpIf;

+  IP6_PREFIX_LIST_ENTRY          *PrefixEntry;

+  EFI_STATUS                     Status;

+  BOOLEAN                        IsUpdated;

+

+  ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY);

+

+  if (((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0) || (DataSize == 0)) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  if (Instance->Policy != Ip6ConfigPolicyManual) {

+    return EFI_WRITE_PROTECTED;

+  }

+

+  NewAddressCount = DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);

+  NewAddress      = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) Data;

+

+  for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) {

+

+    if (NetIp6IsLinkLocalAddr (&NewAddress->Address)    ||

+        !NetIp6IsValidUnicast (&NewAddress->Address)    ||

+        (NewAddress->PrefixLength > 128)

+        ) {

+      //

+      // make sure the IPv6 address is unicast and not link-local address &&

+      // the prefix length is valid.

+      //

+      return EFI_INVALID_PARAMETER;

+    }

+

+    TmpAddress = NewAddress + 1;

+    for (Index2 = Index1 + 1; Index2 < NewAddressCount; Index2++, TmpAddress++) {

+      //

+      // Any two addresses in the array can't be equal.

+      //

+      if (EFI_IP6_EQUAL (&TmpAddress->Address, &NewAddress->Address)) {

+

+        return EFI_INVALID_PARAMETER;

+      }

+    }

+  }

+

+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+

+  //

+  // Build the current source address list.

+  //

+  InitializeListHead (&CurrentSourceList);

+  CurrentSourceCount = 0;

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+    IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);

+

+    NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {

+      CurrentAddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);

+

+      Copy            = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), CurrentAddrInfo);

+      if (Copy == NULL) {

+        break;

+      }

+

+      InsertTailList (&CurrentSourceList, &Copy->Link);

+      CurrentSourceCount++;

+    }

+  }

+

+  //

+  // Update the value... a long journey starts

+  //

+  NewAddress = AllocateCopyPool (DataSize, Data);

+  if (NewAddress == NULL) {

+    Ip6RemoveAddr (NULL, &CurrentSourceList, &CurrentSourceCount, NULL, 0);

+

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Store the new data, and init the DataItem status to EFI_NOT_READY because

+  // we may have an asynchronous configuration process.

+  //

+  DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];

+  if (DataItem->Data.Ptr != NULL) {

+    FreePool (DataItem->Data.Ptr);

+  }

+  DataItem->Data.Ptr = NewAddress;

+  DataItem->DataSize = DataSize;

+  DataItem->Status   = EFI_NOT_READY;

+

+  //

+  // Trigger DAD, it's an asynchronous process.

+  //

+  IsUpdated  = FALSE;

+

+  for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) {

+    if (Ip6IsOneOfSetAddress (IpSb, &NewAddress->Address, NULL, &CurrentAddrInfo)) {

+      ASSERT (CurrentAddrInfo != NULL);

+      //

+      // Remove this already existing source address from the CurrentSourceList

+      // built before.

+      //

+      Ip6RemoveAddr (

+        NULL,

+        &CurrentSourceList,

+        &CurrentSourceCount,

+        &CurrentAddrInfo->Address,

+        128

+        );

+

+      //

+      // This manual address is already in use, see whether prefix length is changed.

+      //

+      if (NewAddress->PrefixLength != CurrentAddrInfo->PrefixLength) {

+        //

+        // Remove the on-link prefix table, the route entry will be removed

+        // implicitly.

+        //

+        PrefixEntry = Ip6FindPrefixListEntry (

+                        IpSb,

+                        TRUE,

+                        CurrentAddrInfo->PrefixLength,

+                        &CurrentAddrInfo->Address

+                        );

+        if (PrefixEntry != NULL) {

+          Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE);

+        }

+

+        //

+        // Save the prefix length.

+        //

+        CurrentAddrInfo->PrefixLength = NewAddress->PrefixLength;

+        IsUpdated = TRUE;

+      }

+

+      //

+      // create a new on-link prefix entry.

+      //

+      PrefixEntry = Ip6FindPrefixListEntry (

+                      IpSb,

+                      TRUE,

+                      NewAddress->PrefixLength,

+                      &NewAddress->Address

+                      );

+      if (PrefixEntry == NULL) {

+        Ip6CreatePrefixListEntry (

+          IpSb,

+          TRUE,

+          (UINT32) IP6_INFINIT_LIFETIME,

+          (UINT32) IP6_INFINIT_LIFETIME,

+          NewAddress->PrefixLength,

+          &NewAddress->Address

+          );

+      }

+

+      CurrentAddrInfo->IsAnycast = NewAddress->IsAnycast;

+      //

+      // Artificially mark this address passed DAD be'coz it is already in use.

+      //

+      Ip6ManualAddrDadCallback (TRUE, &NewAddress->Address, Instance);

+    } else {

+      //

+      // A new address.

+      //

+      IsUpdated = TRUE;

+

+      //

+      // Set the new address, this will trigger DAD and activate the address if

+      // DAD succeeds.

+      //

+      Ip6SetAddress (

+        IpSb->DefaultInterface,

+        &NewAddress->Address,

+        NewAddress->IsAnycast,

+        NewAddress->PrefixLength,

+        (UINT32) IP6_INFINIT_LIFETIME,

+        (UINT32) IP6_INFINIT_LIFETIME,

+        Ip6ManualAddrDadCallback,

+        Instance

+        );

+    }

+  }

+

+  //

+  // Check the CurrentSourceList, it now contains those addresses currently in

+  // use and will be removed.

+  //

+  IpIf = IpSb->DefaultInterface;

+

+  while (!IsListEmpty (&CurrentSourceList)) {

+    IsUpdated = TRUE;

+

+    CurrentAddrInfo = NET_LIST_HEAD (&CurrentSourceList, IP6_ADDRESS_INFO, Link);

+

+    //

+    // This local address is going to be removed, the IP instances that are

+    // currently using it will be destroyed.

+    //

+    Ip6RemoveAddr (

+      IpSb,

+      &IpIf->AddressList,

+      &IpIf->AddressCount,

+      &CurrentAddrInfo->Address,

+      128

+      );

+

+    //

+    // Remove the on-link prefix table, the route entry will be removed

+    // implicitly.

+    //

+    PrefixEntry = Ip6FindPrefixListEntry (

+                    IpSb,

+                    TRUE,

+                    CurrentAddrInfo->PrefixLength,

+                    &CurrentAddrInfo->Address

+                    );

+    if (PrefixEntry != NULL) {

+      Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE);

+    }

+

+    RemoveEntryList (&CurrentAddrInfo->Link);

+    FreePool (CurrentAddrInfo);

+  }

+

+  if (IsUpdated) {

+    if (DataItem->Status == EFI_NOT_READY) {

+      //

+      // If DAD is disabled on this interface, the configuration process is

+      // actually synchronous, and the data item's status will be changed to

+      // the final status before we reach here, just check it.

+      //

+      Status = EFI_NOT_READY;

+    } else {

+      Status = EFI_SUCCESS;

+    }

+  } else {

+    //

+    // No update is taken, reset the status to success and return EFI_ABORTED.

+    //

+    DataItem->Status = EFI_SUCCESS;

+    Status           = EFI_ABORTED;

+  }

+

+  return Status;

+}

+

+/**

+  The work function for EfiIp6ConfigSetData() to set the gateway addresses manually

+  for the EFI IPv6 network stack that is running on the communication device that

+  this EFI IPv6 Configuration Protocol manages. It is not configurable when the policy is

+  Ip6ConfigPolicyAutomatic. The gateway addresses must be unicast IPv6 addresses.

+

+  @param[in]     Instance The pointer to the IP6 config instance data.

+  @param[in]     DataSize The size of the buffer pointed to by Data in bytes.

+  @param[in]     Data     The data buffer to set. This points to an array of

+                          EFI_IPv6_ADDRESS instances.

+

+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type.

+  @retval EFI_WRITE_PROTECTED   The specified configuration data cannot be set

+                                under the current policy.

+  @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to complete the operation.

+  @retval EFI_ABORTED           The manual gateway addresses to be set equal the

+                                current configuration.

+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6

+                                network stack was set.

+

+**/

+EFI_STATUS

+Ip6ConfigSetGateway (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN UINTN                DataSize,

+  IN VOID                 *Data

+  )

+{

+  UINTN                 Index1;

+  UINTN                 Index2;

+  EFI_IPv6_ADDRESS      *OldGateway;

+  EFI_IPv6_ADDRESS      *NewGateway;

+  UINTN                 OldGatewayCount;

+  UINTN                 NewGatewayCount;

+  IP6_CONFIG_DATA_ITEM  *Item;

+  BOOLEAN               OneRemoved;

+  BOOLEAN               OneAdded;

+  IP6_SERVICE           *IpSb;

+  IP6_DEFAULT_ROUTER    *DefaultRouter;

+  VOID                  *Tmp;

+

+  if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  if (Instance->Policy != Ip6ConfigPolicyManual) {

+    return EFI_WRITE_PROTECTED;

+  }

+

+  NewGateway      = (EFI_IPv6_ADDRESS *) Data;

+  NewGatewayCount = DataSize / sizeof (EFI_IPv6_ADDRESS);

+  for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {

+

+    if (!NetIp6IsValidUnicast (NewGateway + Index1)) {

+

+      return EFI_INVALID_PARAMETER;

+    }

+

+    for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) {

+      if (EFI_IP6_EQUAL (NewGateway + Index1, NewGateway + Index2)) {

+        return EFI_INVALID_PARAMETER;

+      }

+    }

+  }

+

+  IpSb            = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+  Item            = &Instance->DataItem[Ip6ConfigDataTypeGateway];

+  OldGateway      = Item->Data.Gateway;

+  OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);

+  OneRemoved      = FALSE;

+  OneAdded        = FALSE;

+

+  if (NewGatewayCount != OldGatewayCount) {

+    Tmp = AllocatePool (DataSize);

+    if (Tmp == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  } else {

+    Tmp = NULL;

+  }

+

+  for (Index1 = 0; Index1 < OldGatewayCount; Index1++) {

+    //

+    // Find the gateways that are no long in the new setting and remove them.

+    //

+    for (Index2 = 0; Index2 < NewGatewayCount; Index2++) {

+      if (EFI_IP6_EQUAL (OldGateway + Index1, NewGateway + Index2)) {

+        OneRemoved = TRUE;

+        break;

+      }

+    }

+

+    if (Index2 == NewGatewayCount) {

+      //

+      // Remove this default router.

+      //

+      DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1);

+      if (DefaultRouter != NULL) {

+        Ip6DestroyDefaultRouter (IpSb, DefaultRouter);

+      }

+    }

+  }

+

+  for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {

+

+    DefaultRouter = Ip6FindDefaultRouter (IpSb, NewGateway + Index1);

+    if (DefaultRouter == NULL) {

+      Ip6CreateDefaultRouter (IpSb, NewGateway + Index1, IP6_INF_ROUTER_LIFETIME);

+      OneAdded = TRUE;

+    }

+  }

+

+  if (!OneRemoved && !OneAdded) {

+    Item->Status = EFI_SUCCESS;

+    return EFI_ABORTED;

+  } else {

+

+    if (Tmp != NULL) {

+      if (Item->Data.Ptr != NULL) {

+        FreePool (Item->Data.Ptr);

+      }

+      Item->Data.Ptr = Tmp;

+    }

+

+    CopyMem (Item->Data.Ptr, Data, DataSize);

+    Item->DataSize = DataSize;

+    Item->Status   = EFI_SUCCESS;

+    return EFI_SUCCESS;

+  }

+}

+

+/**

+  The work function for EfiIp6ConfigSetData() to set the DNS server list for the

+  EFI IPv6 network stack running on the communication device that this EFI IPv6

+  Configuration Protocol manages. It is not configurable when the policy is

+  Ip6ConfigPolicyAutomatic. The DNS server addresses must be unicast IPv6 addresses.

+

+  @param[in]     Instance The pointer to the IP6 config instance data.

+  @param[in]     DataSize The size of the buffer pointed to by Data in bytes.

+  @param[in]     Data     The data buffer to set, points to an array of

+                          EFI_IPv6_ADDRESS instances.

+

+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type.

+  @retval EFI_WRITE_PROTECTED   The specified configuration data cannot be set

+                                under the current policy.

+  @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to complete the operation.

+  @retval EFI_ABORTED           The DNS server addresses to be set equal the current

+                                configuration.

+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6

+                                network stack was set.

+

+**/

+EFI_STATUS

+Ip6ConfigSetDnsServer (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN UINTN                DataSize,

+  IN VOID                 *Data

+  )

+{

+  UINTN                 OldIndex;

+  UINTN                 NewIndex;

+  UINTN                 Index1;

+  EFI_IPv6_ADDRESS      *OldDns;

+  EFI_IPv6_ADDRESS      *NewDns;

+  UINTN                 OldDnsCount;

+  UINTN                 NewDnsCount;

+  IP6_CONFIG_DATA_ITEM  *Item;

+  BOOLEAN               OneAdded;

+  VOID                  *Tmp;

+

+  if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  if (Instance->Policy != Ip6ConfigPolicyManual) {

+    return EFI_WRITE_PROTECTED;

+  }

+

+  Item        = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];

+  NewDns      = (EFI_IPv6_ADDRESS *) Data;

+  OldDns      = Item->Data.DnsServers;

+  NewDnsCount = DataSize / sizeof (EFI_IPv6_ADDRESS);

+  OldDnsCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);

+  OneAdded    = FALSE;

+

+  if (NewDnsCount != OldDnsCount) {

+    Tmp = AllocatePool (DataSize);

+    if (Tmp == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  } else {

+    Tmp = NULL;

+  }

+

+  for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) {

+

+    if (!NetIp6IsValidUnicast (NewDns + NewIndex)) {

+      //

+      // The dns server address must be unicast.

+      //

+      FreePool (Tmp);

+      return EFI_INVALID_PARAMETER;

+    }

+

+    for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) {

+      if (EFI_IP6_EQUAL (NewDns + NewIndex, NewDns + Index1)) {

+        FreePool (Tmp);

+        return EFI_INVALID_PARAMETER;

+      }

+    }

+

+    if (OneAdded) {

+      //

+      // If any address in the new setting is not in the old settings, skip the

+      // comparision below.

+      //

+      continue;

+    }

+

+    for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) {

+      if (EFI_IP6_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) {

+        //

+        // If found break out.

+        //

+        break;

+      }

+    }

+

+    if (OldIndex == OldDnsCount) {

+      OneAdded = TRUE;

+    }

+  }

+

+  if (!OneAdded && (DataSize == Item->DataSize)) {

+    //

+    // No new item is added and the size is the same.

+    //

+    Item->Status = EFI_SUCCESS;

+    return EFI_ABORTED;

+  } else {

+    if (Tmp != NULL) {

+      if (Item->Data.Ptr != NULL) {

+        FreePool (Item->Data.Ptr);

+      }

+      Item->Data.Ptr = Tmp;

+    }

+

+    CopyMem (Item->Data.Ptr, Data, DataSize);

+    Item->DataSize = DataSize;

+    Item->Status   = EFI_SUCCESS;

+    return EFI_SUCCESS;

+  }

+}

+

+/**

+  Generate the operational state of the interface this IP6 config instance manages

+  and output in EFI_IP6_CONFIG_INTERFACE_INFO.

+

+  @param[in]      IpSb     The pointer to the IP6 service binding instance.

+  @param[out]     IfInfo   The pointer to the IP6 configuration interface information structure.

+

+**/

+VOID

+Ip6ConfigInitIfInfo (

+  IN  IP6_SERVICE                    *IpSb,

+  OUT EFI_IP6_CONFIG_INTERFACE_INFO  *IfInfo

+  )

+{

+  IfInfo->Name[0] = L'e';

+  IfInfo->Name[1] = L't';

+  IfInfo->Name[2] = L'h';

+  IfInfo->Name[3] = (CHAR16) (L'0' + IpSb->Ip6ConfigInstance.IfIndex);

+  IfInfo->Name[4] = 0;

+

+  IfInfo->IfType        = IpSb->SnpMode.IfType;

+  IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize;

+  CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize);

+}

+

+/**

+  Parse DHCPv6 reply packet to get the DNS server list.

+  It is the work function for Ip6ConfigOnDhcp6Reply and Ip6ConfigOnDhcp6Event.

+

+  @param[in]      Dhcp6    The pointer to the EFI_DHCP6_PROTOCOL instance.

+  @param[in, out] Instance The pointer to the IP6 configuration instance data.

+  @param[in]      Reply    The pointer to the DHCPv6 reply packet.

+

+  @retval EFI_SUCCESS      The DNS server address was retrieved from the reply packet.

+  @retval EFI_NOT_READY    The reply packet does not contain the DNS server option, or

+                           the DNS server address is not valid.

+

+**/

+EFI_STATUS

+Ip6ConfigParseDhcpReply (

+  IN     EFI_DHCP6_PROTOCOL  *Dhcp6,

+  IN OUT IP6_CONFIG_INSTANCE *Instance,

+  IN     EFI_DHCP6_PACKET    *Reply

+  )

+{

+  EFI_STATUS               Status;

+  UINT32                   OptCount;

+  EFI_DHCP6_PACKET_OPTION  **OptList;

+  UINT16                   OpCode;

+  UINT16                   Length;

+  UINTN                    Index;

+  UINTN                    Index2;

+  EFI_IPv6_ADDRESS         *DnsServer;

+  IP6_CONFIG_DATA_ITEM     *Item;

+

+  //

+  // A DHCPv6 reply packet is received as the response to our InfoRequest

+  // packet.

+  //

+  OptCount = 0;

+  Status   = Dhcp6->Parse (Dhcp6, Reply, &OptCount, NULL);

+  if (Status != EFI_BUFFER_TOO_SMALL) {

+    return EFI_NOT_READY;

+  }

+

+  OptList = AllocatePool (OptCount * sizeof (EFI_DHCP6_PACKET_OPTION *));

+  if (OptList == NULL) {

+    return EFI_NOT_READY;

+  }

+

+  Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, OptList);

+  if (EFI_ERROR (Status)) {

+    Status = EFI_NOT_READY;

+    goto ON_EXIT;

+  }

+

+  Status = EFI_SUCCESS;

+

+  for (Index = 0; Index < OptCount; Index++) {

+    //

+    // Go through all the options to check the ones we are interested in.

+    // The OpCode and Length are in network byte-order and may not be naturally

+    // aligned.

+    //

+    CopyMem (&OpCode, &OptList[Index]->OpCode, sizeof (OpCode));

+    OpCode = NTOHS (OpCode);

+

+    if (OpCode == IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS) {

+      CopyMem (&Length, &OptList[Index]->OpLen, sizeof (Length));

+      Length = NTOHS (Length);

+

+      if ((Length == 0) || ((Length % sizeof (EFI_IPv6_ADDRESS)) != 0)) {

+        //

+        // The length should be a multiple of 16 bytes.

+        //

+        Status = EFI_NOT_READY;

+        break;

+      }

+

+      //

+      // Validate the DnsServers: whether they are unicast addresses.

+      //

+      DnsServer = (EFI_IPv6_ADDRESS *) OptList[Index]->Data;

+      for (Index2 = 0; Index2 < Length / sizeof (EFI_IPv6_ADDRESS); Index2++) {

+        if (!NetIp6IsValidUnicast (DnsServer)) {

+          Status = EFI_NOT_READY;

+          goto ON_EXIT;

+        }

+

+        DnsServer++;

+      }

+

+      Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];

+

+      if (Item->DataSize != Length) {

+        if (Item->Data.Ptr != NULL) {

+          FreePool (Item->Data.Ptr);

+        }

+

+        Item->Data.Ptr = AllocatePool (Length);

+        ASSERT (Item->Data.Ptr != NULL);

+      }

+

+      CopyMem (Item->Data.Ptr, OptList[Index]->Data, Length);

+      Item->DataSize = Length;

+      Item->Status   = EFI_SUCCESS;

+

+      //

+      // Signal the waiting events.

+      //

+      NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL);

+

+      break;

+    }

+  }

+

+ON_EXIT:

+

+  FreePool (OptList);

+  return Status;

+}

+

+/**

+  The callback function for Ip6SetAddr. The prototype is defined

+  as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed

+  on the tentative address by DHCPv6 in Ip6ConfigOnDhcp6Event().

+

+  @param[in]     IsDadPassed   If TRUE, Duplicate Address Detection passes.

+  @param[in]     TargetAddress The tentative IPv6 address to be checked.

+  @param[in]     Context       Pointer to the IP6 configuration instance data.

+

+**/

+VOID

+Ip6ConfigSetStatefulAddrCallback (

+  IN BOOLEAN           IsDadPassed,

+  IN EFI_IPv6_ADDRESS  *TargetAddress,

+  IN VOID              *Context

+  )

+{

+  IP6_CONFIG_INSTANCE  *Instance;

+

+  Instance = (IP6_CONFIG_INSTANCE *) Context;

+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);

+

+  //

+  // We should record the addresses that fail the DAD, and DECLINE them.

+  //

+  if (IsDadPassed) {

+    //

+    // Decrease the count, no interests in those passed DAD.

+    //

+    if (Instance->FailedIaAddressCount > 0 ) {

+      Instance->FailedIaAddressCount--;

+    }

+  } else {

+    //

+    // Record it.

+    //

+    IP6_COPY_ADDRESS (Instance->DeclineAddress + Instance->DeclineAddressCount, TargetAddress);

+    Instance->DeclineAddressCount++;

+  }

+

+  if (Instance->FailedIaAddressCount == Instance->DeclineAddressCount) {

+    //

+    // The checking on all addresses are finished.

+    //

+    if (Instance->DeclineAddressCount != 0) {

+      //

+      // Decline those duplicates.

+      //

+      Instance->Dhcp6->Decline (

+                         Instance->Dhcp6,

+                         Instance->DeclineAddressCount,

+                         Instance->DeclineAddress

+                         );

+    }

+

+    if (Instance->DeclineAddress != NULL) {

+      FreePool (Instance->DeclineAddress);

+    }

+    Instance->DeclineAddress      = NULL;

+    Instance->DeclineAddressCount = 0;

+  }

+}

+

+/**

+  The event handle routine when DHCPv6 process is finished or is updated.

+

+  @param[in]     Event         Not used.

+  @param[in]     Context       The pointer to the IP6 configuration instance data.

+

+**/

+VOID

+EFIAPI

+Ip6ConfigOnDhcp6Event (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  )

+{

+  IP6_CONFIG_INSTANCE      *Instance;

+  EFI_DHCP6_PROTOCOL       *Dhcp6;

+  EFI_STATUS               Status;

+  EFI_DHCP6_MODE_DATA      Dhcp6ModeData;

+  EFI_DHCP6_IA             *Ia;

+  EFI_DHCP6_IA_ADDRESS     *IaAddr;

+  UINT32                   Index;

+  IP6_SERVICE              *IpSb;

+  IP6_ADDRESS_INFO         *AddrInfo;

+  IP6_INTERFACE            *IpIf;

+

+  Instance = (IP6_CONFIG_INSTANCE *) Context;

+

+  if ((Instance->Policy != Ip6ConfigPolicyAutomatic) || Instance->OtherInfoOnly) {

+    //

+    // IPv6 is not operating in the automatic policy now or

+    // the DHCPv6 information request message exchange is aborted.

+    //

+    return ;

+  }

+

+  //

+  // The stateful address autoconfiguration is done or updated.

+  //

+  Dhcp6 = Instance->Dhcp6;

+

+  Status = Dhcp6->GetModeData (Dhcp6, &Dhcp6ModeData, NULL);

+  if (EFI_ERROR (Status)) {

+    return ;

+  }

+

+  IpSb   = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+  IpIf   = IpSb->DefaultInterface;

+  Ia     = Dhcp6ModeData.Ia;

+  IaAddr = Ia->IaAddress;

+

+  if (Instance->DeclineAddress != NULL) {

+    FreePool (Instance->DeclineAddress);

+  }

+

+  Instance->DeclineAddress = (EFI_IPv6_ADDRESS *) AllocatePool (Ia->IaAddressCount * sizeof (EFI_IPv6_ADDRESS));

+  if (Instance->DeclineAddress == NULL) {

+    goto ON_EXIT;

+  }

+

+  Instance->FailedIaAddressCount = Ia->IaAddressCount;

+  Instance->DeclineAddressCount   = 0;

+

+  for (Index = 0; Index < Ia->IaAddressCount; Index++, IaAddr++) {

+    if (Ia->IaAddress[Index].ValidLifetime != 0 && Ia->State == Dhcp6Bound) {

+      //

+      // Set this address, either it's a new address or with updated lifetimes.

+      // An appropriate prefix length will be set.

+      //

+      Ip6SetAddress (

+        IpIf,

+        &IaAddr->IpAddress,

+        FALSE,

+        0,

+        IaAddr->ValidLifetime,

+        IaAddr->PreferredLifetime,

+        Ip6ConfigSetStatefulAddrCallback,

+        Instance

+        );

+    } else {

+      //

+      // discard this address, artificially decrease the count as if this address

+      // passed DAD.

+      //

+      if (Ip6IsOneOfSetAddress (IpSb, &IaAddr->IpAddress, NULL, &AddrInfo)) {

+        ASSERT (AddrInfo != NULL);

+        Ip6RemoveAddr (

+          IpSb,

+          &IpIf->AddressList,

+          &IpIf->AddressCount,

+          &AddrInfo->Address,

+          AddrInfo->PrefixLength

+          );

+      }

+

+      if (Instance->FailedIaAddressCount > 0) {

+        Instance->FailedIaAddressCount--;

+      }

+    }

+  }

+

+  //

+  // Parse the Reply packet to get the options we need.

+  //

+  if (Dhcp6ModeData.Ia->ReplyPacket != NULL) {

+    Ip6ConfigParseDhcpReply (Dhcp6, Instance, Dhcp6ModeData.Ia->ReplyPacket);

+  }

+

+ON_EXIT:

+

+  FreePool (Dhcp6ModeData.ClientId);

+  FreePool (Dhcp6ModeData.Ia);

+}

+

+/**

+  The event process routine when the DHCPv6 server is answered with a reply packet

+  for an information request.

+

+  @param[in]     This          Points to the EFI_DHCP6_PROTOCOL.

+  @param[in]     Context       The pointer to the IP6 configuration instance data.

+  @param[in]     Packet        The DHCPv6 reply packet.

+

+  @retval EFI_SUCCESS      The DNS server address was retrieved from the reply packet.

+  @retval EFI_NOT_READY    The reply packet does not contain the DNS server option, or

+                           the DNS server address is not valid.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ConfigOnDhcp6Reply (

+  IN EFI_DHCP6_PROTOCOL  *This,

+  IN VOID                *Context,

+  IN EFI_DHCP6_PACKET    *Packet

+  )

+{

+  return Ip6ConfigParseDhcpReply (This, (IP6_CONFIG_INSTANCE *) Context, Packet);

+}

+

+/**

+  The event process routine when the DHCPv6 service binding protocol is installed

+  in the system.

+

+  @param[in]     Event         Not used.

+  @param[in]     Context       The pointer to the IP6 config instance data.

+

+**/

+VOID

+EFIAPI

+Ip6ConfigOnDhcp6SbInstalled (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  )

+{

+  IP6_CONFIG_INSTANCE  *Instance;

+

+  Instance = (IP6_CONFIG_INSTANCE *) Context;

+

+  if ((Instance->Dhcp6Handle != NULL) || (Instance->Policy != Ip6ConfigPolicyAutomatic)) {

+    //

+    // The DHCP6 child is already created or the policy is no longer AUTOMATIC.

+    //

+    return ;

+  }

+

+  Ip6ConfigStartStatefulAutoConfig (Instance, Instance->OtherInfoOnly);

+}

+

+/**

+  Set the configuration for the EFI IPv6 network stack running on the communication

+  device this EFI IPv6 Configuration Protocol instance manages.

+

+  This function is used to set the configuration data of type DataType for the EFI

+  IPv6 network stack that is running on the communication device that this EFI IPv6

+  Configuration Protocol instance manages.

+

+  DataSize is used to calculate the count of structure instances in the Data for

+  a DataType in which multiple structure instances are allowed.

+

+  This function is always non-blocking. When setting some type of configuration data,

+  an asynchronous process is invoked to check the correctness of the data, such as

+  performing Duplicate Address Detection on the manually set local IPv6 addresses.

+  EFI_NOT_READY is returned immediately to indicate that such an asynchronous process

+  is invoked, and the process is not finished yet. The caller wanting to get the result

+  of the asynchronous process is required to call RegisterDataNotify() to register an

+  event on the specified configuration data. Once the event is signaled, the caller

+  can call GetData() to obtain the configuration data and know the result.

+  For other types of configuration data that do not require an asynchronous configuration

+  process, the result of the operation is immediately returned.

+

+  @param[in]     This           The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.

+  @param[in]     DataType       The type of data to set.

+  @param[in]     DataSize       Size of the buffer pointed to by Data in bytes.

+  @param[in]     Data           The data buffer to set. The type of the data buffer is

+                                associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data for the EFI IPv6

+                                network stack was set successfully.

+  @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:

+                                - This is NULL.

+                                - Data is NULL.

+                                - One or more fields in Data do not match the requirement of the

+                                  data type indicated by DataType.

+  @retval EFI_WRITE_PROTECTED   The specified configuration data is read-only or the specified

+                                configuration data cannot be set under the current policy.

+  @retval EFI_ACCESS_DENIED     Another set operation on the specified configuration

+                                data is already in process.

+  @retval EFI_NOT_READY         An asynchronous process was invoked to set the specified

+                                configuration data, and the process is not finished yet.

+  @retval EFI_BAD_BUFFER_SIZE   The DataSize does not match the size of the type

+                                indicated by DataType.

+  @retval EFI_UNSUPPORTED       This DataType is not supported.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_DEVICE_ERROR      An unexpected system error or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6ConfigSetData (

+  IN EFI_IP6_CONFIG_PROTOCOL    *This,

+  IN EFI_IP6_CONFIG_DATA_TYPE   DataType,

+  IN UINTN                      DataSize,

+  IN VOID                       *Data

+  )

+{

+  EFI_TPL              OldTpl;

+  EFI_STATUS           Status;

+  IP6_CONFIG_INSTANCE  *Instance;

+  IP6_SERVICE          *IpSb;

+

+  if ((This == NULL) || (Data == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (DataType >= Ip6ConfigDataTypeMaximum) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);

+  IpSb     = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  Status = Instance->DataItem[DataType].Status;

+  if (Status != EFI_NOT_READY) {

+

+    if (Instance->DataItem[DataType].SetData == NULL) {

+      //

+      // This type of data is readonly.

+      //

+      Status = EFI_WRITE_PROTECTED;

+    } else {

+

+      Status = Instance->DataItem[DataType].SetData (Instance, DataSize, Data);

+      if (!EFI_ERROR (Status)) {

+        //

+        // Fire up the events registered with this type of data.

+        //

+        NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL);

+        Ip6ConfigWriteConfigData (IpSb->MacString, Instance);

+      } else if (Status == EFI_ABORTED) {

+        //

+        // The SetData is aborted because the data to set is the same with

+        // the one maintained.

+        //

+        Status = EFI_SUCCESS;

+        NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL);

+      }

+    }

+  } else {

+    //

+    // Another asynchornous process is on the way.

+    //

+    Status = EFI_ACCESS_DENIED;

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+/**

+  Get the configuration data for the EFI IPv6 network stack running on the communication

+  device that this EFI IPv6 Configuration Protocol instance manages.

+

+  This function returns the configuration data of type DataType for the EFI IPv6 network

+  stack running on the communication device that this EFI IPv6 Configuration Protocol instance

+  manages.

+

+  The caller is responsible for allocating the buffer used to return the specified

+  configuration data. The required size will be returned to the caller if the size of

+  the buffer is too small.

+

+  EFI_NOT_READY is returned if the specified configuration data is not ready due to an

+  asynchronous configuration process already in progress. The caller can call RegisterDataNotify()

+  to register an event on the specified configuration data. Once the asynchronous configuration

+  process is finished, the event will be signaled, and a subsequent GetData() call will return

+  the specified configuration data.

+

+  @param[in]      This           Pointer to the EFI_IP6_CONFIG_PROTOCOL instance.

+  @param[in]      DataType       The type of data to get.

+  @param[in, out] DataSize       On input, in bytes, the size of Data. On output, in bytes, the

+                                 size of buffer required to store the specified configuration data.

+  @param[in]     Data            The data buffer in which the configuration data is returned. The

+                                 type of the data buffer is associated with the DataType.

+                                 This is an optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:

+                                - This is NULL.

+                                - DataSize is NULL.

+                                - Data is NULL if *DataSize is not zero.

+  @retval EFI_BUFFER_TOO_SMALL  The size of Data is too small for the specified configuration data,

+                                and the required size is returned in DataSize.

+  @retval EFI_NOT_READY         The specified configuration data is not ready due to an

+                                asynchronous configuration process already in progress.

+  @retval EFI_NOT_FOUND         The specified configuration data is not found.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6ConfigGetData (

+  IN EFI_IP6_CONFIG_PROTOCOL    *This,

+  IN EFI_IP6_CONFIG_DATA_TYPE   DataType,

+  IN OUT UINTN                  *DataSize,

+  IN VOID                       *Data   OPTIONAL

+  )

+{

+  EFI_TPL               OldTpl;

+  EFI_STATUS            Status;

+  IP6_CONFIG_INSTANCE   *Instance;

+  IP6_CONFIG_DATA_ITEM  *DataItem;

+

+  if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (DataType >= Ip6ConfigDataTypeMaximum) {

+    return EFI_NOT_FOUND;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);

+  DataItem = &Instance->DataItem[DataType];

+

+  Status   = Instance->DataItem[DataType].Status;

+  if (!EFI_ERROR (Status)) {

+

+    if (DataItem->GetData != NULL) {

+

+      Status = DataItem->GetData (Instance, DataSize, Data);

+    } else if (*DataSize < Instance->DataItem[DataType].DataSize) {

+      //

+      // Update the buffer length.

+      //

+      *DataSize = Instance->DataItem[DataType].DataSize;

+      Status    = EFI_BUFFER_TOO_SMALL;

+    } else {

+

+      *DataSize = Instance->DataItem[DataType].DataSize;

+      CopyMem (Data, Instance->DataItem[DataType].Data.Ptr, *DataSize);

+    }

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+/**

+  Register an event that is signaled whenever a configuration process on the specified

+  configuration data is done.

+

+  This function registers an event that is to be signaled whenever a configuration

+  process on the specified configuration data is performed. An event can be registered

+  for a different DataType simultaneously. The caller is responsible for determining

+  which type of configuration data causes the signaling of the event in such an event.

+

+  @param[in]     This           Pointer to the EFI_IP6_CONFIG_PROTOCOL instance.

+  @param[in]     DataType       The type of data to unregister the event for.

+  @param[in]     Event          The event to register.

+

+  @retval EFI_SUCCESS           The notification event for the specified configuration data is

+                                registered.

+  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.

+  @retval EFI_UNSUPPORTED       The configuration data type specified by DataType is not

+                                supported.

+  @retval EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval EFI_ACCESS_DENIED     The Event is already registered for the DataType.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6ConfigRegisterDataNotify (

+  IN EFI_IP6_CONFIG_PROTOCOL    *This,

+  IN EFI_IP6_CONFIG_DATA_TYPE   DataType,

+  IN EFI_EVENT                  Event

+  )

+{

+  EFI_TPL              OldTpl;

+  EFI_STATUS           Status;

+  IP6_CONFIG_INSTANCE  *Instance;

+  NET_MAP              *EventMap;

+  NET_MAP_ITEM         *Item;

+

+  if ((This == NULL) || (Event == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (DataType >= Ip6ConfigDataTypeMaximum) {

+    return EFI_UNSUPPORTED;

+  }

+

+  OldTpl    = gBS->RaiseTPL (TPL_CALLBACK);

+

+  Instance  = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);

+  EventMap  = &Instance->DataItem[DataType].EventMap;

+

+  //

+  // Check whether this event is already registered for this DataType.

+  //

+  Item = NetMapFindKey (EventMap, Event);

+  if (Item == NULL) {

+

+    Status = NetMapInsertTail (EventMap, Event, NULL);

+

+    if (EFI_ERROR (Status)) {

+

+      Status = EFI_OUT_OF_RESOURCES;

+    }

+

+  } else {

+

+    Status = EFI_ACCESS_DENIED;

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+/**

+  Remove a previously registered event for the specified configuration data.

+

+  @param  This                   The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.

+  @param  DataType               The type of data to remove from the previously

+                                 registered event.

+  @param  Event                  The event to be unregistered.

+

+  @retval EFI_SUCCESS            The event registered for the specified

+                                 configuration data was removed.

+  @retval EFI_INVALID_PARAMETER  This is NULL or Event is NULL.

+  @retval EFI_NOT_FOUND          The Event has not been registered for the

+                                 specified DataType.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6ConfigUnregisterDataNotify (

+  IN EFI_IP6_CONFIG_PROTOCOL    *This,

+  IN EFI_IP6_CONFIG_DATA_TYPE   DataType,

+  IN EFI_EVENT                  Event

+  )

+{

+  EFI_TPL              OldTpl;

+  EFI_STATUS           Status;

+  IP6_CONFIG_INSTANCE  *Instance;

+  NET_MAP_ITEM         *Item;

+

+  if ((This == NULL) || (Event == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (DataType >= Ip6ConfigDataTypeMaximum) {

+    return EFI_NOT_FOUND;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);

+

+  Item = NetMapFindKey (&Instance->DataItem[DataType].EventMap, Event);

+  if (Item != NULL) {

+

+    NetMapRemoveItem (&Instance->DataItem[DataType].EventMap, Item, NULL);

+    Status = EFI_SUCCESS;

+  } else {

+

+    Status = EFI_NOT_FOUND;

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+/**

+  Initialize an IP6_CONFIG_INSTANCE.

+

+  @param[out]    Instance       The buffer of IP6_CONFIG_INSTANCE to be initialized.

+

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to complete the operation.

+  @retval EFI_SUCCESS           The IP6_CONFIG_INSTANCE initialized successfully.

+

+**/

+EFI_STATUS

+Ip6ConfigInitInstance (

+  OUT IP6_CONFIG_INSTANCE  *Instance

+  )

+{

+  IP6_SERVICE           *IpSb;

+  IP6_CONFIG_INSTANCE   *TmpInstance;

+  LIST_ENTRY            *Entry;

+  EFI_STATUS            Status;

+  UINTN                 Index;

+  UINT16                IfIndex;

+  IP6_CONFIG_DATA_ITEM  *DataItem;

+

+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+

+  Instance->Signature = IP6_CONFIG_INSTANCE_SIGNATURE;

+

+  //

+  // Determine the index of this interface.

+  //

+  IfIndex = 0;

+  NET_LIST_FOR_EACH (Entry, &mIp6ConfigInstanceList) {

+    TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_CONFIG_INSTANCE, Link, IP6_CONFIG_INSTANCE_SIGNATURE);

+

+    if (TmpInstance->IfIndex > IfIndex) {

+      //

+      // There is a sequence hole because some interface is down.

+      //

+      break;

+    }

+

+    IfIndex++;

+  }

+

+  Instance->IfIndex = IfIndex;

+  NetListInsertBefore (Entry, &Instance->Link);

+

+  for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {

+    //

+    // Initialize the event map for each data item.

+    //

+    NetMapInit (&Instance->DataItem[Index].EventMap);

+  }

+

+  //

+  // Initialize the NET_MAPs used for DAD on manually configured source addresses.

+  //

+  NetMapInit (&Instance->DadFailedMap);

+  NetMapInit (&Instance->DadPassedMap);

+

+  //

+  // Initialize each data type: associate storage and set data size for the

+  // fixed size data types, hook the SetData function, set the data attribute.

+  //

+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo];

+  DataItem->GetData  = Ip6ConfigGetIfInfo;

+  DataItem->Data.Ptr = &Instance->InterfaceInfo;

+  DataItem->DataSize = sizeof (Instance->InterfaceInfo);

+  SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE);

+  Ip6ConfigInitIfInfo (IpSb, &Instance->InterfaceInfo);

+

+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId];

+  DataItem->SetData  = Ip6ConfigSetAltIfId;

+  DataItem->Data.Ptr = &Instance->AltIfId;

+  DataItem->DataSize = sizeof (Instance->AltIfId);

+  DataItem->Status   = EFI_NOT_FOUND;

+  SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);

+

+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypePolicy];

+  DataItem->SetData  = Ip6ConfigSetPolicy;

+  DataItem->Data.Ptr = &Instance->Policy;

+  DataItem->DataSize = sizeof (Instance->Policy);

+  Instance->Policy   = Ip6ConfigPolicyAutomatic;

+  SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);

+

+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits];

+  DataItem->SetData  = Ip6ConfigSetDadXmits;

+  DataItem->Data.Ptr = &Instance->DadXmits;

+  DataItem->DataSize = sizeof (Instance->DadXmits);

+  Instance->DadXmits.DupAddrDetectTransmits = IP6_CONFIG_DEFAULT_DAD_XMITS;

+  SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);

+

+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];

+  DataItem->SetData  = Ip6ConfigSetMaunualAddress;

+  DataItem->Status   = EFI_NOT_FOUND;

+

+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeGateway];

+  DataItem->SetData  = Ip6ConfigSetGateway;

+  DataItem->Status   = EFI_NOT_FOUND;

+

+  DataItem           = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];

+  DataItem->SetData  = Ip6ConfigSetDnsServer;

+  DataItem->Status   = EFI_NOT_FOUND;

+

+  //

+  // Create the event used for DHCP.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_CALLBACK,

+                  Ip6ConfigOnDhcp6Event,

+                  Instance,

+                  &Instance->Dhcp6Event

+                  );

+  ASSERT_EFI_ERROR (Status);

+

+  Instance->Configured  = TRUE;

+

+  //

+  // Try to read the config data from NV variable.

+  //

+  Status = Ip6ConfigReadConfigData (IpSb->MacString, Instance);

+  if (Status == EFI_NOT_FOUND) {

+    //

+    // The NV variable is not set, so generate a random IAID, and write down the

+    // fresh new configuration as the NV variable now.

+    //

+    Instance->IaId = NET_RANDOM (NetRandomInitSeed ());

+

+    for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) {

+      Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31));

+    }

+

+    Ip6ConfigWriteConfigData (IpSb->MacString, Instance);

+  } else if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Instance->Ip6Config.SetData              = EfiIp6ConfigSetData;

+  Instance->Ip6Config.GetData              = EfiIp6ConfigGetData;

+  Instance->Ip6Config.RegisterDataNotify   = EfiIp6ConfigRegisterDataNotify;

+  Instance->Ip6Config.UnregisterDataNotify = EfiIp6ConfigUnregisterDataNotify;

+

+

+  //

+  // Publish the IP6 configuration form

+  //

+  return Ip6ConfigFormInit (Instance);

+}

+

+/**

+  Release an IP6_CONFIG_INSTANCE.

+

+  @param[in, out] Instance    The buffer of IP6_CONFIG_INSTANCE to be freed.

+

+**/

+VOID

+Ip6ConfigCleanInstance (

+  IN OUT IP6_CONFIG_INSTANCE  *Instance

+  )

+{

+  UINTN                 Index;

+  IP6_CONFIG_DATA_ITEM  *DataItem;

+

+  if (Instance->DeclineAddress != NULL) {

+    FreePool (Instance->DeclineAddress);

+  }

+

+  if (!Instance->Configured) {

+    return ;

+  }

+

+  if (Instance->Dhcp6Handle != NULL) {

+

+    Ip6ConfigDestroyDhcp6 (Instance);

+  }

+

+  //

+  // Close the event.

+  //

+  if (Instance->Dhcp6Event != NULL) {

+    gBS->CloseEvent (Instance->Dhcp6Event);

+  }

+

+  NetMapClean (&Instance->DadPassedMap);

+  NetMapClean (&Instance->DadFailedMap);

+

+  for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {

+

+    DataItem = &Instance->DataItem[Index];

+

+    if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {

+      if (DataItem->Data.Ptr != NULL) {

+        FreePool (DataItem->Data.Ptr);

+      }

+      DataItem->Data.Ptr = NULL;

+      DataItem->DataSize = 0;

+    }

+

+    NetMapClean (&Instance->DataItem[Index].EventMap);

+  }

+

+  Ip6ConfigFormUnload (Instance);

+

+  RemoveEntryList (&Instance->Link);

+}

+

+/**

+  Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources.

+

+  @param[in, out] Instance    The buffer of IP6_CONFIG_INSTANCE to be freed.

+

+  @retval EFI_SUCCESS         The child was successfully destroyed.

+  @retval Others              Failed to destory the child.

+

+**/

+EFI_STATUS

+Ip6ConfigDestroyDhcp6 (

+  IN OUT IP6_CONFIG_INSTANCE  *Instance

+  )

+{

+  IP6_SERVICE                 *IpSb;

+  EFI_STATUS                  Status;

+  EFI_DHCP6_PROTOCOL          *Dhcp6;

+

+  Dhcp6 = Instance->Dhcp6;

+  ASSERT (Dhcp6 != NULL);

+

+  Dhcp6->Stop (Dhcp6);

+  Dhcp6->Configure (Dhcp6, NULL);

+  Instance->Dhcp6 = NULL;

+

+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+

+  //

+  // Close DHCPv6 protocol and destroy the child.

+  //

+  Status = gBS->CloseProtocol (

+                  Instance->Dhcp6Handle,

+                  &gEfiDhcp6ProtocolGuid,

+                  IpSb->Image,

+                  IpSb->Controller

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = NetLibDestroyServiceChild (

+             IpSb->Controller,

+             IpSb->Image,

+             &gEfiDhcp6ServiceBindingProtocolGuid,

+             Instance->Dhcp6Handle

+             );

+

+  Instance->Dhcp6Handle = NULL;

+

+  return Status;

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h
new file mode 100644
index 0000000..5ae4839
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h
@@ -0,0 +1,295 @@
+/** @file

+  Definitions for EFI IPv6 Configuartion Protocol implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __IP6_CONFIG_IMPL_H__

+#define __IP6_CONFIG_IMPL_H__

+

+#define IP6_CONFIG_INSTANCE_SIGNATURE    SIGNATURE_32 ('I', 'P', '6', 'C')

+#define IP6_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'F', 'C', 'I')

+#define IP6_CONFIG_VARIABLE_ATTRIBUTE    (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)

+

+#define IP6_CONFIG_DEFAULT_DAD_XMITS        1

+#define IP6_CONFIG_DHCP6_OPTION_ORO         6

+#define IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS 23

+

+#define DATA_ATTRIB_SIZE_FIXED              0x1

+#define DATA_ATTRIB_VOLATILE                0x2

+

+#define DATA_ATTRIB_SET(Attrib, Bits)       (BOOLEAN)((Attrib) & (Bits))

+#define SET_DATA_ATTRIB(Attrib, Bits)       ((Attrib) |= (Bits))

+

+typedef struct _IP6_CONFIG_INSTANCE IP6_CONFIG_INSTANCE;

+

+#define IP6_CONFIG_INSTANCE_FROM_PROTOCOL(Proto) \

+  CR ((Proto), \

+      IP6_CONFIG_INSTANCE, \

+      Ip6Config, \

+      IP6_CONFIG_INSTANCE_SIGNATURE \

+      )

+

+

+#define IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK(Callback) \

+  CR ((Callback), \

+      IP6_CONFIG_INSTANCE, \

+      CallbackInfo, \

+      IP6_CONFIG_INSTANCE_SIGNATURE \

+      )

+

+#define IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE(Instance) \

+  CR ((Instance), \

+      IP6_SERVICE, \

+      Ip6ConfigInstance, \

+      IP6_SERVICE_SIGNATURE \

+      )

+

+#define IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(ConfigAccess) \

+  CR ((ConfigAccess), \

+      IP6_FORM_CALLBACK_INFO, \

+      HiiConfigAccess, \

+      IP6_FORM_CALLBACK_INFO_SIGNATURE \

+      )

+

+/**

+  The prototype of work function for EfiIp6ConfigSetData().

+

+  @param[in]     Instance The pointer to the IP6 config instance data.

+  @param[in]     DataSize In bytes, the size of the buffer pointed to by Data.

+  @param[in]     Data     The data buffer to set.

+

+  @retval EFI_BAD_BUFFER_SIZE  The DataSize does not match the size of the type,

+                               8 bytes.

+  @retval EFI_SUCCESS          The specified configuration data for the EFI IPv6

+                               network stack was set successfully.

+  

+**/

+typedef

+EFI_STATUS

+(*IP6_CONFIG_SET_DATA) (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN UINTN                DataSize,

+  IN VOID                 *Data

+  );

+

+/**

+  The prototype of work function for EfiIp6ConfigGetData().

+

+  @param[in]      Instance The pointer to the IP6 config instance data.

+  @param[in, out] DataSize On input, in bytes, the size of Data. On output, in

+                           bytes, the size of buffer required to store the specified

+                           configuration data.

+  @param[in]      Data     The data buffer in which the configuration data is returned.  

+                           Ignored if DataSize is ZERO.

+

+  @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified

+                               configuration data, and the required size is 

+                               returned in DataSize.

+  @retval EFI_SUCCESS          The specified configuration data was obtained successfully.                               

+  

+**/

+typedef

+EFI_STATUS

+(*IP6_CONFIG_GET_DATA) (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN OUT UINTN            *DataSize,

+  IN VOID                 *Data      OPTIONAL

+  );

+

+typedef union {

+  VOID                                      *Ptr;

+  EFI_IP6_CONFIG_INTERFACE_INFO             *IfInfo;

+  EFI_IP6_CONFIG_INTERFACE_ID               *AltIfId;

+  EFI_IP6_CONFIG_POLICY                     *Policy;

+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  *DadXmits;

+  EFI_IP6_CONFIG_MANUAL_ADDRESS             *ManualAddress;

+  EFI_IPv6_ADDRESS                          *Gateway;

+  EFI_IPv6_ADDRESS                          *DnsServers;

+} IP6_CONFIG_DATA;

+

+typedef struct {

+  IP6_CONFIG_SET_DATA  SetData;

+  IP6_CONFIG_GET_DATA  GetData;

+  EFI_STATUS           Status;

+  UINT8                Attribute;

+  NET_MAP              EventMap;

+  IP6_CONFIG_DATA      Data;

+  UINTN                DataSize;

+} IP6_CONFIG_DATA_ITEM;

+

+typedef struct {

+  UINT16                    Offset;

+  UINTN                     DataSize;

+  EFI_IP6_CONFIG_DATA_TYPE  DataType;

+} IP6_CONFIG_DATA_RECORD;

+

+#pragma pack(1)

+

+//

+// heap data that contains the data for each data record.

+//

+//  BOOLEAN                                   IsAltIfIdSet;

+//  EFI_IP6_CONFIG_POLICY                     Policy;

+//  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  DadXmits;

+//  UINT32                                    ManualaddressCount;

+//  UINT32                                    GatewayCount;

+//  UINT32                                    DnsServersCount;

+//  EFI_IP6_CONFIG_INTERFACE_ID               AltIfId;

+//  EFI_IP6_CONFIG_MANUAL_ADDRESS             ManualAddress[];

+//  EFI_IPv6_ADDRESS                          Gateway[];

+//  EFI_IPv6_ADDRESS                          DnsServers[];

+//

+typedef struct {

+  UINT32                  IaId;

+  UINT16                  Checksum;

+  UINT16                  DataRecordCount;

+  IP6_CONFIG_DATA_RECORD  DataRecord[1];

+} IP6_CONFIG_VARIABLE;

+

+#pragma pack()

+

+typedef struct {

+  LIST_ENTRY                  Link;

+  EFI_IP6_ADDRESS_INFO        AddrInfo;

+} IP6_ADDRESS_INFO_ENTRY;

+

+typedef struct {

+  EFI_IP6_CONFIG_POLICY                    Policy;              ///< manual or automatic  

+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadTransmitCount;    ///< dad transmits count

+  EFI_IP6_CONFIG_INTERFACE_ID              InterfaceId;         ///< alternative interface id 

+  LIST_ENTRY                               ManualAddress;       ///< IP addresses

+  UINT32                                   ManualAddressCount;  ///< IP addresses count

+  LIST_ENTRY                               GatewayAddress;      ///< Gateway address

+  UINT32                                   GatewayAddressCount; ///< Gateway address count

+  LIST_ENTRY                               DnsAddress;          ///< DNS server address

+  UINT32                                   DnsAddressCount;     ///< DNS server address count

+} IP6_CONFIG_NVDATA;

+

+typedef struct _IP6_FORM_CALLBACK_INFO {

+  UINT32                           Signature;

+  EFI_HANDLE                       ChildHandle;

+  EFI_HII_CONFIG_ACCESS_PROTOCOL   HiiConfigAccess;

+  EFI_DEVICE_PATH_PROTOCOL         *HiiVendorDevicePath;

+  EFI_HII_HANDLE                   RegisteredHandle;

+} IP6_FORM_CALLBACK_INFO;

+

+struct _IP6_CONFIG_INSTANCE {

+  UINT32                                    Signature;

+  BOOLEAN                                   Configured;

+  LIST_ENTRY                                Link;

+  UINT16                                    IfIndex;

+

+  EFI_IP6_CONFIG_INTERFACE_INFO             InterfaceInfo;

+  EFI_IP6_CONFIG_INTERFACE_ID               AltIfId;

+  EFI_IP6_CONFIG_POLICY                     Policy;

+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  DadXmits;

+

+  IP6_CONFIG_DATA_ITEM                      DataItem[Ip6ConfigDataTypeMaximum];

+  NET_MAP                                   DadFailedMap;

+  NET_MAP                                   DadPassedMap;

+

+  EFI_IP6_CONFIG_PROTOCOL                   Ip6Config;

+

+  EFI_EVENT                                 Dhcp6SbNotifyEvent;

+  VOID                                      *Registration;

+  EFI_HANDLE                                Dhcp6Handle;

+  EFI_DHCP6_PROTOCOL                        *Dhcp6;

+  BOOLEAN                                   OtherInfoOnly;

+  UINT32                                    IaId;

+  EFI_EVENT                                 Dhcp6Event;

+  UINT32                                    FailedIaAddressCount;

+  EFI_IPv6_ADDRESS                          *DeclineAddress;

+  UINT32                                    DeclineAddressCount;

+

+  IP6_FORM_CALLBACK_INFO                    CallbackInfo;

+  IP6_CONFIG_NVDATA                         Ip6NvData;

+};

+

+/**

+  The event process routine when the DHCPv6 server is answered with a reply packet

+  for an information request.

+  

+  @param[in]     This          Points to the EFI_DHCP6_PROTOCOL.

+  @param[in]     Context       The pointer to the IP6 configuration instance data.

+  @param[in]     Packet        The DHCPv6 reply packet.

+

+  @retval EFI_SUCCESS      The DNS server address was retrieved from the reply packet.

+  @retval EFI_NOT_READY    The reply packet does not contain the DNS server option, or 

+                           the DNS server address is not valid.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ConfigOnDhcp6Reply (

+  IN EFI_DHCP6_PROTOCOL  *This,

+  IN VOID                *Context,

+  IN EFI_DHCP6_PACKET    *Packet

+  );

+

+/**

+  The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration.

+  

+  @param[in]     Instance      Pointer to the IP6 config instance data.

+  @param[in]     OtherInfoOnly If FALSE, get stateful address and other information

+                               via DHCPv6. Otherwise, only get the other information.

+

+  @retval    EFI_SUCCESS       The operation finished successfully.

+  @retval    EFI_UNSUPPORTED   The DHCP6 driver is not available.

+

+**/

+EFI_STATUS

+Ip6ConfigStartStatefulAutoConfig (

+  IN IP6_CONFIG_INSTANCE  *Instance,

+  IN BOOLEAN              OtherInfoOnly

+  );

+

+/**

+  Initialize an IP6_CONFIG_INSTANCE.

+

+  @param[out]    Instance       The buffer of IP6_CONFIG_INSTANCE to be initialized.

+  

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to complete the operation.

+  @retval EFI_SUCCESS           The IP6_CONFIG_INSTANCE initialized successfully.

+  

+**/

+EFI_STATUS

+Ip6ConfigInitInstance (

+  OUT IP6_CONFIG_INSTANCE  *Instance

+  );

+

+/**

+  Release an IP6_CONFIG_INSTANCE.

+

+  @param[in, out] Instance    The buffer of IP6_CONFIG_INSTANCE to be freed.

+  

+**/

+VOID

+Ip6ConfigCleanInstance (

+  IN OUT IP6_CONFIG_INSTANCE  *Instance

+  );

+

+/**

+  Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources.

+

+  @param[in, out] Instance    The buffer of IP6_CONFIG_INSTANCE to be freed.

+

+  @retval EFI_SUCCESS         The child was successfully destroyed.

+  @retval Others              Failed to destory the child.

+  

+**/

+EFI_STATUS

+Ip6ConfigDestroyDhcp6 (

+  IN OUT IP6_CONFIG_INSTANCE  *Instance

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c
new file mode 100644
index 0000000..9ec4886
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c
@@ -0,0 +1,2116 @@
+/** @file

+  Helper functions for configuring or obtaining the parameters relating to IP6.

+

+  Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+EFI_GUID  mIp6HiiVendorDevicePathGuid = IP6_HII_VENDOR_DEVICE_PATH_GUID;

+EFI_GUID  mIp6ConfigNvDataGuid        = IP6_CONFIG_NVDATA_GUID;

+CHAR16    mIp6ConfigStorageName[]     = L"IP6_CONFIG_IFR_NVDATA";

+

+/**

+  The notify function of create event when performing a manual configuration.

+

+  @param[in]    Event        The pointer of Event.

+  @param[in]    Context      The pointer of Context.

+

+**/

+VOID

+EFIAPI

+Ip6ConfigManualAddressNotify (

+  IN EFI_EVENT    Event,

+  IN VOID         *Context

+  )

+{

+  *((BOOLEAN *) Context) = TRUE;

+}

+

+/**

+  Get the configuration data for the EFI IPv6 network stack running on the

+  communication. It is a help function to the call EfiIp6ConfigGetData().

+

+  @param[in]      Ip6Config      The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.

+  @param[in]      DataType       The type of data to get.

+  @param[out]     DataSize       The size of buffer required in bytes.

+  @param[out]     Data           The data buffer in which the configuration data is returned. The

+                                 type of the data buffer associated with the DataType.

+                                 It is the caller's responsibility to free the resource.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:

+                                - Ip6Config is NULL or invalid.

+                                - DataSize is NULL.

+                                - Data is NULL.

+  @retval EFI_OUT_OF_RESOURCES  Fail to perform the operation due to lack of resources.

+  @retval EFI_NOT_READY         The specified configuration data is not ready due to an

+                                asynchronous configuration process already in progress.

+  @retval EFI_NOT_FOUND         The specified configuration data was not found.

+

+**/

+EFI_STATUS

+Ip6ConfigNvGetData (

+  IN  EFI_IP6_CONFIG_PROTOCOL                *Ip6Config,

+  IN  EFI_IP6_CONFIG_DATA_TYPE               DataType,

+  OUT UINTN                                  *DataSize,

+  OUT VOID                                   **Data

+  )

+{

+  UINTN                   BufferSize;

+  VOID                    *Buffer;

+  EFI_STATUS              Status;

+

+  if ((Ip6Config == NULL) || (Data == NULL) || (DataSize == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  BufferSize = 0;

+  Status = Ip6Config->GetData (

+                        Ip6Config,

+                        DataType,

+                        &BufferSize,

+                        NULL

+                        );

+  if (Status != EFI_BUFFER_TOO_SMALL) {

+    return Status;

+  }

+

+  Buffer = AllocateZeroPool (BufferSize);

+  if (Buffer == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = Ip6Config->GetData (

+                        Ip6Config,

+                        DataType,

+                        &BufferSize,

+                        Buffer

+                        );

+  if (EFI_ERROR (Status)) {

+    FreePool (Buffer);

+    return Status;

+  }

+

+  *DataSize = BufferSize;

+  *Data     = Buffer;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Free all nodes in IP6_ADDRESS_INFO_ENTRY in the list array specified

+  with ListHead.

+

+  @param[in]      ListHead  The head of the list array in IP6_ADDRESS_INFO_ENTRY.

+

+**/

+VOID

+Ip6FreeAddressInfoList (

+  IN LIST_ENTRY                  *ListHead

+  )

+{

+  IP6_ADDRESS_INFO_ENTRY         *Node;

+  LIST_ENTRY                     *Entry;

+  LIST_ENTRY                     *NextEntry;

+

+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, ListHead) {

+    Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link);

+    RemoveEntryList (&Node->Link);

+    FreePool (Node);

+  }

+}

+

+/**

+  Convert the IPv6 address into a formatted string.

+

+  @param[in]  Ip6       The IPv6 address.

+  @param[out] Str       The formatted IP string.

+

+**/

+VOID

+Ip6ToStr (

+  IN  EFI_IPv6_ADDRESS  *Ip6,

+  OUT CHAR16            *Str

+  )

+{

+  UINTN                 Index;

+  BOOLEAN               Short;

+  UINTN                 Number;

+  CHAR16                FormatString[8];

+

+  Short = FALSE;

+

+  for (Index = 0; Index < 15; Index = Index + 2) {

+    if (!Short &&

+        Index % 2 == 0 &&

+        Ip6->Addr[Index] == 0 &&

+        Ip6->Addr[Index + 1] == 0

+        ) {

+      //

+      // Deal with the case of ::.

+      //

+      if (Index == 0) {

+        *Str       = L':';

+        *(Str + 1) = L':';

+        Str        = Str + 2;

+      } else {

+        *Str       = L':';

+        Str        = Str + 1;

+      }

+

+      while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) {

+        Index = Index + 2;

+      }

+

+      Short = TRUE;

+

+      if (Index == 16) {

+        //

+        // :: is at the end of the address.

+        //

+        *Str = L'\0';

+        break;

+      }

+    }

+

+    ASSERT (Index < 15);

+

+    if (Ip6->Addr[Index] == 0) {

+      Number = UnicodeSPrint (Str, 2 * IP6_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]);

+    } else {

+      if (Ip6->Addr[Index + 1] < 0x10) {

+        CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:"));

+      } else {

+        CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:"));

+      }

+

+      Number = UnicodeSPrint (

+                 Str,

+                 2 * IP6_STR_MAX_SIZE,

+                 (CONST CHAR16 *) FormatString,

+                 (UINTN) Ip6->Addr[Index],

+                 (UINTN) Ip6->Addr[Index + 1]

+                 );

+    }

+

+    Str = Str + Number;

+

+    if (Index + 2 == 16) {

+      *Str = L'\0';

+      if (*(Str - 1) == L':') {

+        *(Str - 1) = L'\0';

+      }

+    }

+  }

+}

+

+/**

+  Convert EFI_IP6_CONFIG_INTERFACE_ID to string format.

+

+  @param[out]      String  The buffer to store the converted string.

+  @param[in]       IfId    The pointer of EFI_IP6_CONFIG_INTERFACE_ID.

+

+  @retval EFI_SUCCESS              The string converted successfully.

+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.

+

+**/

+EFI_STATUS

+Ip6ConvertInterfaceIdToString (

+  OUT CHAR16                         *String,

+  IN  EFI_IP6_CONFIG_INTERFACE_ID    *IfId

+  )

+{

+  UINT8                          Index;

+  UINTN                          Number;

+

+  if ((String == NULL) || (IfId == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  for (Index = 0; Index < 8; Index++) {

+    Number = UnicodeSPrint (

+               String,

+               2 * INTERFACE_ID_STR_STORAGE,

+               L"%x:",

+               (UINTN) IfId->Id[Index]

+               );

+    String = String + Number;

+  }

+

+  *(String - 1) = '\0';

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Parse InterfaceId in string format and convert it to EFI_IP6_CONFIG_INTERFACE_ID.

+

+  @param[in]        String  The buffer of the string to be parsed.

+  @param[out]       IfId    The pointer of EFI_IP6_CONFIG_INTERFACE_ID.

+

+  @retval EFI_SUCCESS              The operation finished successfully.

+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.

+

+**/

+EFI_STATUS

+Ip6ParseInterfaceIdFromString (

+  IN CONST CHAR16                    *String,

+  OUT EFI_IP6_CONFIG_INTERFACE_ID    *IfId

+  )

+{

+  UINT8                          Index;

+  CHAR16                         *IfIdStr;

+  CHAR16                         *TempStr;

+  UINTN                          NodeVal;

+

+  if ((String == NULL) || (IfId == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IfIdStr = (CHAR16 *) String;

+

+  ZeroMem (IfId, sizeof (EFI_IP6_CONFIG_INTERFACE_ID));

+

+  for (Index = 0; Index < 8; Index++) {

+    TempStr = IfIdStr;

+

+    while ((*IfIdStr != L'\0') && (*IfIdStr != L':')) {

+      IfIdStr++;

+    }

+

+    //

+    // The InterfaceId format is X:X:X:X, the number of X should not exceed 8.

+    // If the number of X is less than 8, zero is appended to the InterfaceId.

+    //

+    if ((*IfIdStr == ':') && (Index == 7)) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    //

+    // Convert the string to interface id. AsciiStrHexToUintn stops at the

+    // first character that is not a valid hex character, ':' or '\0' here.

+    //

+    NodeVal = StrHexToUintn (TempStr);

+    if (NodeVal > 0xFF) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    IfId->Id[Index] = (UINT8) NodeVal;

+

+    IfIdStr++;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Create Hii Extend Label OpCode as the start opcode and end opcode. It is

+  a help function.

+

+  @param[in]  StartLabelNumber   The number of start label.

+  @param[out] StartOpCodeHandle  Points to the start opcode handle.

+  @param[out] StartLabel         Points to the created start opcode.

+  @param[out] EndOpCodeHandle    Points to the end opcode handle.

+  @param[out] EndLabel           Points to the created end opcode.

+

+  @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this

+                                 operation.

+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.

+  @retval EFI_SUCCESS            The operation completed successfully.

+

+**/

+EFI_STATUS

+Ip6CreateOpCode (

+  IN  UINT16                        StartLabelNumber,

+  OUT VOID                          **StartOpCodeHandle,

+  OUT EFI_IFR_GUID_LABEL            **StartLabel,

+  OUT VOID                          **EndOpCodeHandle,

+  OUT EFI_IFR_GUID_LABEL            **EndLabel

+  )

+{

+  EFI_STATUS                        Status;

+  EFI_IFR_GUID_LABEL                *InternalStartLabel;

+  EFI_IFR_GUID_LABEL                *InternalEndLabel;

+

+  if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  *StartOpCodeHandle = NULL;

+  *EndOpCodeHandle   = NULL;

+  Status             = EFI_OUT_OF_RESOURCES;

+

+  //

+  // Initialize the container for dynamic opcodes.

+  //

+  *StartOpCodeHandle = HiiAllocateOpCodeHandle ();

+  if (*StartOpCodeHandle == NULL) {

+    return Status;

+  }

+

+  *EndOpCodeHandle = HiiAllocateOpCodeHandle ();

+  if (*EndOpCodeHandle == NULL) {

+    goto Exit;

+  }

+

+  //

+  // Create Hii Extend Label OpCode as the start opcode.

+  //

+  InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (

+                                                *StartOpCodeHandle,

+                                                &gEfiIfrTianoGuid,

+                                                NULL,

+                                                sizeof (EFI_IFR_GUID_LABEL)

+                                                );

+  if (InternalStartLabel == NULL) {

+    goto Exit;

+  }

+

+  InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;

+  InternalStartLabel->Number       = StartLabelNumber;

+

+  //

+  // Create Hii Extend Label OpCode as the end opcode.

+  //

+  InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (

+                                              *EndOpCodeHandle,

+                                              &gEfiIfrTianoGuid,

+                                              NULL,

+                                              sizeof (EFI_IFR_GUID_LABEL)

+                                              );

+  if (InternalEndLabel == NULL) {

+    goto Exit;

+  }

+

+  InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;

+  InternalEndLabel->Number       = LABEL_END;

+

+  *StartLabel = InternalStartLabel;

+  *EndLabel   = InternalEndLabel;

+

+  return EFI_SUCCESS;

+

+Exit:

+

+  if (*StartOpCodeHandle != NULL) {

+    HiiFreeOpCodeHandle (*StartOpCodeHandle);

+  }

+

+  if (*EndOpCodeHandle != NULL) {

+    HiiFreeOpCodeHandle (*EndOpCodeHandle);

+  }

+

+  return Status;

+}

+

+/**

+  This function converts the different format of address list to string format and

+  then generates the corresponding text opcode to illustarate the address info in

+  IP6 configuration page. Currently, the following formats are supported:

+  EFI_IP6_ADDRESS_INFO AddressType: Ip6ConfigNvHostAddress;

+  EFI_IPv6_ADDRESS     AddressType: Ip6ConfigNvGatewayAddress and Ip6ConfigNvDnsAddress;

+  EFI_IP6_ROUTE_TABLE  AddressType: Ip6ConfigNvRouteTable.

+

+  @param[in, out] String           The pointer to the buffer to store the converted

+                                   string.

+  @param[in]      HiiHandle        A handle that was previously registered in the

+                                   HII Database.

+  @param[in]      AddressType      The address type.

+  @param[in]      AddressInfo      Pointer to the address list.

+  @param[in]      AddressCount     The address count of the address list.

+

+  @retval EFI_SUCCESS              The operation finished successfully.

+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.

+  @retval EFI_UNSUPPORTED          The AddressType is not supported.

+

+

+**/

+EFI_STATUS

+Ip6ConvertAddressListToString (

+  IN OUT CHAR16                         *String,

+  IN     EFI_HII_HANDLE                 HiiHandle,

+  IN     IP6_CONFIG_NV_ADDRESS_TYPE     AddressType,

+  IN     VOID                           *AddressInfo,

+  IN     UINTN                          AddressCount

+  )

+{

+  UINTN                          Index;

+  UINTN                          Number;

+  CHAR16                         *TempStr;

+  EFI_STATUS                     Status;

+  VOID                           *StartOpCodeHandle;

+  EFI_IFR_GUID_LABEL             *StartLabel;

+  VOID                           *EndOpCodeHandle;

+  EFI_IFR_GUID_LABEL             *EndLabel;

+  UINT16                         StartLabelNumber;

+  EFI_STRING_ID                  TextTwo;

+  UINT8                          *AddressHead;

+  UINT8                          PrefixLength;

+  EFI_IPv6_ADDRESS               *Address;

+

+  if ((String == NULL) || (HiiHandle == NULL) || (AddressInfo == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (AddressType == Ip6ConfigNvHostAddress) {

+    StartLabelNumber = HOST_ADDRESS_LABEL;

+  } else if (AddressType == Ip6ConfigNvGatewayAddress) {

+    StartLabelNumber = GATEWAY_ADDRESS_LABEL;

+  } else if (AddressType == Ip6ConfigNvDnsAddress) {

+    StartLabelNumber = DNS_ADDRESS_LABEL;

+  } else if (AddressType == Ip6ConfigNvRouteTable) {

+    StartLabelNumber = ROUTE_TABLE_LABEL;

+  } else {

+    ASSERT (FALSE);

+    return EFI_UNSUPPORTED;

+  }

+

+  Status = Ip6CreateOpCode (

+             StartLabelNumber,

+             &StartOpCodeHandle,

+             &StartLabel,

+             &EndOpCodeHandle,

+             &EndLabel

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  AddressHead = (UINT8 *) AddressInfo;

+

+  for (Index = 0; Index < AddressCount; Index++) {

+    if (AddressType == Ip6ConfigNvHostAddress) {

+      AddressInfo = AddressHead + sizeof (EFI_IP6_ADDRESS_INFO) * Index;

+      Address     = &((EFI_IP6_ADDRESS_INFO *) AddressInfo)->Address;

+    } else if (AddressType == Ip6ConfigNvRouteTable) {

+      AddressInfo = AddressHead + sizeof (EFI_IP6_ROUTE_TABLE) * Index;

+      Address     = &((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Destination;

+    } else {

+      AddressInfo = AddressHead + sizeof (EFI_IPv6_ADDRESS) * Index;

+      Address     = AddressInfo;

+    }

+

+    //

+    // Convert the IP address info to string.

+    //

+    Ip6ToStr (Address, String);

+    TempStr = String + StrLen (String);

+

+    if ((AddressType == Ip6ConfigNvHostAddress) || (AddressType == Ip6ConfigNvRouteTable)) {

+      if (AddressType == Ip6ConfigNvHostAddress) {

+        PrefixLength = ((EFI_IP6_ADDRESS_INFO *) AddressInfo)->PrefixLength;

+      } else {

+        PrefixLength = ((EFI_IP6_ROUTE_TABLE *) AddressInfo)->PrefixLength;

+      }

+

+      //

+      // Append the prefix length to the string.

+      //

+      *TempStr = L'/';

+      TempStr++;

+      Number  = UnicodeSPrint (TempStr, 6, L"%d", PrefixLength);

+      TempStr = TempStr + Number;

+    }

+

+    if (AddressType == Ip6ConfigNvRouteTable) {

+      //

+      // Append " >> " to the string.

+      //

+      Number   = UnicodeSPrint (TempStr, 8, L" >>  ");

+      TempStr  = TempStr + Number;

+

+      //

+      // Append the gateway address to the string.

+      //

+      Ip6ToStr (&((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Gateway, TempStr);

+      TempStr = TempStr + StrLen (TempStr);

+    }

+

+    //

+    // Generate a text opcode and update the UI.

+    //

+    TextTwo = HiiSetString (HiiHandle, 0, String, NULL);

+    if (TextTwo == 0) {

+      Status = EFI_INVALID_PARAMETER;

+      goto Exit;

+    }

+

+    HiiCreateTextOpCode (StartOpCodeHandle, STR_NULL, STR_NULL, TextTwo);

+

+    String = TempStr;

+    *String = IP6_ADDRESS_DELIMITER;

+    String++;

+  }

+

+  *(String - 1) = '\0';

+

+  Status = HiiUpdateForm (

+             HiiHandle,                       // HII handle

+             &mIp6ConfigNvDataGuid,           // Formset GUID

+             FORMID_MAIN_FORM,                // Form ID

+             StartOpCodeHandle,               // Label for where to insert opcodes

+             EndOpCodeHandle                  // Replace data

+             );

+

+Exit:

+  HiiFreeOpCodeHandle (StartOpCodeHandle);

+  HiiFreeOpCodeHandle (EndOpCodeHandle);

+

+  return Status;

+}

+

+/**

+  Parse address list in string format and convert it to a list array of node in

+  IP6_ADDRESS_INFO_ENTRY.

+

+  @param[in]        String         The buffer to string to be parsed.

+  @param[out]       ListHead       The list head of array.

+  @param[out]       AddressCount   The number of list nodes in the array.

+

+  @retval EFI_SUCCESS              The operation finished successfully.

+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES     Failed to perform the operation due to lack of resource.

+

+**/

+EFI_STATUS

+Ip6ParseAddressListFromString (

+  IN CONST CHAR16                    *String,

+  OUT LIST_ENTRY                     *ListHead,

+  OUT UINT32                         *AddressCount

+  )

+{

+  EFI_STATUS                     Status;

+  CHAR16                         *LocalString;

+  CHAR16                         *Temp;

+  CHAR16                         *TempStr;

+  EFI_IP6_ADDRESS_INFO           AddressInfo;

+  IP6_ADDRESS_INFO_ENTRY         *Node;

+  BOOLEAN                        Last;

+  UINT32                         Count;

+

+  if ((String == NULL) || (ListHead == NULL) || (AddressCount == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  LocalString = (CHAR16 *) AllocateCopyPool (StrSize (String), String);

+  if (LocalString == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Clean the original address list.

+  //

+  Ip6FreeAddressInfoList (ListHead);

+

+  Temp  = LocalString;

+  Last  = FALSE;

+  Count = 0;

+

+  while (*LocalString != L'\0') {

+    TempStr = LocalString;

+    while ((*LocalString != L'\0') && (*LocalString != IP6_ADDRESS_DELIMITER)) {

+      LocalString++;

+    }

+

+    if (*LocalString == L'\0') {

+      Last = TRUE;

+    }

+

+    *LocalString = L'\0';

+

+    Status = NetLibStrToIp6andPrefix (TempStr, &AddressInfo.Address, &AddressInfo.PrefixLength);

+    if (EFI_ERROR (Status)) {

+      goto Error;

+    }

+

+    if (AddressInfo.PrefixLength == 0xFF) {

+      AddressInfo.PrefixLength = 0;

+    }

+

+    if (!NetIp6IsValidUnicast (&AddressInfo.Address)) {

+      Status = EFI_INVALID_PARAMETER;

+      goto Error;

+    }

+

+    Node = AllocatePool (sizeof (IP6_ADDRESS_INFO_ENTRY));

+    if (Node == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto Error;

+    }

+

+    CopyMem (&Node->AddrInfo, &AddressInfo, sizeof (EFI_IP6_ADDRESS_INFO));

+    InsertTailList (ListHead, &Node->Link);

+    Count++;

+

+    if (Last) {

+      break;

+    }

+

+    LocalString++;

+  }

+

+  FreePool (Temp);

+  *AddressCount = Count;

+  return EFI_SUCCESS;

+

+Error:

+  Ip6FreeAddressInfoList (ListHead);

+  FreePool (Temp);

+  return Status;

+}

+

+/**

+  This function converts the interface info to string and draws it to the IP6 UI.

+  The interface information includes interface name, interface type, hardware address,

+  address info, and route table information. The address information is also used as the

+  content of manual addresses in IP6 UI.

+

+  @param[in]       IfInfo          The pointer of EFI_IP6_CONFIG_INTERFACE_INFO.

+  @param[in]       HiiHandle       The handle that was previously registered in the

+                                   HII Database.

+  @param[in, out]  IfrNvData       Points to IP6_CONFIG_IFR_NVDATA.

+

+  @retval EFI_SUCCESS              The operation finished successfully.

+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES     The operation failed due to lack of resources.

+

+**/

+EFI_STATUS

+Ip6ConvertInterfaceInfoToString (

+  IN     EFI_IP6_CONFIG_INTERFACE_INFO  *IfInfo,

+  IN     EFI_HII_HANDLE                 HiiHandle,

+  IN OUT IP6_CONFIG_IFR_NVDATA          *IfrNvData

+  )

+{

+  UINT32                         Index;

+  UINTN                          Number;

+  CHAR16                         *String;

+  CHAR16                         *LinkLocalStr;

+  CHAR16                         PortString[ADDRESS_STR_MAX_SIZE];

+  CHAR16                         FormatString[8];

+  EFI_STRING_ID                  StringId;

+  EFI_STATUS                     Status;

+

+  if ((IfInfo == NULL) || (HiiHandle == NULL) || (IfrNvData == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Print the interface name.

+  //

+  StringId = HiiSetString (

+               HiiHandle,

+               STRING_TOKEN (STR_IP6_INTERFACE_NAME_CONTENT),

+               IfInfo->Name,

+               NULL

+               );

+  if (StringId == 0) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Print the interface type.

+  //

+  if (IfInfo->IfType == Ip6InterfaceTypeEthernet) {

+    StrCpy (PortString, IP6_ETHERNET);

+  } else if (IfInfo->IfType == Ip6InterfaceTypeExperimentalEthernet) {

+    StrCpy (PortString, IP6_EXPERIMENTAL_ETHERNET);

+  } else {

+    //

+    // Refer to RFC1700, chapter Number Hardware Type.

+    //

+    UnicodeSPrint (PortString, 6, L"%d", IfInfo->IfType);

+  }

+

+  StringId = HiiSetString (

+               HiiHandle,

+               STRING_TOKEN (STR_IP6_INTERFACE_TYPE_CONTENT),

+               PortString,

+               NULL

+               );

+  if (StringId == 0) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Convert the hardware address.

+  //

+  String = PortString;

+  ASSERT (IfInfo->HwAddressSize <= 32);

+

+  for (Index = 0; Index < IfInfo->HwAddressSize; Index++) {

+

+    if (IfInfo->HwAddress.Addr[Index] < 0x10) {

+      StrCpy (FormatString, L"0%x-");

+    } else {

+      StrCpy (FormatString, L"%x-");

+    }

+

+    Number = UnicodeSPrint (

+               String,

+               8,

+               (CONST CHAR16 *) FormatString,

+               (UINTN) IfInfo->HwAddress.Addr[Index]

+               );

+    String = String + Number;

+  }

+

+  if (Index != 0) {

+    ASSERT (String > PortString);

+    String--;

+    *String = '\0';

+  }

+

+  //

+  // Print the hardware address.

+  //

+  StringId = HiiSetString (

+               HiiHandle,

+               STRING_TOKEN (STR_IP6_MAC_ADDRESS_CONTENT),

+               PortString,

+               NULL

+               );

+  if (StringId == 0) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Print the host address Information.

+  //

+  Status = Ip6ConvertAddressListToString (

+             PortString,

+             HiiHandle,

+             Ip6ConfigNvHostAddress,

+             IfInfo->AddressInfo,

+             IfInfo->AddressInfoCount

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Copy the Host Address Info to manual address field.

+  // Do not copy the link local address.

+  //

+  LinkLocalStr = StrStr (PortString, IP6_LINK_LOCAL_PREFIX);

+  if (LinkLocalStr != NULL) {

+    Number = LinkLocalStr - PortString;

+    if (Number > 0) {

+      CopyMem (IfrNvData->ManualAddress, PortString, Number * sizeof (CHAR16));

+    }

+

+    while ((*LinkLocalStr != L' ') && (*LinkLocalStr != L'\0')) {

+      LinkLocalStr++;

+    }

+

+    if (*LinkLocalStr != L'\0') {

+      LinkLocalStr++;

+      StrCat (IfrNvData->ManualAddress, LinkLocalStr);

+    }

+  } else {

+    StrCpy (IfrNvData->ManualAddress, PortString);

+  }

+

+  //

+  // Print the route table information.

+  //

+  Status = Ip6ConvertAddressListToString (

+             PortString,

+             HiiHandle,

+             Ip6ConfigNvRouteTable,

+             IfInfo->RouteTable,

+             IfInfo->RouteCount

+             );

+  return Status;

+}

+

+/**

+  Build the address info list from list array of node in IP6_ADDRESS_INFO_ENTRY.

+

+  @param[in]      Instance         Points to IP6 config instance data.

+  @param[in]      AddressType      The address type.

+  @param[out]     AddressInfo      The pointer to the buffer to store the address list.

+  @param[out]     AddressSize      The address size of the address list.

+

+  @retval EFI_SUCCESS              The operation finished successfully.

+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.

+  @retval EFI_UNSUPPORTED          The AddressType is not supported.

+

+**/

+EFI_STATUS

+Ip6BuildNvAddressInfo (

+  IN  IP6_CONFIG_INSTANCE            *Instance,

+  IN  IP6_CONFIG_NV_ADDRESS_TYPE     AddressType,

+  OUT VOID                           **AddressInfo,

+  OUT UINTN                          *AddressSize

+  )

+{

+  IP6_CONFIG_NVDATA                  *Ip6NvData;

+  LIST_ENTRY                         *Entry;

+  LIST_ENTRY                         *ListHead;

+  IP6_ADDRESS_INFO_ENTRY             *Node;

+  VOID                               *AddressList;

+  VOID                               *TmpStr;

+  UINTN                              DataSize;

+  EFI_IPv6_ADDRESS                   *Ip6Address;

+  EFI_IP6_CONFIG_MANUAL_ADDRESS      *ManualAddress;

+

+  if ((Instance == NULL) || (AddressInfo == NULL) || (AddressSize == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);

+

+  Ip6NvData = &Instance->Ip6NvData;

+

+  if (AddressType == Ip6ConfigNvHostAddress) {

+    ListHead = &Ip6NvData->ManualAddress;

+    DataSize = sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS) * Ip6NvData->ManualAddressCount;

+  } else if (AddressType == Ip6ConfigNvGatewayAddress) {

+    ListHead = &Ip6NvData->GatewayAddress;

+    DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->GatewayAddressCount;

+  } else if (AddressType == Ip6ConfigNvDnsAddress) {

+    ListHead = &Ip6NvData->DnsAddress;

+    DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->DnsAddressCount;

+  } else {

+    return EFI_UNSUPPORTED;

+  }

+

+  AddressList = AllocateZeroPool (DataSize);

+  if (AddressList  == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  TmpStr = AddressList;

+

+  NET_LIST_FOR_EACH (Entry, ListHead) {

+    Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link);

+    if (AddressType == Ip6ConfigNvHostAddress) {

+      ManualAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AddressList;

+      IP6_COPY_ADDRESS (&ManualAddress->Address, &Node->AddrInfo.Address);

+      ManualAddress->PrefixLength = Node->AddrInfo.PrefixLength;

+      AddressList = (UINT8 *) AddressList + sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);

+    } else {

+      Ip6Address = (EFI_IPv6_ADDRESS *) AddressList;

+      IP6_COPY_ADDRESS (Ip6Address, &Node->AddrInfo.Address);

+      AddressList = (UINT8 *) AddressList + sizeof (EFI_IPv6_ADDRESS);

+    }

+  }

+

+  *AddressInfo = TmpStr;

+  *AddressSize = DataSize;

+  return EFI_SUCCESS;

+}

+

+/**

+  Convert the IP6 configuration data into the IFR data.

+

+  @param[in, out]  IfrNvData       The IFR NV data.

+  @param[in]       Instance        The IP6 config instance data.

+

+  @retval EFI_SUCCESS              The operation finished successfully.

+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.

+  @retval EFI_UNSUPPORTED          The policy is not supported in the current implementation.

+  @retval Others                   Other errors as indicated.

+

+**/

+EFI_STATUS

+Ip6ConvertConfigNvDataToIfrNvData (

+  IN OUT IP6_CONFIG_IFR_NVDATA       *IfrNvData,

+  IN     IP6_CONFIG_INSTANCE         *Instance

+  )

+{

+  EFI_IP6_CONFIG_PROTOCOL                    *Ip6Config;

+  UINTN                                      DataSize;

+  VOID                                       *Data;

+  EFI_STATUS                                 Status;

+  EFI_IP6_CONFIG_INTERFACE_ID                InterfaceId;

+  EFI_IP6_CONFIG_POLICY                      Policy;

+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS   DadXmits;

+  EFI_HII_HANDLE                             HiiHandle;

+

+  if ((IfrNvData == NULL) || (Instance == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);

+

+  Ip6Config = &Instance->Ip6Config;

+  Data      = NULL;

+  DataSize  = 0;

+  HiiHandle = Instance->CallbackInfo.RegisteredHandle;

+

+  //

+  // Get the current interface info.

+  //

+  Status = Ip6ConfigNvGetData (

+             Ip6Config,

+             Ip6ConfigDataTypeInterfaceInfo,

+             &DataSize,

+             (VOID **) &Data

+             );

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  //

+  // Convert the interface info to string and print.

+  //

+  Status = Ip6ConvertInterfaceInfoToString (

+             (EFI_IP6_CONFIG_INTERFACE_INFO *) Data,

+             HiiHandle,

+             IfrNvData

+             );

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  //

+  // Get the interface id.

+  //

+  DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);

+  ZeroMem (&InterfaceId, DataSize);

+  Status = Ip6Config->GetData (

+                        Ip6Config,

+                        Ip6ConfigDataTypeAltInterfaceId,

+                        &DataSize,

+                        &InterfaceId

+                        );

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  Ip6ConvertInterfaceIdToString (IfrNvData->InterfaceId, &InterfaceId);

+

+  //

+  // Get current policy.

+  //

+  DataSize = sizeof (EFI_IP6_CONFIG_POLICY);

+  Status   = Ip6Config->GetData (

+                          Ip6Config,

+                          Ip6ConfigDataTypePolicy,

+                          &DataSize,

+                          &Policy

+                          );

+

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  if (Policy == Ip6ConfigPolicyManual) {

+    IfrNvData->Policy = IP6_POLICY_MANUAL;

+  } else if (Policy == Ip6ConfigPolicyAutomatic) {

+    IfrNvData->Policy = IP6_POLICY_AUTO;

+  } else {

+    ASSERT (FALSE);

+    Status = EFI_UNSUPPORTED;

+    goto Exit;

+  }

+

+  //

+  // Get Duplicate Address Detection Transmits count.

+  //

+  DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);

+  Status   = Ip6Config->GetData (

+                          Ip6Config,

+                          Ip6ConfigDataTypeDupAddrDetectTransmits,

+                          &DataSize,

+                          &DadXmits

+                          );

+

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  IfrNvData->DadTransmitCount = DadXmits.DupAddrDetectTransmits;

+

+  //

+  // Get DNS server list.

+  //

+  FreePool (Data);

+  Data     = NULL;

+  DataSize = 0;

+  Status   = Ip6ConfigNvGetData (

+               Ip6Config,

+               Ip6ConfigDataTypeDnsServer,

+               &DataSize,

+               (VOID **) &Data

+               );

+

+  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {

+    goto Exit;

+  }

+

+  if (DataSize > 0) {

+    //

+    // Convert the DNS server address to string and draw it to UI.

+    //

+    Status = Ip6ConvertAddressListToString (

+               IfrNvData->DnsAddress,

+               HiiHandle,

+               Ip6ConfigNvDnsAddress,

+               Data,

+               DataSize / sizeof (EFI_IPv6_ADDRESS)

+               );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    FreePool (Data);

+    Data = NULL;

+  }

+

+  //

+  // Get gateway adderss list.

+  //

+  DataSize = 0;

+  Status   = Ip6ConfigNvGetData (

+               Ip6Config,

+               Ip6ConfigDataTypeGateway,

+               &DataSize,

+               (VOID **) &Data

+               );

+

+  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {

+    goto Exit;

+  }

+

+  if (DataSize > 0) {

+    //

+    // Convert the gateway address to string and draw it to UI.

+    //

+    Status = Ip6ConvertAddressListToString (

+               IfrNvData->GatewayAddress,

+               HiiHandle,

+               Ip6ConfigNvGatewayAddress,

+               Data,

+               DataSize / sizeof (EFI_IPv6_ADDRESS)

+               );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+  }

+

+  Status = EFI_SUCCESS;

+

+Exit:

+  if (Data != NULL) {

+     FreePool (Data);

+  }

+

+  return Status;

+}

+

+/**

+  Convert IFR data into IP6 configuration data. The policy, alternative interface

+  ID, and DAD transmit counts, and will be saved. If under manual policy, the configured

+  manual address, gateway address, and DNS server address will be saved.

+

+  @param[in]       IfrNvData       The IFR NV data.

+  @param[in, out]  Instance        The IP6 config instance data.

+

+  @retval EFI_SUCCESS              The operation finished successfully.

+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.

+  @retval Others                   Other errors as indicated.

+

+**/

+EFI_STATUS

+Ip6ConvertIfrNvDataToConfigNvData (

+  IN     IP6_CONFIG_IFR_NVDATA       *IfrNvData,

+  IN OUT IP6_CONFIG_INSTANCE         *Instance

+  )

+{

+  IP6_CONFIG_NVDATA                  *Ip6NvData;

+  EFI_IP6_CONFIG_PROTOCOL            *Ip6Config;

+  EFI_STATUS                         Status;

+  EFI_IP6_CONFIG_MANUAL_ADDRESS      *ManualAddress;

+  EFI_IPv6_ADDRESS                   *Address;

+  BOOLEAN                            IsAddressOk;

+  EFI_EVENT                          SetAddressEvent;

+  EFI_EVENT                          TimeoutEvent;

+  UINTN                              DataSize;

+

+  if ((IfrNvData == NULL) || (Instance == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);

+  Ip6NvData = &Instance->Ip6NvData;

+  Ip6Config = &Instance->Ip6Config;

+

+  //

+  // Update those fields which don't have INTERACTIVE attribute.

+  //

+  if (IfrNvData->Policy == IP6_POLICY_AUTO) {

+    Ip6NvData->Policy = Ip6ConfigPolicyAutomatic;

+  } else if (IfrNvData->Policy == IP6_POLICY_MANUAL) {

+    Ip6NvData->Policy = Ip6ConfigPolicyManual;

+  }

+

+  Ip6NvData->DadTransmitCount.DupAddrDetectTransmits = IfrNvData->DadTransmitCount;

+

+  //

+  // Set the configured policy.

+  //

+  Status = Ip6Config->SetData (

+                        Ip6Config,

+                        Ip6ConfigDataTypePolicy,

+                        sizeof (EFI_IP6_CONFIG_POLICY),

+                        &Ip6NvData->Policy

+                        );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Set the duplicate address detection transmits count.

+  //

+  Status = Ip6Config->SetData (

+                        Ip6Config,

+                        Ip6ConfigDataTypeDupAddrDetectTransmits,

+                        sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS),

+                        &Ip6NvData->DadTransmitCount

+                        );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Set the alternative interface ID

+  //

+  Status = Ip6Config->SetData (

+                        Ip6Config,

+                        Ip6ConfigDataTypeAltInterfaceId,

+                        sizeof (EFI_IP6_CONFIG_INTERFACE_ID),

+                        &Ip6NvData->InterfaceId

+                        );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+

+  if (Ip6NvData->Policy == Ip6ConfigPolicyAutomatic) {

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Create events & timers for asynchronous settings.

+  //

+  SetAddressEvent = NULL;

+  TimeoutEvent    = NULL;

+  ManualAddress   = NULL;

+  Address         = NULL;

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  Ip6ConfigManualAddressNotify,

+                  &IsAddressOk,

+                  &SetAddressEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  Status = gBS->CreateEvent (

+                  EVT_TIMER,

+                  TPL_CALLBACK,

+                  NULL,

+                  NULL,

+                  &TimeoutEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  //

+  // Set the manual address list. This is an asynchronous process.

+  //

+  if (!IsListEmpty (&Ip6NvData->ManualAddress) && (Ip6NvData->ManualAddressCount != 0)) {

+    Status = Ip6BuildNvAddressInfo (

+               Instance,

+               Ip6ConfigNvHostAddress,

+               (VOID **) &ManualAddress,

+               &DataSize

+               );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    IsAddressOk = FALSE;

+

+    Status = Ip6Config->RegisterDataNotify (

+                          Ip6Config,

+                          Ip6ConfigDataTypeManualAddress,

+                          SetAddressEvent

+                          );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    Status = Ip6Config->SetData (

+                          Ip6Config,

+                          Ip6ConfigDataTypeManualAddress,

+                          DataSize,

+                          (VOID *) ManualAddress

+                          );

+    if (Status == EFI_NOT_READY) {

+      gBS->SetTimer (TimeoutEvent, TimerRelative, 50000000);

+      while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {

+        if (IsAddressOk) {

+          Status = EFI_SUCCESS;

+        }

+        break;

+      }

+    }

+

+    Status = Ip6Config->UnregisterDataNotify (

+                          Ip6Config,

+                          Ip6ConfigDataTypeManualAddress,

+                          SetAddressEvent

+                          );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+  }

+

+  //

+  // Set gateway address list.

+  //

+  if (!IsListEmpty (&Ip6NvData->GatewayAddress) && (Ip6NvData->GatewayAddressCount != 0)) {

+    Status = Ip6BuildNvAddressInfo (

+               Instance,

+               Ip6ConfigNvGatewayAddress,

+               (VOID **) &Address,

+               &DataSize

+               );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    Status = Ip6Config->SetData (

+                          Ip6Config,

+                          Ip6ConfigDataTypeGateway,

+                          DataSize,

+                          (VOID *) Address

+                          );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    FreePool (Address);

+    Address = NULL;

+  }

+

+  //

+  // Set DNS server address list.

+  //

+  if (!IsListEmpty (&Ip6NvData->DnsAddress) && (Ip6NvData->DnsAddressCount != 0)) {

+    Status = Ip6BuildNvAddressInfo (

+               Instance,

+               Ip6ConfigNvDnsAddress,

+               (VOID **) &Address,

+               &DataSize

+               );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    Status = Ip6Config->SetData (

+                          Ip6Config,

+                          Ip6ConfigDataTypeDnsServer,

+                          DataSize,

+                          (VOID *) Address

+                          );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+  }

+

+  Status = EFI_SUCCESS;

+

+Exit:

+  if (SetAddressEvent != NULL) {

+    gBS->CloseEvent (SetAddressEvent);

+  }

+

+  if (TimeoutEvent != NULL) {

+    gBS->CloseEvent (TimeoutEvent);

+  }

+

+  if (ManualAddress != NULL) {

+    FreePool (ManualAddress);

+  }

+

+  if (Address != NULL) {

+    FreePool (Address);

+  }

+

+  return Status;

+}

+

+/**

+  This function allows the caller to request the current

+  configuration for one or more named elements. The resulting

+  string is in <ConfigAltResp> format. Any and all alternative

+  configuration strings shall also be appended to the end of the

+  current configuration string. If they are, they must appear

+  after the current configuration. They must contain the same

+  routing (GUID, NAME, PATH) as the current configuration string.

+  They must have an additional description indicating the type of

+  alternative configuration the string represents,

+  "ALTCFG=<StringToken>". That <StringToken> (when

+  converted from Hex UNICODE to binary) is a reference to a

+  string in the associated string pack.

+

+  @param[in] This       Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.

+  @param[in] Request    A null-terminated Unicode string in

+                        <ConfigRequest> format. Note that this

+                        includes the routing information as well as

+                        the configurable name / value pairs. It is

+                        invalid for this string to be in

+                        <MultiConfigRequest> format.

+  @param[out] Progress  On return, points to a character in the

+                        Request string. Points to the string's null

+                        terminator if request was successful. Points

+                        to the most recent "&" before the first

+                        failing name / value pair (or the beginning

+                        of the string if the failure is in the first

+                        name / value pair) if the request was not

+                        successful.

+  @param[out] Results   A null-terminated Unicode string in

+                        <ConfigAltResp> format which has all values

+                        filled in for the names in the Request string.

+                        String to be allocated by the called function.

+

+  @retval EFI_SUCCESS             The Results string is filled with the

+                                  values corresponding to all requested

+                                  names.

+  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the

+                                  parts of the results that must be

+                                  stored awaiting possible future

+                                  protocols.

+  @retval EFI_INVALID_PARAMETER   For example, passing in a NULL

+                                  for the Request parameter

+                                  would result in this type of

+                                  error. In this case, the

+                                  Progress parameter would be

+                                  set to NULL.

+  @retval EFI_NOT_FOUND           Routing data doesn't match any

+                                  known driver. Progress set to the

+                                  first character in the routing header.

+                                  Note: There is no requirement that the

+                                  driver validate the routing data. It

+                                  must skip the <ConfigHdr> in order to

+                                  process the names.

+  @retval EFI_INVALID_PARAMETER   Illegal syntax. Progress set

+                                  to most recent & before the

+                                  error or the beginning of the

+                                  string.

+  @retval EFI_INVALID_PARAMETER   Unknown name. Progress points

+                                  to the & before the name in

+                                  question. Currently not implemented.

+**/

+EFI_STATUS

+EFIAPI

+Ip6FormExtractConfig (

+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,

+  IN  CONST EFI_STRING                       Request,

+  OUT EFI_STRING                             *Progress,

+  OUT EFI_STRING                             *Results

+  )

+{

+

+  EFI_STATUS                                 Status;

+  IP6_FORM_CALLBACK_INFO                     *Private;

+  IP6_CONFIG_INSTANCE                        *Ip6ConfigInstance;

+  IP6_CONFIG_IFR_NVDATA                      *IfrNvData;

+  EFI_STRING                                 ConfigRequestHdr;

+  EFI_STRING                                 ConfigRequest;

+  BOOLEAN                                    AllocatedRequest;

+  UINTN                                      Size;

+  UINTN                                      BufferSize;

+

+  if (This == NULL || Progress == NULL || Results == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  *Progress = Request;

+  if ((Request != NULL) &&

+      !HiiIsConfigHdrMatch (Request, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) {

+    return EFI_NOT_FOUND;

+  }

+

+  ConfigRequestHdr = NULL;

+  ConfigRequest    = NULL;

+  AllocatedRequest = FALSE;

+  Size             = 0;

+

+  Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);

+  Ip6ConfigInstance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private);

+  BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);

+

+  IfrNvData = (IP6_CONFIG_IFR_NVDATA *) AllocateZeroPool (BufferSize);

+  if (IfrNvData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = Ip6ConvertConfigNvDataToIfrNvData (IfrNvData, Ip6ConfigInstance);

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  ConfigRequest = Request;

+  if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {

+    //

+    // Request has no request element, construct full request string.

+    // Allocate and fill a buffer large enough to hold the <ConfigHdr> template

+    // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator.

+    //

+    ConfigRequestHdr = HiiConstructConfigHdr (

+                         &mIp6ConfigNvDataGuid,

+                         mIp6ConfigStorageName,

+                         Private->ChildHandle

+                         );

+    Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);

+    ConfigRequest = AllocateZeroPool (Size);

+    ASSERT (ConfigRequest != NULL);

+    AllocatedRequest = TRUE;

+    UnicodeSPrint (

+      ConfigRequest,

+      Size,

+      L"%s&OFFSET=0&WIDTH=%016LX",

+      ConfigRequestHdr,

+      (UINT64) BufferSize

+      );

+    FreePool (ConfigRequestHdr);

+  }

+

+  //

+  // Convert buffer data to <ConfigResp> by helper function BlockToConfig()

+  //

+  Status = gHiiConfigRouting->BlockToConfig (

+                                gHiiConfigRouting,

+                                ConfigRequest,

+                                (UINT8 *) IfrNvData,

+                                BufferSize,

+                                Results,

+                                Progress

+                                );

+

+Exit:

+  FreePool (IfrNvData);

+  //

+  // Free the allocated config request string.

+  //

+  if (AllocatedRequest) {

+    FreePool (ConfigRequest);

+    ConfigRequest = NULL;

+  }

+  //

+  // Set Progress string to the original request string.

+  //

+  if (Request == NULL) {

+    *Progress = NULL;

+  } else if (StrStr (Request, L"OFFSET") == NULL) {

+    *Progress = Request + StrLen (Request);

+  }

+

+  return Status;

+}

+

+/**

+  This function applies changes in a driver's configuration.

+  Input is a Configuration, which has the routing data for this

+  driver followed by name / value configuration pairs. The driver

+  must apply those pairs to its configurable storage. If the

+  driver's configuration is stored in a linear block of data

+  and the driver's name / value pairs are in <BlockConfig>

+  format, it may use the ConfigToBlock helper function (above) to

+  simplify the job. Currently not implemented.

+

+  @param[in]  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.

+  @param[in]  Configuration  A null-terminated Unicode string in

+                             <ConfigString> format.

+  @param[out] Progress       A pointer to a string filled in with the

+                             offset of the most recent '&' before the

+                             first failing name / value pair (or the

+                             beginn ing of the string if the failure

+                             is in the first name / value pair) or

+                             the terminating NULL if all was

+                             successful.

+

+  @retval EFI_SUCCESS             The results have been distributed or are

+                                  awaiting distribution.

+  @retval EFI_OUT_OF_MEMORY       Not enough memory to store the

+                                  parts of the results that must be

+                                  stored awaiting possible future

+                                  protocols.

+  @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the

+                                  Results parameter would result

+                                  in this type of error.

+  @retval EFI_NOT_FOUND           Target for the specified routing data

+                                  was not found.

+**/

+EFI_STATUS

+EFIAPI

+Ip6FormRouteConfig (

+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,

+  IN  CONST EFI_STRING                       Configuration,

+  OUT EFI_STRING                             *Progress

+  )

+{

+  if (This == NULL || Configuration == NULL || Progress == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Check routing data in <ConfigHdr>.

+  // Note: if only one Storage is used, then this checking could be skipped.

+  //

+  if (!HiiIsConfigHdrMatch (Configuration, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) {

+    *Progress = Configuration;

+    return EFI_NOT_FOUND;

+  }

+

+  *Progress = Configuration + StrLen (Configuration);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is called to provide results data to the driver.

+  This data consists of a unique key that is used to identify

+  which data is either being passed back or being asked for.

+

+  @param[in]  This               Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.

+  @param[in]  Action             Specifies the type of action taken by the browser.

+  @param[in]  QuestionId         A unique value which is sent to the original

+                                 exporting driver so that it can identify the type

+                                 of data to expect. The format of the data tends to

+                                 vary based on the opcode that generated the callback.

+  @param[in]  Type               The type of value for the question.

+  @param[in]  Value              A pointer to the data being sent to the original

+                                 exporting driver.

+  @param[out]  ActionRequest     On return, points to the action requested by the

+                                 callback function.

+

+  @retval EFI_SUCCESS            The callback successfully handled the action.

+  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the

+                                 variable and its data.

+  @retval EFI_DEVICE_ERROR       The variable could not be saved.

+  @retval EFI_UNSUPPORTED        The specified Action is not supported by the

+                                 callback. Currently not implemented.

+  @retval EFI_INVALID_PARAMETER  Passed in the wrong parameter.

+  @retval Others                 Other errors as indicated.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6FormCallback (

+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,

+  IN  EFI_BROWSER_ACTION                     Action,

+  IN  EFI_QUESTION_ID                        QuestionId,

+  IN  UINT8                                  Type,

+  IN  EFI_IFR_TYPE_VALUE                     *Value,

+  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest

+  )

+{

+  IP6_FORM_CALLBACK_INFO        *Private;

+  UINTN                         BufferSize;

+  IP6_CONFIG_IFR_NVDATA         *IfrNvData;

+  IP6_CONFIG_IFR_NVDATA         OldIfrNvData;

+  EFI_STATUS                    Status;

+  EFI_INPUT_KEY                 Key;

+  IP6_CONFIG_INSTANCE           *Instance;

+  IP6_CONFIG_NVDATA             *Ip6NvData;

+  EFI_IP6_CONFIG_PROTOCOL       *Ip6Config;

+  EFI_IP6_CONFIG_INTERFACE_INFO *Data;

+  UINTN                         DataSize;

+  CHAR16                        PortString[ADDRESS_STR_MAX_SIZE];

+  EFI_HII_HANDLE                HiiHandle;

+  EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private   = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);

+  Instance  = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private);

+  Ip6NvData = &Instance->Ip6NvData;

+

+  if (Action == EFI_BROWSER_ACTION_FORM_OPEN) {

+    //

+    // Update main Form when main Form is opened.

+    // This will be done only in FORM_OPEN CallBack of question with KEY_INTERFACE_ID from main Form.

+    //

+    if (QuestionId != KEY_INTERFACE_ID) {

+      return EFI_SUCCESS;

+    }

+

+    Ip6Config = &Instance->Ip6Config;

+    HiiHandle = Instance->CallbackInfo.RegisteredHandle;

+

+    //

+    // Get the current interface info.

+    //

+    Status = Ip6ConfigNvGetData (

+               Ip6Config,

+               Ip6ConfigDataTypeInterfaceInfo,

+               &DataSize,

+               (VOID **) &Data

+               );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    //

+    // Generate the dynamic text opcode for host address and draw it.

+    //

+    IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data;

+    Status = Ip6ConvertAddressListToString (

+               PortString,

+               HiiHandle,

+               Ip6ConfigNvHostAddress,

+               IfInfo->AddressInfo,

+               IfInfo->AddressInfoCount

+               );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    //

+    // Generate the dynamic text opcode for route table and draw it.

+    //

+    Status = Ip6ConvertAddressListToString (

+               PortString,

+               HiiHandle,

+               Ip6ConfigNvRouteTable,

+               IfInfo->RouteTable,

+               IfInfo->RouteCount

+               );

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    //

+    // Get DNS server list.

+    //

+    DataSize = 0;

+    Status = Ip6ConfigNvGetData (

+               Ip6Config,

+               Ip6ConfigDataTypeDnsServer,

+               &DataSize,

+               (VOID **) &Data

+               );

+    if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {

+      goto Exit;

+    }

+

+    if (DataSize > 0) {

+      //

+      // Generate the dynamic text opcode for DNS server and draw it.

+      //

+      Status = Ip6ConvertAddressListToString (

+                 PortString,

+                 HiiHandle,

+                 Ip6ConfigNvDnsAddress,

+                 Data,

+                 DataSize / sizeof (EFI_IPv6_ADDRESS)

+                 );

+      if (EFI_ERROR (Status)) {

+        goto Exit;

+      }

+    }

+

+    //

+    // Get gateway adderss list.

+    //

+    DataSize = 0;

+    Status = Ip6ConfigNvGetData (

+               Ip6Config,

+               Ip6ConfigDataTypeGateway,

+               &DataSize,

+               (VOID **) &Data

+               );

+    if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {

+      goto Exit;

+    }

+

+    if (DataSize > 0) {

+      //

+      // Generate the dynamic text opcode for gateway and draw it.

+      //

+      Status = Ip6ConvertAddressListToString (

+                 PortString,

+                 HiiHandle,

+                 Ip6ConfigNvGatewayAddress,

+                 Data,

+                 DataSize / sizeof (EFI_IPv6_ADDRESS)

+                 );

+      if (EFI_ERROR (Status)) {

+        goto Exit;

+      }

+    }

+

+Exit:

+    FreePool (Data);

+    return Status;

+  }

+

+  if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) {

+    //

+    // Do nothing for UEFI FORM_CLOSE action

+    //

+    return EFI_SUCCESS;

+  }

+

+  if ((Value == NULL) || (ActionRequest == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Retrieve uncommitted data from Browser

+  //

+

+  BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);

+  IfrNvData = AllocateZeroPool (BufferSize);

+  if (IfrNvData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = EFI_SUCCESS;

+

+  ZeroMem (&OldIfrNvData, BufferSize);

+

+  HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData);

+

+  CopyMem (&OldIfrNvData, IfrNvData, BufferSize);

+

+  switch (QuestionId) {

+  case KEY_INTERFACE_ID:

+    Status = Ip6ParseInterfaceIdFromString (IfrNvData->InterfaceId, &Ip6NvData->InterfaceId);

+    if (EFI_ERROR (Status)) {

+      CreatePopUp (

+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,

+        &Key,

+        L"Invalid Interface ID!",

+        NULL

+        );

+    }

+

+    break;

+

+  case KEY_MANUAL_ADDRESS:

+    Status = Ip6ParseAddressListFromString (

+               IfrNvData->ManualAddress,

+               &Ip6NvData->ManualAddress,

+               &Ip6NvData->ManualAddressCount

+               );

+    if (EFI_ERROR (Status)) {

+      CreatePopUp (

+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,

+        &Key,

+        L"Invalid Host Addresses!",

+        NULL

+        );

+    }

+

+    break;

+

+  case KEY_GATEWAY_ADDRESS:

+    Status = Ip6ParseAddressListFromString (

+               IfrNvData->GatewayAddress,

+               &Ip6NvData->GatewayAddress,

+               &Ip6NvData->GatewayAddressCount

+               );

+    if (EFI_ERROR (Status)) {

+      CreatePopUp (

+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,

+        &Key,

+        L"Invalid Gateway Addresses!",

+        NULL

+        );

+    }

+

+    break;

+

+  case KEY_DNS_ADDRESS:

+    Status = Ip6ParseAddressListFromString (

+               IfrNvData->DnsAddress,

+               &Ip6NvData->DnsAddress,

+               &Ip6NvData->DnsAddressCount

+               );

+    if (EFI_ERROR (Status)) {

+      CreatePopUp (

+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,

+        &Key,

+        L"Invalid DNS Addresses!",

+        NULL

+        );

+    }

+

+    break;

+

+  case KEY_SAVE_CONFIG_CHANGES:

+    CopyMem (&OldIfrNvData, IfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));

+    *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;

+    break;

+

+  case KEY_IGNORE_CONFIG_CHANGES:

+    CopyMem (IfrNvData, &OldIfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));

+    *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;

+    break;

+

+  case KEY_SAVE_CHANGES:

+    Status = Ip6ConvertIfrNvDataToConfigNvData (IfrNvData, Instance);

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+

+    *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;

+    break;

+

+  default:

+    break;

+  }

+

+  if (!EFI_ERROR (Status)) {

+    //

+    // Pass changed uncommitted data back to Form Browser.

+    //

+    BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);

+    HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL);

+  }

+

+  FreePool (IfrNvData);

+  return Status;

+}

+

+/**

+  Install HII Config Access protocol for network device and allocate resources.

+

+  @param[in, out]  Instance      The IP6_CONFIG_INSTANCE to create a form.

+

+  @retval EFI_SUCCESS            The HII Config Access protocol is installed.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.

+  @retval Others                 Other errors as indicated.

+

+**/

+EFI_STATUS

+Ip6ConfigFormInit (

+  IN OUT IP6_CONFIG_INSTANCE     *Instance

+  )

+{

+  EFI_STATUS                     Status;

+  IP6_SERVICE                    *IpSb;

+  IP6_FORM_CALLBACK_INFO         *CallbackInfo;

+  EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;

+  VENDOR_DEVICE_PATH             VendorDeviceNode;

+  EFI_SERVICE_BINDING_PROTOCOL   *MnpSb;

+  CHAR16                         *MacString;

+  CHAR16                         MenuString[128];

+  CHAR16                         PortString[128];

+  CHAR16                         *OldMenuString;

+  EFI_DEVICE_PATH_PROTOCOL       *ParentDevicePath;

+

+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+  ASSERT (IpSb != NULL);

+

+  Status = gBS->HandleProtocol (

+                  IpSb->Controller,

+                  &gEfiDevicePathProtocolGuid,

+                  (VOID **) &ParentDevicePath

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  CallbackInfo = &Instance->CallbackInfo;

+  CallbackInfo->Signature = IP6_FORM_CALLBACK_INFO_SIGNATURE;

+

+  //

+  // Construct device path node for EFI HII Config Access protocol,

+  // which consists of controller physical device path and one hardware

+  // vendor guid node.

+  //

+  ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH));

+  VendorDeviceNode.Header.Type    = HARDWARE_DEVICE_PATH;

+  VendorDeviceNode.Header.SubType = HW_VENDOR_DP;

+

+  CopyGuid (&VendorDeviceNode.Guid, &mIp6HiiVendorDevicePathGuid);

+

+  SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH));

+  CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode (

+                                        ParentDevicePath,

+                                        (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode

+                                        );

+  if (CallbackInfo->HiiVendorDevicePath == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto Error;

+  }

+

+  ConfigAccess                = &CallbackInfo->HiiConfigAccess;

+  ConfigAccess->ExtractConfig = Ip6FormExtractConfig;

+  ConfigAccess->RouteConfig   = Ip6FormRouteConfig;

+  ConfigAccess->Callback      = Ip6FormCallback;

+

+  //

+  // Install Device Path Protocol and Config Access protocol on new handle

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &CallbackInfo->ChildHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  CallbackInfo->HiiVendorDevicePath,

+                  &gEfiHiiConfigAccessProtocolGuid,

+                  ConfigAccess,

+                  NULL

+                  );

+  if (!EFI_ERROR (Status)) {

+    //

+    // Open the Parent Handle for the child

+    //

+    Status = gBS->OpenProtocol (

+                    IpSb->Controller,

+                    &gEfiManagedNetworkServiceBindingProtocolGuid,

+                    (VOID **) &MnpSb,

+                    IpSb->Image,

+                    CallbackInfo->ChildHandle,

+                    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                    );

+  }

+

+  if (EFI_ERROR (Status)) {

+    goto Error;

+  }

+

+  //

+  // Publish our HII data

+  //

+  CallbackInfo->RegisteredHandle = HiiAddPackages (

+                                     &mIp6ConfigNvDataGuid,

+                                     CallbackInfo->ChildHandle,

+                                     Ip6DxeStrings,

+                                     Ip6ConfigBin,

+                                     NULL

+                                     );

+  if (CallbackInfo->RegisteredHandle == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto Error;

+  }

+

+  //

+  // Append MAC string in the menu string and tile string

+  //

+  Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &MacString);

+  if (!EFI_ERROR (Status)) {

+    OldMenuString = HiiGetString (

+                      CallbackInfo->RegisteredHandle,

+                      STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),

+                      NULL)

+                      ;

+    UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);

+    HiiSetString (

+      CallbackInfo->RegisteredHandle,

+      STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),

+      MenuString,

+      NULL

+      );

+    UnicodeSPrint (PortString, 128, L"MAC:%s", MacString);

+    HiiSetString (

+      CallbackInfo->RegisteredHandle,

+      STRING_TOKEN (STR_IP6_DEVICE_FORM_TITLE),

+      PortString,

+      NULL

+      );

+

+    FreePool (MacString);

+    FreePool (OldMenuString);

+

+    InitializeListHead (&Instance->Ip6NvData.ManualAddress);

+    InitializeListHead (&Instance->Ip6NvData.GatewayAddress);

+    InitializeListHead (&Instance->Ip6NvData.DnsAddress);

+

+    return EFI_SUCCESS;

+  }

+

+Error:

+  Ip6ConfigFormUnload (Instance);

+  return Status;

+}

+

+/**

+  Uninstall the HII Config Access protocol for network devices and free up the resources.

+

+  @param[in, out]  Instance      The IP6_CONFIG_INSTANCE to unload a form.

+

+**/

+VOID

+Ip6ConfigFormUnload (

+  IN OUT IP6_CONFIG_INSTANCE     *Instance

+  )

+{

+  IP6_SERVICE                    *IpSb;

+  IP6_FORM_CALLBACK_INFO         *CallbackInfo;

+  IP6_CONFIG_NVDATA              *Ip6NvData;

+

+  IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);

+  ASSERT (IpSb != NULL);

+

+  CallbackInfo = &Instance->CallbackInfo;

+

+  if (CallbackInfo->ChildHandle != NULL) {

+

+    //

+    // Close the child handle

+    //

+    gBS->CloseProtocol (

+           IpSb->Controller,

+           &gEfiManagedNetworkServiceBindingProtocolGuid,

+           IpSb->Image,

+           CallbackInfo->ChildHandle

+           );

+    //

+    // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL

+    //

+    gBS->UninstallMultipleProtocolInterfaces (

+           CallbackInfo->ChildHandle,

+           &gEfiDevicePathProtocolGuid,

+           CallbackInfo->HiiVendorDevicePath,

+           &gEfiHiiConfigAccessProtocolGuid,

+           &CallbackInfo->HiiConfigAccess,

+           NULL

+           );

+  }

+

+  if (CallbackInfo->HiiVendorDevicePath != NULL) {

+    FreePool (CallbackInfo->HiiVendorDevicePath);

+  }

+

+  if (CallbackInfo->RegisteredHandle != NULL) {

+    //

+    // Remove HII package list

+    //

+    HiiRemovePackages (CallbackInfo->RegisteredHandle);

+  }

+

+  Ip6NvData = &Instance->Ip6NvData;

+

+  Ip6FreeAddressInfoList (&Ip6NvData->ManualAddress);

+  Ip6FreeAddressInfoList (&Ip6NvData->GatewayAddress);

+  Ip6FreeAddressInfoList (&Ip6NvData->DnsAddress);

+

+  Ip6NvData->ManualAddressCount  = 0;

+  Ip6NvData->GatewayAddressCount = 0;

+  Ip6NvData->DnsAddressCount     = 0;

+}

diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h
new file mode 100644
index 0000000..d184776
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h
@@ -0,0 +1,73 @@
+/** @file

+  The header file of Ip6ConfigNv.c.

+

+  Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _IP6_CONFIGNV_H_

+#define _IP6_CONFIGNV_H_

+

+#include "Ip6NvData.h"

+#include "Ip6ConfigImpl.h"

+

+extern UINT8  Ip6ConfigBin[];

+extern UINT8  Ip6DxeStrings[];

+

+#define IP6_HII_VENDOR_DEVICE_PATH_GUID \

+  { \

+    0x13288098, 0xb11f, 0x45b9, { 0xbc, 0x4f, 0x91, 0xb5, 0x4b, 0xa3, 0x39, 0xb9 } \

+  }

+

+#define IP6_ETHERNET              L"Ethernet"

+#define IP6_EXPERIMENTAL_ETHERNET L"Experimental Ethernet"

+#define IP6_ADDRESS_DELIMITER     L' '

+#define IP6_LINK_LOCAL_PREFIX     L"FE80::"

+

+typedef enum {

+  Ip6InterfaceTypeEthernet = 1,

+  Ip6InterfaceTypeExperimentalEthernet

+} IP6_INTERFACE_TYPE;

+

+typedef enum {

+  Ip6ConfigNvHostAddress,

+  Ip6ConfigNvGatewayAddress,

+  Ip6ConfigNvDnsAddress,

+  Ip6ConfigNvRouteTable

+} IP6_CONFIG_NV_ADDRESS_TYPE;

+

+/**

+  Install HII Config Access protocol for network device and allocate resources.

+

+  @param[in, out]  Instance      The IP6_CONFIG_INSTANCE to create a form.

+

+  @retval EFI_SUCCESS            The HII Config Access protocol is installed.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.

+  @retval Others                 Other errors as indicated.

+

+**/

+EFI_STATUS

+Ip6ConfigFormInit (

+  IN OUT IP6_CONFIG_INSTANCE     *Instance

+  );

+

+/**

+  Uninstall HII Config Access protocol for network device and free resource.

+

+  @param[in, out]  Instance      The IP6_CONFIG_INSTANCE to unload a form.

+

+**/

+VOID

+Ip6ConfigFormUnload (

+  IN OUT IP6_CONFIG_INSTANCE     *Instance

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.c b/NetworkPkg/Ip6Dxe/Ip6Driver.c
new file mode 100644
index 0000000..388dade
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Driver.c
@@ -0,0 +1,930 @@
+/** @file

+  The driver binding and service binding protocol for IP6 driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding = {

+  Ip6DriverBindingSupported,

+  Ip6DriverBindingStart,

+  Ip6DriverBindingStop,

+  0xa,

+  NULL,

+  NULL

+};

+

+/**

+  This is the declaration of an EFI image entry point. This entry point is

+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including

+  both device drivers and bus drivers.

+

+  The entry point for IP6 driver which installs the driver

+  binding and component name protocol on its image.

+

+  @param[in]  ImageHandle           The firmware allocated handle for the UEFI image.

+  @param[in]  SystemTable           A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The operation completed successfully.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6DriverEntryPoint (

+  IN EFI_HANDLE             ImageHandle,

+  IN EFI_SYSTEM_TABLE       *SystemTable

+  )

+{

+  return EfiLibInstallDriverBindingComponentName2 (

+           ImageHandle,

+           SystemTable,

+           &gIp6DriverBinding,

+           ImageHandle,

+           &gIp6ComponentName,

+           &gIp6ComponentName2

+           );

+}

+

+/**

+  Test to see if this driver supports ControllerHandle.

+

+  @param[in]  This                   Protocol instance pointer.

+  @param[in]  ControllerHandle       Handle of device to test.

+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child

+                                     device to start.

+

+  @retval EFI_SUCCESS                This driver supports this device.

+  @retval EFI_ALREADY_STARTED        This driver is already running on this device.

+  @retval other                      This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6DriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  )

+{

+  //

+  // Test for the MNP service binding Protocol

+  //

+  return gBS->OpenProtocol (

+                ControllerHandle,

+                &gEfiManagedNetworkServiceBindingProtocolGuid,

+                NULL,

+                This->DriverBindingHandle,

+                ControllerHandle,

+                EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                );

+}

+

+/**

+  Clean up an IP6 service binding instance. It releases all

+  the resource allocated by the instance. The instance may be

+  partly initialized, or partly destroyed. If a resource is

+  destroyed, it is marked as that in case the destory failed and

+  being called again later.

+

+  @param[in]  IpSb               The IP6 service binding instance to clean up.

+

+  @retval EFI_SUCCESS            The resource used by the instance are cleaned up.

+  @retval Others                 Failed to clean up some of the resources.

+

+**/

+EFI_STATUS

+Ip6CleanService (

+  IN IP6_SERVICE            *IpSb

+  )

+{

+  EFI_STATUS                Status;

+  EFI_IPv6_ADDRESS          AllNodes;

+  IP6_NEIGHBOR_ENTRY        *NeighborCache;

+

+  Ip6ConfigCleanInstance (&IpSb->Ip6ConfigInstance);

+

+  //

+  // Leave link-scope all-nodes multicast address (FF02::1)

+  //

+  Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);

+

+  Status = Ip6LeaveGroup (IpSb, &AllNodes);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (IpSb->DefaultInterface != NULL) {

+    Ip6CleanInterface (IpSb->DefaultInterface, NULL);

+    IpSb->DefaultInterface = NULL;

+  }

+

+  Ip6CleanDefaultRouterList (IpSb);

+

+  Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix);

+  Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix);

+

+  if (IpSb->RouteTable != NULL) {

+    Ip6CleanRouteTable (IpSb->RouteTable);

+    IpSb->RouteTable = NULL;

+  }

+

+  if (IpSb->InterfaceId != NULL) {

+    FreePool (IpSb->InterfaceId);

+  }

+

+  IpSb->InterfaceId = NULL;

+

+  Ip6CleanAssembleTable (&IpSb->Assemble);

+

+  if (IpSb->MnpChildHandle != NULL) {

+    if (IpSb->Mnp != NULL) {

+      IpSb->Mnp->Cancel (IpSb->Mnp, NULL);

+      IpSb->Mnp->Configure (IpSb->Mnp, NULL);

+      gBS->CloseProtocol (

+            IpSb->MnpChildHandle,

+            &gEfiManagedNetworkProtocolGuid,

+            IpSb->Image,

+            IpSb->Controller

+            );

+

+      IpSb->Mnp = NULL;

+    }

+

+    NetLibDestroyServiceChild (

+      IpSb->Controller,

+      IpSb->Image,

+      &gEfiManagedNetworkServiceBindingProtocolGuid,

+      IpSb->MnpChildHandle

+      );

+

+    IpSb->MnpChildHandle = NULL;

+  }

+

+  if (IpSb->RecvRequest.MnpToken.Event != NULL) {

+    gBS->CloseEvent (IpSb->RecvRequest.MnpToken.Event);

+  }

+

+  if (IpSb->Timer != NULL) {

+    gBS->SetTimer (IpSb->Timer, TimerCancel, 0);

+    gBS->CloseEvent (IpSb->Timer);

+

+    IpSb->Timer = NULL;

+  }

+

+  if (IpSb->FasterTimer != NULL) {

+    gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);

+    gBS->CloseEvent (IpSb->FasterTimer);

+

+    IpSb->FasterTimer = NULL;

+  }

+  //

+  // Free the Neighbor Discovery resources

+  //

+  while (!IsListEmpty (&IpSb->NeighborTable)) {

+    NeighborCache = NET_LIST_HEAD (&IpSb->NeighborTable, IP6_NEIGHBOR_ENTRY, Link);

+    Ip6FreeNeighborEntry (IpSb, NeighborCache, FALSE, TRUE, EFI_SUCCESS, NULL, NULL);

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Create a new IP6 driver service binding protocol.

+

+  @param[in]  Controller         The controller that has MNP service binding

+                                 installed.

+  @param[in]  ImageHandle        The IP6 driver's image handle.

+  @param[out]  Service           The variable to receive the newly created IP6

+                                 service.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.

+  @retval EFI_SUCCESS            A new IP6 service binding private is created.

+

+**/

+EFI_STATUS

+Ip6CreateService (

+  IN  EFI_HANDLE            Controller,

+  IN  EFI_HANDLE            ImageHandle,

+  OUT IP6_SERVICE           **Service

+  )

+{

+  IP6_SERVICE                           *IpSb;

+  EFI_STATUS                            Status;

+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;

+  EFI_MANAGED_NETWORK_CONFIG_DATA       *Config;

+  IP6_CONFIG_DATA_ITEM                  *DataItem;

+

+  ASSERT (Service != NULL);

+

+  *Service = NULL;

+

+  //

+  // allocate a service private data then initialize all the filed to

+  // empty resources, so if any thing goes wrong when allocating

+  // resources, Ip6CleanService can be called to clean it up.

+  //

+  IpSb = AllocateZeroPool (sizeof (IP6_SERVICE));

+

+  if (IpSb == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  IpSb->Signature                   = IP6_SERVICE_SIGNATURE;

+  IpSb->ServiceBinding.CreateChild  = Ip6ServiceBindingCreateChild;

+  IpSb->ServiceBinding.DestroyChild = Ip6ServiceBindingDestroyChild;

+  IpSb->State                       = IP6_SERVICE_UNSTARTED;

+  IpSb->InDestroy                   = FALSE;

+

+  IpSb->NumChildren                 = 0;

+  InitializeListHead (&IpSb->Children);

+

+  InitializeListHead (&IpSb->Interfaces);

+  IpSb->DefaultInterface            = NULL;

+  IpSb->RouteTable                  = NULL;

+

+  IpSb->RecvRequest.Signature       = IP6_LINK_RX_SIGNATURE;

+  IpSb->RecvRequest.CallBack        = NULL;

+  IpSb->RecvRequest.Context         = NULL;

+  MnpToken                          = &IpSb->RecvRequest.MnpToken;

+  MnpToken->Event                   = NULL;

+  MnpToken->Status                  = EFI_NOT_READY;

+  MnpToken->Packet.RxData           = NULL;

+

+  Ip6CreateAssembleTable (&IpSb->Assemble);

+

+  IpSb->MldCtrl.Mldv1QuerySeen      = 0;

+  InitializeListHead (&IpSb->MldCtrl.Groups);

+

+  ZeroMem (&IpSb->LinkLocalAddr, sizeof (EFI_IPv6_ADDRESS));

+  IpSb->LinkLocalOk                 = FALSE;

+  IpSb->LinkLocalDadFail            = FALSE;

+  IpSb->Dhcp6NeedStart              = FALSE;

+  IpSb->Dhcp6NeedInfoRequest        = FALSE;

+

+  IpSb->CurHopLimit                 = IP6_HOP_LIMIT;

+  IpSb->LinkMTU                     = IP6_MIN_LINK_MTU;

+  IpSb->BaseReachableTime           = IP6_REACHABLE_TIME;

+  Ip6UpdateReachableTime (IpSb);

+  //

+  // RFC4861 RETRANS_TIMER: 1,000 milliseconds

+  //

+  IpSb->RetransTimer                = IP6_RETRANS_TIMER;

+

+  IpSb->RoundRobin                  = 0;

+

+  InitializeListHead (&IpSb->NeighborTable);

+  InitializeListHead (&IpSb->DefaultRouterList);

+  InitializeListHead (&IpSb->OnlinkPrefix);

+  InitializeListHead (&IpSb->AutonomousPrefix);

+

+  IpSb->InterfaceIdLen              = IP6_IF_ID_LEN;

+  IpSb->InterfaceId                 = NULL;

+

+  IpSb->RouterAdvertiseReceived     = FALSE;

+  IpSb->SolicitTimer                = IP6_MAX_RTR_SOLICITATIONS;

+  IpSb->Ticks                       = 0;

+

+  IpSb->Image                       = ImageHandle;

+  IpSb->Controller                  = Controller;

+

+  IpSb->MnpChildHandle              = NULL;

+  IpSb->Mnp                         = NULL;

+

+  Config                            = &IpSb->MnpConfigData;

+  Config->ReceivedQueueTimeoutValue = 0;

+  Config->TransmitQueueTimeoutValue = 0;

+  Config->ProtocolTypeFilter        = IP6_ETHER_PROTO;

+  Config->EnableUnicastReceive      = TRUE;

+  Config->EnableMulticastReceive    = TRUE;

+  Config->EnableBroadcastReceive    = TRUE;

+  Config->EnablePromiscuousReceive  = FALSE;

+  Config->FlushQueuesOnReset        = TRUE;

+  Config->EnableReceiveTimestamps   = FALSE;

+  Config->DisableBackgroundPolling  = FALSE;

+

+  ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));

+

+  IpSb->Timer                       = NULL;

+  IpSb->FasterTimer                 = NULL;

+

+  ZeroMem (&IpSb->Ip6ConfigInstance, sizeof (IP6_CONFIG_INSTANCE));

+

+  IpSb->MacString                   = NULL;

+

+  //

+  // Create various resources. First create the route table, timer

+  // event, MNP token event and MNP child.

+  //

+

+  IpSb->RouteTable = Ip6CreateRouteTable ();

+  if (IpSb->RouteTable == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_ERROR;

+  }

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,

+                  TPL_CALLBACK,

+                  Ip6TimerTicking,

+                  IpSb,

+                  &IpSb->Timer

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,

+                  TPL_CALLBACK,

+                  Ip6NdFasterTimerTicking,

+                  IpSb,

+                  &IpSb->FasterTimer

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = NetLibCreateServiceChild (

+             Controller,

+             ImageHandle,

+             &gEfiManagedNetworkServiceBindingProtocolGuid,

+             &IpSb->MnpChildHandle

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  IpSb->MnpChildHandle,

+                  &gEfiManagedNetworkProtocolGuid,

+                  (VOID **) (&IpSb->Mnp),

+                  ImageHandle,

+                  Controller,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = Ip6ServiceConfigMnp (IpSb, TRUE);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  IpSb->MaxPacketSize = IP6_MIN_LINK_MTU - sizeof (EFI_IP6_HEADER);

+  if (NetLibGetVlanId (IpSb->Controller) != 0) {

+    //

+    // This is a VLAN device, reduce MTU by VLAN tag length

+    //

+    IpSb->MaxPacketSize -= NET_VLAN_TAG_LEN;

+  }

+  IpSb->OldMaxPacketSize = IpSb->MaxPacketSize;

+

+  //

+  // Currently only ETHERNET is supported in IPv6 stack, since

+  // link local address requires an IEEE 802 48-bit MACs for

+  // EUI-64 format interface identifier mapping.

+  //

+  if (IpSb->SnpMode.IfType != NET_IFTYPE_ETHERNET) {

+    Status = EFI_UNSUPPORTED;

+    goto ON_ERROR;

+  }

+

+  Status = Ip6InitMld (IpSb);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // The timer expires every 100 (IP6_TIMER_INTERVAL_IN_MS) milliseconds.

+  //

+  Status = gBS->SetTimer (IpSb->FasterTimer, TimerPeriodic, TICKS_PER_MS * IP6_TIMER_INTERVAL_IN_MS);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // The timer expires every 1000 (IP6_ONE_SECOND_IN_MS) milliseconds.

+  //

+  Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_MS * IP6_ONE_SECOND_IN_MS);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  Ip6OnFrameReceived,

+                  &IpSb->RecvRequest,

+                  &MnpToken->Event

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = Ip6ConfigInitInstance (&IpSb->Ip6ConfigInstance);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  IpSb->DefaultInterface = Ip6CreateInterface (IpSb, TRUE);

+  if (IpSb->DefaultInterface == NULL) {

+    Status = EFI_DEVICE_ERROR;

+    goto ON_ERROR;

+  }

+

+  //

+  // If there is any manual address, set it.

+  //

+  DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress];

+  if (DataItem->Data.Ptr != NULL) {

+    DataItem->SetData (

+                &IpSb->Ip6ConfigInstance,

+                DataItem->DataSize,

+                DataItem->Data.Ptr

+                );

+  }

+

+  InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link);

+

+  *Service = IpSb;

+  return EFI_SUCCESS;

+

+ON_ERROR:

+  Ip6CleanService (IpSb);

+  FreePool (IpSb);

+  return Status;

+}

+

+

+/**

+  Start this driver on ControllerHandle.

+

+  @param[in]  This                Protocol instance pointer.

+  @param[in]  ControllerHandle    Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath Optional parameter used to pick a specific child

+                                  device to start.

+

+  @retval EFI_SUCCES              This driver is added to ControllerHandle.

+  @retval EFI_ALREADY_STARTED     This driver is already running on ControllerHandle.

+  @retval other                   This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6DriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  )

+{

+  IP6_SERVICE               *IpSb;

+  EFI_STATUS                Status;

+

+  //

+  // Test for the Ip6 service binding protocol

+  //

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiIp6ServiceBindingProtocolGuid,

+                  NULL,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+

+  if (Status == EFI_SUCCESS) {

+    return EFI_ALREADY_STARTED;

+  }

+

+  Status = Ip6CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  ASSERT (IpSb != NULL);

+

+  //

+  // Install the Ip6ServiceBinding Protocol onto ControlerHandle

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &ControllerHandle,

+                  &gEfiIp6ServiceBindingProtocolGuid,

+                  &IpSb->ServiceBinding,

+                  &gEfiIp6ConfigProtocolGuid,

+                  &IpSb->Ip6ConfigInstance.Ip6Config,

+                  NULL

+                  );

+

+  if (EFI_ERROR (Status)) {

+

+    Ip6CleanService (IpSb);

+    FreePool (IpSb);

+  } else {

+    //

+    // Initialize the IP6 ID

+    //

+    mIp6Id = NET_RANDOM (NetRandomInitSeed ());

+

+    Ip6SetVariableData (IpSb);

+  }

+

+  return Status;

+}

+

+/**

+  Stop this driver on ControllerHandle.

+

+  @param[in]  This               Protocol instance pointer.

+  @param[in]  ControllerHandle   Handle of device to stop driver on.

+  @param[in]  NumberOfChildren   Number of Handles in ChildHandleBuffer. If number

+                                 of children is zero, stop the entire  bus driver.

+  @param[in]  ChildHandleBuffer  An array of child handles to be freed. May be NULL

+                                 if NumberOfChildren is 0.

+

+  @retval EFI_SUCCESS           The device was stopped.

+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6DriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL

+  )

+{

+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;

+  IP6_SERVICE                   *IpSb;

+  IP6_PROTOCOL                  *IpInstance;

+  EFI_HANDLE                    NicHandle;

+  EFI_STATUS                    Status;

+  BOOLEAN                       IsDhcp6;

+  EFI_TPL                       OldTpl;

+  INTN                          State;

+

+  IsDhcp6   = FALSE;

+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);

+

+  if (NicHandle != NULL) {

+    //

+    // DriverBindingStop is triggered by the uninstallation of the EFI DHCPv6

+    // Protocol used by Ip6Config.

+    //

+    IsDhcp6 = TRUE;

+  } else {

+

+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);

+

+    if (NicHandle == NULL) {

+      return EFI_DEVICE_ERROR;

+    }

+  }

+

+  Status = gBS->OpenProtocol (

+                  NicHandle,

+                  &gEfiIp6ServiceBindingProtocolGuid,

+                  (VOID **) &ServiceBinding,

+                  This->DriverBindingHandle,

+                  NicHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  IpSb = IP6_SERVICE_FROM_PROTOCOL (ServiceBinding);

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (IpSb->InDestroy) {

+    Status = EFI_SUCCESS;

+    goto Exit;

+  }

+

+  if (IsDhcp6) {

+

+    Status = Ip6ConfigDestroyDhcp6 (&IpSb->Ip6ConfigInstance);

+    gBS->CloseEvent (IpSb->Ip6ConfigInstance.Dhcp6Event);

+    IpSb->Ip6ConfigInstance.Dhcp6Event = NULL;

+  } else if (NumberOfChildren == 0) {

+

+    IpSb->InDestroy = TRUE;

+    State           = IpSb->State;

+    IpSb->State     = IP6_SERVICE_DESTROY;

+

+    //

+    // Clear the variable data.

+    //

+    Ip6ClearVariableData (IpSb);

+

+    Status = Ip6CleanService (IpSb);

+    if (EFI_ERROR (Status)) {

+      IpSb->State = State;

+      goto Exit;

+    }

+

+    Status = gBS->UninstallMultipleProtocolInterfaces (

+                    NicHandle,

+                    &gEfiIp6ServiceBindingProtocolGuid,

+                    ServiceBinding,

+                    &gEfiIp6ConfigProtocolGuid,

+                    &IpSb->Ip6ConfigInstance.Ip6Config,

+                    NULL

+                    );

+    ASSERT_EFI_ERROR (Status);

+    FreePool (IpSb);

+  } else {

+    //

+    // NumberOfChildren is not zero, destroy all IP6 children instances.

+    //

+    while (!IsListEmpty (&IpSb->Children)) {

+      IpInstance = NET_LIST_HEAD (&IpSb->Children, IP6_PROTOCOL, Link);

+      ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle);

+    }

+

+    if (IpSb->NumChildren != 0) {

+      Status = EFI_DEVICE_ERROR;

+    }

+  }

+

+Exit:

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  Creates a child handle with a set of I/O services.

+

+  @param[in]  This               Protocol instance pointer.

+  @param[in]  ChildHandle        Pointer to the handle of the child to create.   If

+                                 it is NULL, then a new handle is created.   If it

+                                 is not NULL, then the I/O services are added to

+                                 the existing child handle.

+

+  @retval EFI_SUCCES             The child handle was created with the I/O services.

+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources availabe to create

+                                 the child.

+  @retval other                  The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ServiceBindingCreateChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    *ChildHandle

+  )

+{

+  IP6_SERVICE               *IpSb;

+  IP6_PROTOCOL              *IpInstance;

+  EFI_TPL                   OldTpl;

+  EFI_STATUS                Status;

+  VOID                      *Mnp;

+

+  if ((This == NULL) || (ChildHandle == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IpSb       = IP6_SERVICE_FROM_PROTOCOL (This);

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  IpInstance = AllocatePool (sizeof (IP6_PROTOCOL));

+

+  if (IpInstance == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Ip6InitProtocol (IpSb, IpInstance);

+

+  //

+  // Install Ip6 onto ChildHandle

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  ChildHandle,

+                  &gEfiIp6ProtocolGuid,

+                  &IpInstance->Ip6Proto,

+                  NULL

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  IpInstance->Handle = *ChildHandle;

+

+  //

+  // Open the Managed Network protocol BY_CHILD.

+  //

+  Status = gBS->OpenProtocol (

+                  IpSb->MnpChildHandle,

+                  &gEfiManagedNetworkProtocolGuid,

+                  (VOID **) &Mnp,

+                  gIp6DriverBinding.DriverBindingHandle,

+                  IpInstance->Handle,

+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                  );

+  if (EFI_ERROR (Status)) {

+    gBS->UninstallMultipleProtocolInterfaces (

+           ChildHandle,

+           &gEfiIp6ProtocolGuid,

+           &IpInstance->Ip6Proto,

+           NULL

+           );

+

+    goto ON_ERROR;

+  }

+

+  //

+  // Insert it into the service binding instance.

+  //

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  InsertTailList (&IpSb->Children, &IpInstance->Link);

+  IpSb->NumChildren++;

+

+  gBS->RestoreTPL (OldTpl);

+

+ON_ERROR:

+

+  if (EFI_ERROR (Status)) {

+

+    Ip6CleanProtocol (IpInstance);

+

+    FreePool (IpInstance);

+  }

+

+  return Status;

+}

+

+/**

+  Destroys a child handle with a set of I/O services.

+

+  @param[in]  This               Protocol instance pointer.

+  @param[in]  ChildHandle        Handle of the child to destroy.

+

+  @retval EFI_SUCCES             The I/O services were removed from the child

+                                 handle.

+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services

+                                  that are being removed.

+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.

+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because

+                                 its I/O services are being used.

+  @retval other                  The child handle was not destroyed.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    ChildHandle

+  )

+{

+  EFI_STATUS                Status;

+  IP6_SERVICE               *IpSb;

+  IP6_PROTOCOL              *IpInstance;

+  EFI_IP6_PROTOCOL          *Ip6;

+  EFI_TPL                   OldTpl;

+  INTN                      State;

+

+  if ((This == NULL) || (ChildHandle == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Retrieve the private context data structures

+  //

+  IpSb   = IP6_SERVICE_FROM_PROTOCOL (This);

+

+  Status = gBS->OpenProtocol (

+                  ChildHandle,

+                  &gEfiIp6ProtocolGuid,

+                  (VOID **) &Ip6,

+                  gIp6DriverBinding.DriverBindingHandle,

+                  ChildHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    return EFI_UNSUPPORTED;

+  }

+

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (Ip6);

+

+  if (IpInstance->Service != IpSb) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // A child can be destroyed more than once. For example,

+  // Ip6DriverBindingStop will destory all of its children.

+  // when UDP driver is being stopped, it will destory all

+  // the IP child it opens.

+  //

+  if (IpInstance->State == IP6_STATE_DESTROY) {

+    gBS->RestoreTPL (OldTpl);

+    return EFI_SUCCESS;

+  }

+

+  State             = IpInstance->State;

+  IpInstance->State = IP6_STATE_DESTROY;

+

+  //

+  // Close the Managed Network protocol.

+  //

+  gBS->CloseProtocol (

+         IpSb->MnpChildHandle,

+         &gEfiManagedNetworkProtocolGuid,

+         gIp6DriverBinding.DriverBindingHandle,

+         ChildHandle

+         );

+

+  //

+  // Uninstall the IP6 protocol first. Many thing happens during

+  // this:

+  // 1. The consumer of the IP6 protocol will be stopped if it

+  // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is

+  // stopped, IP driver's stop function will be called, and uninstall

+  // EFI_IP6_PROTOCOL will trigger the UDP's stop function. This

+  // makes it possible to create the network stack bottom up, and

+  // stop it top down.

+  // 2. the upper layer will recycle the received packet. The recycle

+  // event's TPL is higher than this function. The recycle events

+  // will be called back before preceeding. If any packets not recycled,

+  // that means there is a resource leak.

+  //

+  Status = gBS->UninstallProtocolInterface (

+                  ChildHandle,

+                  &gEfiIp6ProtocolGuid,

+                  &IpInstance->Ip6Proto

+                  );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = Ip6CleanProtocol (IpInstance);

+

+  Ip6SetVariableData (IpSb);

+

+  if (EFI_ERROR (Status)) {

+    gBS->InstallMultipleProtocolInterfaces (

+           &ChildHandle,

+           &gEfiIp6ProtocolGuid,

+           Ip6,

+           NULL

+           );

+

+    goto ON_ERROR;

+  }

+

+  RemoveEntryList (&IpInstance->Link);

+  ASSERT (IpSb->NumChildren > 0);

+  IpSb->NumChildren--;

+

+  gBS->RestoreTPL (OldTpl);

+

+  FreePool (IpInstance);

+  return EFI_SUCCESS;

+

+ON_ERROR:

+  IpInstance->State = State;

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.h b/NetworkPkg/Ip6Dxe/Ip6Driver.h
new file mode 100644
index 0000000..fcb92ab
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Driver.h
@@ -0,0 +1,185 @@
+/** @file

+  The driver binding and service binding protocol for IP6 driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_DRIVER_H__

+#define __EFI_IP6_DRIVER_H__

+

+extern EFI_DRIVER_BINDING_PROTOCOL  gIp6DriverBinding;

+extern EFI_COMPONENT_NAME_PROTOCOL  gIp6ComponentName;

+extern EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2;

+

+/**

+  Clean up an IP6 service binding instance. It releases all

+  the resource allocated by the instance. The instance may be

+  partly initialized, or partly destroyed. If a resource is

+  destroyed, it is marked as that in case the destory failed and

+  being called again later.

+

+  @param[in]  IpSb               The IP6 service binding instance to clean up.

+

+  @retval EFI_SUCCESS            The resource used by the instance are cleaned up.

+  @retval Others                 Failed to clean up some of the resources.

+

+**/

+EFI_STATUS

+Ip6CleanService (

+  IN IP6_SERVICE            *IpSb

+  );

+

+//

+// Function prototype for the driver's entry point

+//

+

+/**

+  This is the declaration of an EFI image entry point. This entry point is

+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including

+  both device drivers and bus drivers.

+

+  The entry point for IP6 driver which installs the driver

+  binding and component name protocol on its image.

+

+  @param[in]  ImageHandle           The firmware allocated handle for the UEFI image.

+  @param[in]  SystemTable           A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The operation completed successfully.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6DriverEntryPoint (

+  IN EFI_HANDLE             ImageHandle,

+  IN EFI_SYSTEM_TABLE       *SystemTable

+  );

+

+//

+// Function prototypes for the Drivr Binding Protocol

+//

+

+/**

+  Test to see if this driver supports ControllerHandle.

+

+  @param[in]  This                   Protocol instance pointer.

+  @param[in]  ControllerHandle       Handle of device to test.

+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child

+                                     device to start.

+

+  @retval EFI_SUCCESS                This driver supports this device.

+  @retval EFI_ALREADY_STARTED        This driver is already running on this device.

+  @retval other                      This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6DriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+/**

+  Start this driver on ControllerHandle.

+

+  @param[in]  This                Protocol instance pointer.

+  @param[in]  ControllerHandle    Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath Optional parameter used to pick a specific child

+                                  device to start.

+

+  @retval EFI_SUCCES              This driver is added to ControllerHandle.

+  @retval EFI_ALREADY_STARTED     This driver is already running on ControllerHandle.

+  @retval other                   This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6DriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+/**

+  Stop this driver on ControllerHandle.

+

+  @param[in]  This               Protocol instance pointer.

+  @param[in]  ControllerHandle   Handle of device to stop driver on.

+  @param[in]  NumberOfChildren   Number of Handles in ChildHandleBuffer. If number

+                                 of children is zero, stop the entire  bus driver.

+  @param[in]  ChildHandleBuffer  An array of child handles to be freed. May be NULL

+                                 if NumberOfChildren is 0.

+

+  @retval EFI_SUCCESS           The device was stopped.

+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6DriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL

+  );

+

+//

+// Function ptototypes for the ServiceBinding Prococol

+//

+

+/**

+  Creates a child handle with a set of I/O services.

+

+  @param[in]  This               Protocol instance pointer.

+  @param[in]  ChildHandle        Pointer to the handle of the child to create.   If

+                                 it is NULL, then a new handle is created.   If it

+                                 is not NULL, then the I/O services are added to

+                                 the existing child handle.

+

+  @retval EFI_SUCCES             The child handle was created with the I/O services.

+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources availabe to create

+                                 the child.

+  @retval other                  The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ServiceBindingCreateChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    *ChildHandle

+  );

+

+/**

+  Destroys a child handle with a set of I/O services.

+

+  @param[in]  This               Protocol instance pointer.

+  @param[in]  ChildHandle        Handle of the child to destroy.

+

+  @retval EFI_SUCCES             The I/O services were removed from the child

+                                 handle.

+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services

+                                  that are being removed.

+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.

+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because

+                                 its  I/O services are being used.

+  @retval other                  The child handle was not destroyed.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6ServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    ChildHandle

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6Dxe.inf b/NetworkPkg/Ip6Dxe/Ip6Dxe.inf
new file mode 100644
index 0000000..aeb341c
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Dxe.inf
@@ -0,0 +1,100 @@
+## @file

+#  Component description file for Ip6 module.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = Ip6Dxe

+  FILE_GUID                      = 5BEDB5CC-D830-4eb2-8742-2D4CC9B54F2C

+  MODULE_TYPE                    = UEFI_DRIVER

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = Ip6DriverEntryPoint

+  UNLOAD_IMAGE                   = NetLibDefaultUnload

+

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC

+#

+#  DRIVER_BINDING                =  gIp6DriverBinding

+#  COMPONENT_NAME                =  gIp6ComponentName

+#  COMPONENT_NAME2               =  gIp6ComponentName2

+#

+

+[Sources]

+  Ip6Output.h

+  Ip6Option.h

+  Ip6Input.h

+  Ip6Nd.h

+  Ip6Mld.h

+  Ip6Impl.c

+  Ip6Driver.c

+  ComponentName.c

+  Ip6Nd.c

+  Ip6Input.c

+  Ip6ConfigImpl.c

+  Ip6ConfigImpl.h

+  Ip6Impl.h

+  Ip6Option.c

+  Ip6If.h

+  Ip6Icmp.h

+  Ip6Mld.c

+  Ip6Common.c

+  Ip6Route.c

+  Ip6If.c

+  Ip6Driver.h

+  Ip6Output.c

+  Ip6Icmp.c

+  Ip6Common.h

+  Ip6Route.h

+  Ip6DxeStrings.uni

+  Ip6NvData.h

+  Ip6ConfigNv.c

+  Ip6ConfigNv.h

+  Ip6Config.vfr

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+

+

+[LibraryClasses]

+  BaseLib

+  BaseMemoryLib

+  DevicePathLib

+  HiiLib

+  UefiHiiServicesLib

+  PrintLib

+  MemoryAllocationLib

+  UefiBootServicesTableLib

+  UefiDriverEntryPoint

+  UefiRuntimeServicesTableLib

+  UefiLib

+  DebugLib

+  NetLib

+  DpcLib

+

+[Protocols]

+  gEfiManagedNetworkServiceBindingProtocolGuid

+  gEfiManagedNetworkProtocolGuid

+  gEfiIp6ServiceBindingProtocolGuid

+  gEfiIp6ProtocolGuid

+  gEfiIp6ConfigProtocolGuid

+  gEfiDhcp6ServiceBindingProtocolGuid

+  gEfiDhcp6ProtocolGuid

+  gEfiIpSecProtocolGuid

+  gEfiHiiConfigAccessProtocolGuid

+

+[Guids]

+  gEfiIfrTianoGuid                              ## CONSUMES ## GUID

diff --git a/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
new file mode 100644
index 0000000..cf85e08
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
Binary files differ
diff --git a/NetworkPkg/Ip6Dxe/Ip6Icmp.c b/NetworkPkg/Ip6Dxe/Ip6Icmp.c
new file mode 100644
index 0000000..db40b81
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Icmp.c
@@ -0,0 +1,684 @@
+/** @file

+  The ICMPv6 handle routines to process the ICMPv6 control messages.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[23] = {

+

+  {

+    ICMP_V6_DEST_UNREACHABLE,

+    ICMP_V6_NO_ROUTE_TO_DEST

+  },

+  {

+    ICMP_V6_DEST_UNREACHABLE,

+    ICMP_V6_COMM_PROHIBITED

+  },

+  {

+    ICMP_V6_DEST_UNREACHABLE,

+    ICMP_V6_BEYOND_SCOPE

+  },

+  {

+    ICMP_V6_DEST_UNREACHABLE,

+    ICMP_V6_ADDR_UNREACHABLE

+  },

+  {

+    ICMP_V6_DEST_UNREACHABLE,

+    ICMP_V6_PORT_UNREACHABLE

+  },

+  {

+    ICMP_V6_DEST_UNREACHABLE,

+    ICMP_V6_SOURCE_ADDR_FAILED

+  },

+  {

+    ICMP_V6_DEST_UNREACHABLE,

+    ICMP_V6_ROUTE_REJECTED

+  },

+

+  {

+    ICMP_V6_PACKET_TOO_BIG,

+    ICMP_V6_DEFAULT_CODE

+  },

+

+  {

+    ICMP_V6_TIME_EXCEEDED,

+    ICMP_V6_TIMEOUT_HOP_LIMIT

+  },

+  {

+    ICMP_V6_TIME_EXCEEDED,

+    ICMP_V6_TIMEOUT_REASSEMBLE

+  },

+

+  {

+    ICMP_V6_PARAMETER_PROBLEM,

+    ICMP_V6_ERRONEOUS_HEADER

+  },

+  {

+    ICMP_V6_PARAMETER_PROBLEM,

+    ICMP_V6_UNRECOGNIZE_NEXT_HDR

+  },

+  {

+    ICMP_V6_PARAMETER_PROBLEM,

+    ICMP_V6_UNRECOGNIZE_OPTION

+  },

+

+  {

+    ICMP_V6_ECHO_REQUEST,

+    ICMP_V6_DEFAULT_CODE

+  },

+  {

+    ICMP_V6_ECHO_REPLY,

+    ICMP_V6_DEFAULT_CODE

+  },

+

+  {

+    ICMP_V6_LISTENER_QUERY,

+    ICMP_V6_DEFAULT_CODE

+  },

+  {

+    ICMP_V6_LISTENER_REPORT,

+    ICMP_V6_DEFAULT_CODE

+  },

+  {

+    ICMP_V6_LISTENER_REPORT_2,

+    ICMP_V6_DEFAULT_CODE

+  },

+  {

+    ICMP_V6_LISTENER_DONE,

+    ICMP_V6_DEFAULT_CODE

+  },

+

+  {

+    ICMP_V6_ROUTER_SOLICIT,

+    ICMP_V6_DEFAULT_CODE

+  },

+  {

+    ICMP_V6_ROUTER_ADVERTISE,

+    ICMP_V6_DEFAULT_CODE

+  },

+  {

+    ICMP_V6_NEIGHBOR_SOLICIT,

+    ICMP_V6_DEFAULT_CODE

+  },

+  {

+    ICMP_V6_NEIGHBOR_ADVERTISE,

+    ICMP_V6_DEFAULT_CODE

+  },

+};

+

+/**

+  Reply an ICMPv6 echo request.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the ICMPv6 informational message.

+  @param[in]  Packet             The content of the ICMPv6 message with the IP head

+                                 removed.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.

+  @retval EFI_SUCCESS            Successfully answered the ICMPv6 Echo request.

+  @retval Others                 Failed to answer the ICMPv6 Echo request.

+

+**/

+EFI_STATUS

+Ip6IcmpReplyEcho (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_INFORMATION_HEAD *Icmp;

+  NET_BUF                   *Data;

+  EFI_STATUS                Status;

+  EFI_IP6_HEADER            ReplyHead;

+

+  Status = EFI_OUT_OF_RESOURCES;

+  //

+  // make a copy the packet, it is really a bad idea to

+  // send the MNP's buffer back to MNP.

+  //

+  Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN);

+  if (Data == NULL) {

+    goto Exit;

+  }

+

+  //

+  // Change the ICMP type to echo reply, exchange the source

+  // and destination, then send it. The source is updated to

+  // use specific destination. See RFC1122. SRR/RR option

+  // update is omitted.

+  //

+  Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Data, 0, NULL);

+  if (Icmp == NULL) {

+    NetbufFree (Data);

+    goto Exit;

+  }

+

+  Icmp->Head.Type     = ICMP_V6_ECHO_REPLY;

+  Icmp->Head.Checksum = 0;

+

+  //

+  // Generate the IPv6 basic header

+  // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address,

+  // the Source address of the Echo Reply must be the same address.

+  //

+  ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER));

+

+  ReplyHead.PayloadLength  = HTONS ((UINT16) (Packet->TotalSize));

+  ReplyHead.NextHeader     = IP6_ICMP;

+  ReplyHead.HopLimit       = IpSb->CurHopLimit;

+  IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress);

+

+  if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {

+    IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress);

+  }

+

+  //

+  // If source is unspecified, Ip6Output will select a source for us

+  //

+  Status = Ip6Output (

+             IpSb,

+             NULL,

+             NULL,

+             Data,

+             &ReplyHead,

+             NULL,

+             0,

+             Ip6SysPacketSent,

+             NULL

+             );

+

+Exit:

+  NetbufFree (Packet);

+  return Status;

+}

+

+/**

+  Process Packet Too Big message sent by a router in response to a packet that

+  it cannot forward because the packet is larger than the MTU of outgoing link.

+  Since this driver already uses IPv6 minimum link MTU as the maximum packet size,

+  if Packet Too Big message is still received, do not reduce the packet size, but

+  rather include a Fragment header in the subsequent packets.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the ICMPv6 error packet.

+  @param[in]  Packet             The content of the ICMPv6 error with the IP head

+                                 removed.

+

+  @retval EFI_SUCCESS            The ICMPv6 error processed successfully.

+  @retval EFI_OUT_OF_RESOURCES   Failed to finish the operation due to lack of

+                                 resource.

+  @retval EFI_NOT_FOUND          The packet too big message is not sent to us.

+

+**/

+EFI_STATUS

+Ip6ProcessPacketTooBig (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_ERROR_HEAD       Icmp;

+  UINT32                    Mtu;

+  IP6_ROUTE_ENTRY           *RouteEntry;

+  EFI_IPv6_ADDRESS          *DestAddress;

+

+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

+  Mtu         = NTOHL (Icmp.Fourth);

+  DestAddress = &Icmp.IpHead.DestinationAddress;

+

+  if (Mtu < IP6_MIN_LINK_MTU) {

+    //

+    // Normally the multicast address is considered to be on-link and not recorded

+    // in route table. Here it is added into the table since the MTU information

+    // need be recorded.

+    //

+    if (IP6_IS_MULTICAST (DestAddress)) {

+      RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL);

+      if (RouteEntry == NULL) {

+        NetbufFree (Packet);

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG;

+      InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link);

+      IpSb->RouteTable->TotalNum++;

+    } else {

+      RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL);

+      if (RouteEntry == NULL) {

+        NetbufFree (Packet);

+        return EFI_NOT_FOUND;

+      }

+

+      RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG;

+

+      Ip6FreeRouteEntry (RouteEntry);

+    }

+  }

+

+  NetbufFree (Packet);

+  return EFI_SUCCESS;

+}

+

+/**

+  Process the ICMPv6 error packet, and deliver the packet to upper layer.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the ICMPv6 error packet.

+  @param[in]  Packet             The content of the ICMPv6 error with the IP head

+                                 removed.

+

+  @retval EFI_SUCCESS            The ICMPv6 error processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval Others                 Failed to process the packet.

+

+**/

+EFI_STATUS

+Ip6ProcessIcmpError (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_ERROR_HEAD       Icmp;

+

+  //

+  // Check the validity of the packet

+  //

+  if (Packet->TotalSize < sizeof (Icmp)) {

+    goto DROP;

+  }

+

+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

+  if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) {

+    return Ip6ProcessPacketTooBig (IpSb, Head, Packet);

+  }

+

+  //

+  // Notify the upper-layer process that an ICMPv6 eror message is received.

+  //

+  IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;

+  return Ip6Demultiplex (IpSb, Head, Packet);

+

+DROP:

+  NetbufFree (Packet);

+  Packet = NULL;

+  return EFI_INVALID_PARAMETER;

+}

+

+/**

+  Process the ICMPv6 informational messages. If it is an ICMPv6 echo

+  request, answer it. If it is a MLD message, trigger MLD routines to

+  process it. If it is a ND message, trigger ND routines to process it.

+  Otherwise, deliver it to upper layer.

+

+  @param[in]  IpSb               The IP service that receivd the packet.

+  @param[in]  Head               The IP head of the ICMPv6 informational packet.

+  @param[in]  Packet             The content of the ICMPv6 informational packet

+                                 with IP head removed.

+

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval EFI_SUCCESS            The ICMPv6 informational message processed.

+  @retval Others                 Failed to process ICMPv6 informational message.

+

+**/

+EFI_STATUS

+Ip6ProcessIcmpInformation (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_INFORMATION_HEAD Icmp;

+  EFI_STATUS                Status;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);

+  ASSERT (Head != NULL);

+

+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

+  Status = EFI_INVALID_PARAMETER;

+

+  switch (Icmp.Head.Type) {

+  case ICMP_V6_ECHO_REQUEST:

+    //

+    // If ICMPv6 echo, reply it

+    //

+    if (Icmp.Head.Code == 0) {

+      Status = Ip6IcmpReplyEcho (IpSb, Head, Packet);

+    }

+    break;

+  case ICMP_V6_LISTENER_QUERY:

+    Status = Ip6ProcessMldQuery (IpSb, Head, Packet);

+    break;

+  case ICMP_V6_LISTENER_REPORT:

+  case ICMP_V6_LISTENER_REPORT_2:

+    Status = Ip6ProcessMldReport (IpSb, Head, Packet);

+    break;

+  case ICMP_V6_NEIGHBOR_SOLICIT:

+    Status = Ip6ProcessNeighborSolicit (IpSb, Head, Packet);

+    break;

+  case ICMP_V6_NEIGHBOR_ADVERTISE:

+    Status = Ip6ProcessNeighborAdvertise (IpSb, Head, Packet);

+    break;

+  case ICMP_V6_ROUTER_ADVERTISE:

+    Status = Ip6ProcessRouterAdvertise (IpSb, Head, Packet);

+    break;

+  case ICMP_V6_REDIRECT:

+    Status = Ip6ProcessRedirect (IpSb, Head, Packet);

+    break;

+  case ICMP_V6_ECHO_REPLY:

+    Status = Ip6Demultiplex (IpSb, Head, Packet);

+    break;

+  default:

+    Status = EFI_INVALID_PARAMETER;

+    break;

+  }

+

+  return Status;

+}

+

+/**

+  Handle the ICMPv6 packet. First validate the message format,

+  then, according to the message types, process it as an informational packet or

+  an error packet.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the ICMPv6 packet.

+  @param[in]  Packet             The content of the ICMPv6 packet with IP head

+                                 removed.

+

+  @retval EFI_INVALID_PARAMETER  The packet is malformated.

+  @retval EFI_SUCCESS            The ICMPv6 message successfully processed.

+  @retval Others                 Failed to handle the ICMPv6 packet.

+

+**/

+EFI_STATUS

+Ip6IcmpHandle (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_HEAD             Icmp;

+  UINT16                    PseudoCheckSum;

+  UINT16                    CheckSum;

+

+  //

+  // Check the validity of the incoming packet.

+  //

+  if (Packet->TotalSize < sizeof (Icmp)) {

+    goto DROP;

+  }

+

+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

+

+  //

+  // Make sure checksum is valid.

+  //

+  PseudoCheckSum = NetIp6PseudoHeadChecksum (

+                     &Head->SourceAddress,

+                     &Head->DestinationAddress,

+                     IP6_ICMP,

+                     Packet->TotalSize

+                     );

+  CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet));

+  if (CheckSum != 0) {

+    goto DROP;

+  }

+

+  //

+  // According to the packet type, call corresponding process

+  //

+  if (Icmp.Type <= ICMP_V6_ERROR_MAX) {

+    return Ip6ProcessIcmpError (IpSb, Head, Packet);

+  } else {

+    return Ip6ProcessIcmpInformation (IpSb, Head, Packet);

+  }

+

+DROP:

+  NetbufFree (Packet);

+  return EFI_INVALID_PARAMETER;

+}

+

+/**

+  Retrieve the Prefix address according to the PrefixLength by clear the useless

+  bits.

+

+  @param[in]       PrefixLength  The prefix length of the prefix.

+  @param[in, out]  Prefix        On input, points to the original prefix address

+                                 with dirty bits; on output, points to the updated

+                                 address with useless bit clear.

+

+**/

+VOID

+Ip6GetPrefix (

+  IN     UINT8              PrefixLength,

+  IN OUT EFI_IPv6_ADDRESS   *Prefix

+  )

+{

+  UINT8                     Byte;

+  UINT8                     Bit;

+  UINT8                     Mask;

+  UINT8                     Value;

+

+  ASSERT ((Prefix != NULL) && (PrefixLength < IP6_PREFIX_NUM));

+

+  if (PrefixLength == 0) {

+    ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS));

+    return ;

+  }

+

+  if (PrefixLength == IP6_PREFIX_NUM - 1) {

+    return ;

+  }

+

+  Byte  = (UINT8) (PrefixLength / 8);

+  Bit   = (UINT8) (PrefixLength % 8);

+  Value = Prefix->Addr[Byte];

+

+  if ((Byte > 0) && (Byte < 16)) {

+    ZeroMem (Prefix->Addr + Byte, 16 - Byte);

+  }

+

+  if (Bit > 0) {

+    Mask = (UINT8) (0xFF << (8 - Bit));

+    Prefix->Addr[Byte] = (UINT8) (Value & Mask);

+  }

+

+}

+

+/**

+  Check whether the DestinationAddress is an anycast address.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  DestinationAddress Points to the Destination Address of the packet.

+

+  @retval TRUE                   The DestinationAddress is anycast address.

+  @retval FALSE                  The DestinationAddress is not anycast address.

+

+**/

+BOOLEAN

+Ip6IsAnycast (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *DestinationAddress

+  )

+{

+  IP6_PREFIX_LIST_ENTRY     *PrefixEntry;

+  EFI_IPv6_ADDRESS          Prefix;

+  BOOLEAN                   Flag;

+

+  ZeroMem (&Prefix, sizeof (EFI_IPv6_ADDRESS));

+

+  Flag = FALSE;

+

+  //

+  // If the address is known as on-link or autonomous prefix, record it as

+  // anycast address.

+  //

+  do {

+    PrefixEntry = Ip6FindPrefixListEntry (IpSb, Flag, 255, DestinationAddress);

+    if (PrefixEntry != NULL) {

+      IP6_COPY_ADDRESS (&Prefix, &PrefixEntry->Prefix);

+      Ip6GetPrefix (PrefixEntry->PrefixLength, &Prefix);

+      if (EFI_IP6_EQUAL (&Prefix, DestinationAddress)) {

+        return TRUE;

+      }

+    }

+

+    Flag = (BOOLEAN) !Flag;

+  } while (Flag);

+

+  return FALSE;

+}

+

+/**

+  Generate ICMPv6 error message and send it out to DestinationAddress. Currently

+  Destination Unreachable message, Time Exceeded message and Parameter Problem

+  message are supported.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Packet             The packet which invoking ICMPv6 error.

+  @param[in]  SourceAddress      If not NULL, points to the SourceAddress.

+                                 Otherwise, the IP layer will select a source address

+                                 according to the DestinationAddress.

+  @param[in]  DestinationAddress Points to the Destination Address of the ICMPv6

+                                 error message.

+  @param[in]  Type               The type of the ICMPv6 message.

+  @param[in]  Code               The additional level of the ICMPv6 message.

+  @param[in]  Pointer            If not NULL, identifies the octet offset within

+                                 the invoking packet where the error was detected.

+

+  @retval EFI_INVALID_PARAMETER  The packet is malformated.

+  @retval EFI_OUT_OF_RESOURCES   There is no sufficient resource to complete the

+                                 operation.

+  @retval EFI_SUCCESS            The ICMPv6 message was successfully sent out.

+  @retval Others                 Failed to generate the ICMPv6 packet.

+

+**/

+EFI_STATUS

+Ip6SendIcmpError (

+  IN IP6_SERVICE            *IpSb,

+  IN NET_BUF                *Packet,

+  IN EFI_IPv6_ADDRESS       *SourceAddress       OPTIONAL,

+  IN EFI_IPv6_ADDRESS       *DestinationAddress,

+  IN UINT8                  Type,

+  IN UINT8                  Code,

+  IN UINT32                 *Pointer             OPTIONAL

+  )

+{

+  UINT32                    PacketLen;

+  NET_BUF                   *ErrorMsg;

+  UINT16                    PayloadLen;

+  EFI_IP6_HEADER            Head;

+  IP6_ICMP_INFORMATION_HEAD *IcmpHead;

+  UINT8                     *ErrorBody;

+

+  if (DestinationAddress == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // An ICMPv6 error message must not be originated as a result of receiving

+  // a packet whose source address does not uniquely identify a single node --

+  // e.g., the IPv6 Unspecified Address, an IPv6 multicast address, or an address

+  // known by the ICMP message originator to be an IPv6 anycast address.

+  //

+  if (NetIp6IsUnspecifiedAddr (DestinationAddress) ||

+      IP6_IS_MULTICAST (DestinationAddress)        ||

+      Ip6IsAnycast (IpSb, DestinationAddress)

+      ) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  switch (Type) {

+  case ICMP_V6_DEST_UNREACHABLE:

+  case ICMP_V6_TIME_EXCEEDED:

+    break;

+

+  case ICMP_V6_PARAMETER_PROBLEM:

+    if (Pointer == NULL) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    break;

+

+  default:

+    return EFI_INVALID_PARAMETER;

+  }

+

+  PacketLen = sizeof (IP6_ICMP_ERROR_HEAD) + Packet->TotalSize;

+

+  if (PacketLen > IpSb->MaxPacketSize) {

+    PacketLen = IpSb->MaxPacketSize;

+  }

+

+  ErrorMsg = NetbufAlloc (PacketLen);

+  if (ErrorMsg == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  PayloadLen = (UINT16) (PacketLen - sizeof (EFI_IP6_HEADER));

+

+  //

+  // Create the basic IPv6 header.

+  //

+  ZeroMem (&Head, sizeof (EFI_IP6_HEADER));

+

+  Head.PayloadLength  = HTONS (PayloadLen);

+  Head.NextHeader     = IP6_ICMP;

+  Head.HopLimit       = IpSb->CurHopLimit;

+

+  if (SourceAddress != NULL) {

+    IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);

+  } else {

+    ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));

+  }

+

+  IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);

+

+  NetbufReserve (ErrorMsg, sizeof (EFI_IP6_HEADER));

+

+  //

+  // Fill in the ICMP error message head

+  //

+  IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (ErrorMsg, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);

+  if (IcmpHead == NULL) {

+    NetbufFree (ErrorMsg);

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));

+  IcmpHead->Head.Type = Type;

+  IcmpHead->Head.Code = Code;

+

+  if (Pointer != NULL) {

+    IcmpHead->Fourth = HTONL (*Pointer);

+  }

+

+  //

+  // Fill in the ICMP error message body

+  //

+  PayloadLen -= sizeof (IP6_ICMP_INFORMATION_HEAD);

+  ErrorBody =  NetbufAllocSpace (ErrorMsg, PayloadLen, FALSE);

+  if (ErrorBody != NULL) {

+    ZeroMem (ErrorBody, PayloadLen);

+    NetbufCopy (Packet, 0, PayloadLen, ErrorBody);

+  }

+

+  //

+  // Transmit the packet

+  //

+  return Ip6Output (IpSb, NULL, NULL, ErrorMsg, &Head, NULL, 0, Ip6SysPacketSent, NULL);

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Icmp.h b/NetworkPkg/Ip6Dxe/Ip6Icmp.h
new file mode 100644
index 0000000..6852ae5
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Icmp.h
@@ -0,0 +1,108 @@
+/** @file

+  Header file for ICMPv6 protocol.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_ICMP_H__

+#define __EFI_IP6_ICMP_H__

+

+#define ICMP_V6_DEFAULT_CODE          0

+

+#define ICMP_V6_ERROR_MAX             127

+

+//

+// ICMPv6 message classes, each class of ICMPv6 message shares

+// a common message format. INVALID_MESSAGE is only a flag.

+//

+#define ICMP_V6_INVALID_MESSAGE       0

+#define ICMP_V6_ERROR_MESSAGE         1

+#define ICMP_V6_INFORMATION_MESSAGE   2

+

+

+extern EFI_IP6_ICMP_TYPE  mIp6SupportedIcmp[];

+

+/**

+  Handle the ICMPv6 packet. First validate the message format,

+  then, according to the message types, process it as an informational packet or

+  an error packet.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the ICMPv6 packet.

+  @param[in]  Packet             The content of the ICMPv6 packet with IP head

+                                 removed.

+

+  @retval EFI_INVALID_PARAMETER  The packet is malformated.

+  @retval EFI_SUCCESS            The ICMPv6 message successfully processed.

+  @retval Others                 Failed to handle the ICMPv6 packet.

+

+**/

+EFI_STATUS

+Ip6IcmpHandle (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  );

+

+/**

+  Check whether the DestinationAddress is an anycast address.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  DestinationAddress Points to the Destination Address of the packet.

+

+  @retval TRUE                   The DestinationAddress is anycast address.

+  @retval FALSE                  The DestinationAddress is not anycast address.

+

+**/

+BOOLEAN

+Ip6IsAnycast (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *DestinationAddress

+  );

+

+/**

+  Generate ICMPv6 error message and send it out to DestinationAddress. Currently

+  Destination Unreachable message, Time Exceeded message and Parameter Problem

+  message are supported.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Packet             The packet which invoking ICMPv6 error.

+  @param[in]  SourceAddress      If not NULL, points to the SourceAddress.

+                                 Otherwise, the IP layer will select a source address

+                                 according to the DestinationAddress.

+  @param[in]  DestinationAddress Points to the Destination Address of the ICMPv6

+                                 error message.

+  @param[in]  Type               The type of the ICMPv6 message.

+  @param[in]  Code               The additional level of the ICMPv6 message.

+  @param[in]  Pointer            If not NULL, identifies the octet offset within

+                                 the invoking packet where the error was detected.

+

+  @retval EFI_INVALID_PARAMETER  The packet is malformated.

+  @retval EFI_OUT_OF_RESOURCES   There is no sufficient resource to complete the

+                                 operation.

+  @retval EFI_SUCCESS            The ICMPv6 message was successfully sent out.

+  @retval Others                 Failed to generate the ICMPv6 packet.

+

+**/

+EFI_STATUS

+Ip6SendIcmpError (

+  IN IP6_SERVICE            *IpSb,

+  IN NET_BUF                *Packet,

+  IN EFI_IPv6_ADDRESS       *SourceAddress       OPTIONAL,

+  IN EFI_IPv6_ADDRESS       *DestinationAddress,

+  IN UINT8                  Type,

+  IN UINT8                  Code,

+  IN UINT32                 *Pointer             OPTIONAL

+  );

+

+#endif

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6If.c b/NetworkPkg/Ip6Dxe/Ip6If.c
new file mode 100644
index 0000000..198b547
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6If.c
@@ -0,0 +1,802 @@
+/** @file

+  Implement IP6 pesudo interface.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+/**

+  Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.

+

+  @param[in]  Event              The transmit token's event.

+  @param[in]  Context            The Context which is pointed to the token.

+

+**/

+VOID

+EFIAPI

+Ip6OnFrameSent (

+  IN EFI_EVENT               Event,

+  IN VOID                    *Context

+  );

+

+/**

+  Fileter function to cancel all the frame related to an IP instance.

+

+  @param[in]  Frame             The transmit request to test whether to cancel.

+  @param[in]  Context           The context which is the Ip instance that issued

+                                the transmit.

+

+  @retval TRUE                  The frame belongs to this instance and is to be

+                                removed.

+  @retval FALSE                 The frame doesn't belong to this instance.

+

+**/

+BOOLEAN

+Ip6CancelInstanceFrame (

+  IN IP6_LINK_TX_TOKEN *Frame,

+  IN VOID              *Context

+  )

+{

+  if (Frame->IpInstance == (IP6_PROTOCOL *) Context) {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Set the interface's address. This will trigger the DAD process for the

+  address to set. To set an already set address, the lifetimes wil be

+  updated to the new value passed in.

+

+  @param[in]  Interface             The interface to set the address.

+  @param[in]  Ip6Addr               The interface's to be assigned IPv6 address.

+  @param[in]  IsAnycast             If TRUE, the unicast IPv6 address is anycast.

+                                    Otherwise, it is not anycast.

+  @param[in]  PrefixLength          The prefix length of the Ip6Addr.

+  @param[in]  ValidLifetime         The valid lifetime for this address.

+  @param[in]  PreferredLifetime     The preferred lifetime for this address.

+  @param[in]  DadCallback           The caller's callback to trigger when DAD finishes.

+                                    This is an optional parameter that may be NULL.

+  @param[in]  Context               The context that will be passed to DadCallback.

+                                    This is an optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS               The interface is scheduled to be configured with

+                                    the specified address.

+  @retval EFI_OUT_OF_RESOURCES      Failed to set the interface's address due to

+                                    lack of resources.

+

+**/

+EFI_STATUS

+Ip6SetAddress (

+  IN IP6_INTERFACE          *Interface,

+  IN EFI_IPv6_ADDRESS       *Ip6Addr,

+  IN BOOLEAN                IsAnycast,

+  IN UINT8                  PrefixLength,

+  IN UINT32                 ValidLifetime,

+  IN UINT32                 PreferredLifetime,

+  IN IP6_DAD_CALLBACK       DadCallback  OPTIONAL,

+  IN VOID                   *Context     OPTIONAL

+  )

+{

+  IP6_SERVICE            *IpSb;

+  IP6_ADDRESS_INFO       *AddressInfo;

+  LIST_ENTRY             *Entry;

+  IP6_PREFIX_LIST_ENTRY  *PrefixEntry;

+  UINT64                 Delay;

+  IP6_DELAY_JOIN_LIST    *DelayNode;

+

+  NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);

+

+  IpSb = Interface->Service;

+

+  if (Ip6IsOneOfSetAddress (IpSb, Ip6Addr, NULL, &AddressInfo)) {

+    ASSERT (AddressInfo != NULL);

+    //

+    // Update the lifetime.

+    //

+    AddressInfo->ValidLifetime     = ValidLifetime;

+    AddressInfo->PreferredLifetime = PreferredLifetime;

+

+    if (DadCallback != NULL) {

+      DadCallback (TRUE, Ip6Addr, Context);

+    }

+

+    return EFI_SUCCESS;

+  }

+

+  AddressInfo = (IP6_ADDRESS_INFO *) AllocatePool (sizeof (IP6_ADDRESS_INFO));

+  if (AddressInfo == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  AddressInfo->Signature         = IP6_ADDR_INFO_SIGNATURE;

+  IP6_COPY_ADDRESS (&AddressInfo->Address, Ip6Addr);

+  AddressInfo->IsAnycast         = IsAnycast;

+  AddressInfo->PrefixLength      = PrefixLength;

+  AddressInfo->ValidLifetime     = ValidLifetime;

+  AddressInfo->PreferredLifetime = PreferredLifetime;

+

+  if (AddressInfo->PrefixLength == 0) {

+    //

+    // Find an appropriate prefix from on-link prefixes and update the prefixlength.

+    // Longest prefix match is used here.

+    //

+    NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {

+      PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);

+

+      if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {

+        AddressInfo->PrefixLength = PrefixEntry->PrefixLength;

+        break;

+      }

+    }

+  }

+

+  if (AddressInfo->PrefixLength == 0) {

+    //

+    // If the prefix length is still zero, try the autonomous prefixes.

+    // Longest prefix match is used here.

+    //

+    NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {

+      PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);

+

+      if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {

+        AddressInfo->PrefixLength = PrefixEntry->PrefixLength;

+        break;

+      }

+    }

+  }

+

+  if (AddressInfo->PrefixLength == 0) {

+    //

+    // BUGBUG: Stil fail, use 64 as the default prefix length.

+    //

+    AddressInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;

+  }

+

+

+  //

+  // Node should delay joining the solicited-node mulitcast address by a random delay

+  // between 0 and MAX_RTR_SOLICITATION_DELAY (1 second).

+  // Thus queue the address to be processed in Duplicate Address Detection module

+  // after the delay time (in milliseconds).

+  //

+  Delay = (UINT64) NET_RANDOM (NetRandomInitSeed ());

+  Delay = MultU64x32 (Delay, IP6_ONE_SECOND_IN_MS);

+  Delay = RShiftU64 (Delay, 32);

+

+  DelayNode = (IP6_DELAY_JOIN_LIST *) AllocatePool (sizeof (IP6_DELAY_JOIN_LIST));

+  if (DelayNode == NULL) {

+    FreePool (AddressInfo);

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  DelayNode->DelayTime   = (UINT32) (DivU64x32 (Delay, IP6_TIMER_INTERVAL_IN_MS));

+  DelayNode->Interface   = Interface;

+  DelayNode->AddressInfo = AddressInfo;

+  DelayNode->DadCallback = DadCallback;

+  DelayNode->Context     = Context;

+

+  InsertTailList (&Interface->DelayJoinList, &DelayNode->Link);

+  return EFI_SUCCESS;

+}

+

+/**

+  Create an IP6_INTERFACE.

+

+  @param[in]  IpSb                  The IP6 service binding instance.

+  @param[in]  LinkLocal             If TRUE, the instance is created for link-local address.

+                                    Otherwise, it is not for a link-local address.

+

+  @return Point to the created IP6_INTERFACE, otherwise NULL.

+

+**/

+IP6_INTERFACE *

+Ip6CreateInterface (

+  IN IP6_SERVICE            *IpSb,

+  IN BOOLEAN                LinkLocal

+  )

+{

+  EFI_STATUS                Status;

+  IP6_INTERFACE             *Interface;

+  EFI_IPv6_ADDRESS          *Ip6Addr;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  Interface = AllocatePool (sizeof (IP6_INTERFACE));

+  if (Interface == NULL) {

+    return NULL;

+  }

+

+  Interface->Signature        = IP6_INTERFACE_SIGNATURE;

+  Interface->RefCnt           = 1;

+

+  InitializeListHead (&Interface->AddressList);

+  Interface->AddressCount     = 0;

+  Interface->Configured       = FALSE;

+

+  Interface->Service          = IpSb;

+  Interface->Controller       = IpSb->Controller;

+  Interface->Image            = IpSb->Image;

+

+  InitializeListHead (&Interface->ArpQues);

+  InitializeListHead (&Interface->SentFrames);

+

+  Interface->DupAddrDetect    = IpSb->Ip6ConfigInstance.DadXmits.DupAddrDetectTransmits;

+  InitializeListHead (&Interface->DupAddrDetectList);

+

+  InitializeListHead (&Interface->DelayJoinList);

+

+  InitializeListHead (&Interface->IpInstances);

+  Interface->PromiscRecv      = FALSE;

+

+  if (!LinkLocal) {

+    return Interface;

+  }

+

+  //

+  // Get the link local addr

+  //

+  Ip6Addr = Ip6CreateLinkLocalAddr (IpSb);

+  if (Ip6Addr == NULL) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Perform DAD - Duplicate Address Detection.

+  //

+  Status = Ip6SetAddress (

+             Interface,

+             Ip6Addr,

+             FALSE,

+             IP6_LINK_LOCAL_PREFIX_LENGTH,

+             (UINT32) IP6_INFINIT_LIFETIME,

+             (UINT32) IP6_INFINIT_LIFETIME,

+             NULL,

+             NULL

+             );

+

+  FreePool (Ip6Addr);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  return Interface;

+

+ON_ERROR:

+

+  FreePool (Interface);

+  return NULL;

+}

+

+/**

+  Free the interface used by IpInstance. All the IP instance with

+  the same Ip/prefix pair share the same interface. It is reference

+  counted. All the frames that haven't been sent will be cancelled.

+  Because the IpInstance is optional, the caller must remove

+  IpInstance from the interface's instance list.

+

+  @param[in]  Interface         The interface used by the IpInstance.

+  @param[in]  IpInstance        The IP instance that free the interface. NULL if

+                                the IP driver is releasing the default interface.

+

+**/

+VOID

+Ip6CleanInterface (

+  IN  IP6_INTERFACE         *Interface,

+  IN  IP6_PROTOCOL          *IpInstance           OPTIONAL

+  )

+{

+  IP6_DAD_ENTRY             *Duplicate;

+  IP6_DELAY_JOIN_LIST       *Delay;

+

+  NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);

+  ASSERT (Interface->RefCnt > 0);

+

+  //

+  // Remove all the pending transmit token related to this IP instance.

+  //

+  Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, IpInstance);

+

+  if (--Interface->RefCnt > 0) {

+    return;

+  }

+

+  //

+  // Destory the interface if this is the last IP instance.

+  // Remove all the system transmitted packets

+  // from this interface, cancel the receive request if exists.

+  //

+  Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, NULL);

+

+  ASSERT (IsListEmpty (&Interface->IpInstances));

+  ASSERT (IsListEmpty (&Interface->ArpQues));

+  ASSERT (IsListEmpty (&Interface->SentFrames));

+

+  while (!IsListEmpty (&Interface->DupAddrDetectList)) {

+    Duplicate = NET_LIST_HEAD (&Interface->DupAddrDetectList, IP6_DAD_ENTRY, Link);

+    NetListRemoveHead (&Interface->DupAddrDetectList);

+    FreePool (Duplicate);

+  }

+

+  while (!IsListEmpty (&Interface->DelayJoinList)) {

+    Delay = NET_LIST_HEAD (&Interface->DelayJoinList, IP6_DELAY_JOIN_LIST, Link);

+    NetListRemoveHead (&Interface->DelayJoinList);

+    FreePool (Delay);

+  }

+

+  Ip6RemoveAddr (Interface->Service, &Interface->AddressList, &Interface->AddressCount, NULL, 0);

+

+  RemoveEntryList (&Interface->Link);

+  FreePool (Interface);

+}

+

+/**

+  Create and wrap a transmit request into a newly allocated IP6_LINK_TX_TOKEN.

+

+  @param[in]  Interface         The interface to send out from.

+  @param[in]  IpInstance        The IpInstance that transmit the packet.  NULL if

+                                the packet is sent by the IP6 driver itself.

+  @param[in]  Packet            The packet to transmit

+  @param[in]  CallBack          Call back function to execute if transmission

+                                finished.

+  @param[in]  Context           Opaque parameter to the callback.

+

+  @return The wrapped token if succeed or NULL.

+

+**/

+IP6_LINK_TX_TOKEN *

+Ip6CreateLinkTxToken (

+  IN IP6_INTERFACE          *Interface,

+  IN IP6_PROTOCOL           *IpInstance    OPTIONAL,

+  IN NET_BUF                *Packet,

+  IN IP6_FRAME_CALLBACK     CallBack,

+  IN VOID                   *Context

+  )

+{

+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;

+  EFI_MANAGED_NETWORK_TRANSMIT_DATA     *MnpTxData;

+  IP6_LINK_TX_TOKEN                     *Token;

+  EFI_STATUS                            Status;

+  UINT32                                Count;

+

+  Token = AllocatePool (sizeof (IP6_LINK_TX_TOKEN) + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));

+

+  if (Token == NULL) {

+    return NULL;

+  }

+

+  Token->Signature = IP6_LINK_TX_SIGNATURE;

+  InitializeListHead (&Token->Link);

+

+  Token->IpInstance = IpInstance;

+  Token->CallBack   = CallBack;

+  Token->Packet     = Packet;

+  Token->Context    = Context;

+  ZeroMem (&Token->DstMac, sizeof (EFI_MAC_ADDRESS));

+  IP6_COPY_LINK_ADDRESS (&Token->SrcMac, &Interface->Service->SnpMode.CurrentAddress);

+

+  MnpToken          = &(Token->MnpToken);

+  MnpToken->Status  = EFI_NOT_READY;

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  Ip6OnFrameSent,

+                  Token,

+                  &MnpToken->Event

+                  );

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Token);

+    return NULL;

+  }

+

+  MnpTxData                     = &Token->MnpTxData;

+  MnpToken->Packet.TxData       = MnpTxData;

+

+  MnpTxData->DestinationAddress = &Token->DstMac;

+  MnpTxData->SourceAddress      = &Token->SrcMac;

+  MnpTxData->ProtocolType       = IP6_ETHER_PROTO;

+  MnpTxData->DataLength         = Packet->TotalSize;

+  MnpTxData->HeaderLength       = 0;

+

+  Count                         = Packet->BlockOpNum;

+

+  NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);

+  MnpTxData->FragmentCount      = (UINT16)Count;

+

+  return Token;

+}

+

+/**

+  Free the link layer transmit token. It will close the event,

+  then free the memory used.

+

+  @param[in]  Token                 Token to free.

+

+**/

+VOID

+Ip6FreeLinkTxToken (

+  IN IP6_LINK_TX_TOKEN      *Token

+  )

+{

+  NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);

+

+  gBS->CloseEvent (Token->MnpToken.Event);

+  FreePool (Token);

+}

+

+/**

+  Callback function when the received packet is freed.

+  Check Ip6OnFrameReceived for information.

+

+  @param[in]  Context       Points to EFI_MANAGED_NETWORK_RECEIVE_DATA.

+

+**/

+VOID

+EFIAPI

+Ip6RecycleFrame (

+  IN VOID                   *Context

+  )

+{

+  EFI_MANAGED_NETWORK_RECEIVE_DATA  *RxData;

+

+  RxData = (EFI_MANAGED_NETWORK_RECEIVE_DATA *) Context;

+

+  gBS->SignalEvent (RxData->RecycleEvent);

+}

+

+/**

+  Received a frame from MNP. Wrap it in net buffer then deliver

+  it to IP's input function. The ownship of the packet also

+  is transferred to IP. When Ip is finished with this packet, it

+  will call NetbufFree to release the packet, NetbufFree will

+  again call the Ip6RecycleFrame to signal MNP's event and free

+  the token used.

+

+  @param[in]  Context         Context for the callback.

+

+**/

+VOID

+EFIAPI

+Ip6OnFrameReceivedDpc (

+  IN VOID                     *Context

+  )

+{

+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;

+  EFI_MANAGED_NETWORK_RECEIVE_DATA      *MnpRxData;

+  IP6_LINK_RX_TOKEN                     *Token;

+  NET_FRAGMENT                          Netfrag;

+  NET_BUF                               *Packet;

+  UINT32                                Flag;

+  IP6_SERVICE                           *IpSb;

+

+  Token = (IP6_LINK_RX_TOKEN *) Context;

+  NET_CHECK_SIGNATURE (Token, IP6_LINK_RX_SIGNATURE);

+

+  //

+  // First clear the interface's receive request in case the

+  // caller wants to call Ip6ReceiveFrame in the callback.

+  //

+  IpSb = (IP6_SERVICE *) Token->Context;

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+

+  MnpToken  = &Token->MnpToken;

+  MnpRxData = MnpToken->Packet.RxData;

+

+  if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {

+    Token->CallBack (NULL, MnpToken->Status, 0, Token->Context);

+    return ;

+  }

+

+  //

+  // Wrap the frame in a net buffer then deliever it to IP input.

+  // IP will reassemble the packet, and deliver it to upper layer

+  //

+  Netfrag.Len  = MnpRxData->DataLength;

+  Netfrag.Bulk = MnpRxData->PacketData;

+

+  Packet = NetbufFromExt (&Netfrag, 1, IP6_MAX_HEADLEN, 0, Ip6RecycleFrame, Token->MnpToken.Packet.RxData);

+

+  if (Packet == NULL) {

+    gBS->SignalEvent (MnpRxData->RecycleEvent);

+

+    Token->CallBack (NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);

+

+    return ;

+  }

+

+  Flag  = (MnpRxData->BroadcastFlag ? IP6_LINK_BROADCAST : 0);

+  Flag |= (MnpRxData->MulticastFlag ? IP6_LINK_MULTICAST : 0);

+  Flag |= (MnpRxData->PromiscuousFlag ? IP6_LINK_PROMISC : 0);

+

+  Token->CallBack (Packet, EFI_SUCCESS, Flag, Token->Context);

+}

+

+/**

+  Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK.

+

+  @param  Event                 The receive event delivered to MNP for receive.

+  @param  Context               Context for the callback.

+

+**/

+VOID

+EFIAPI

+Ip6OnFrameReceived (

+  IN EFI_EVENT                Event,

+  IN VOID                     *Context

+  )

+{

+  //

+  // Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK

+  //

+  QueueDpc (TPL_CALLBACK, Ip6OnFrameReceivedDpc, Context);

+}

+

+/**

+  Request to receive the packet from the interface.

+

+  @param[in]  CallBack          Function to call when receive finished.

+  @param[in]  IpSb              Points to IP6 service binding instance.

+

+  @retval EFI_ALREADY_STARTED   There is already a pending receive request.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to receive.

+  @retval EFI_SUCCESS           The recieve request has been started.

+

+**/

+EFI_STATUS

+Ip6ReceiveFrame (

+  IN  IP6_FRAME_CALLBACK    CallBack,

+  IN  IP6_SERVICE           *IpSb

+  )

+{

+  EFI_STATUS                Status;

+  IP6_LINK_RX_TOKEN         *Token;

+

+  if (IpSb->InDestroy) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  Token           = &IpSb->RecvRequest;

+  Token->CallBack = CallBack;

+  Token->Context  = (VOID *) IpSb;

+

+  Status = IpSb->Mnp->Receive (IpSb->Mnp, &Token->MnpToken);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Callback funtion when frame transmission is finished. It will

+  call the frame owner's callback function to tell it the result.

+

+  @param[in]  Context        Context which points to the token.

+

+**/

+VOID

+EFIAPI

+Ip6OnFrameSentDpc (

+  IN VOID                    *Context

+  )

+{

+  IP6_LINK_TX_TOKEN         *Token;

+

+  Token = (IP6_LINK_TX_TOKEN *) Context;

+  NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);

+

+  RemoveEntryList (&Token->Link);

+

+  Token->CallBack (

+          Token->Packet,

+          Token->MnpToken.Status,

+          0,

+          Token->Context

+          );

+

+  Ip6FreeLinkTxToken (Token);

+}

+

+/**

+  Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.

+

+  @param[in]  Event                 The transmit token's event.

+  @param[in]  Context               Context which points to the token.

+

+**/

+VOID

+EFIAPI

+Ip6OnFrameSent (

+  IN EFI_EVENT               Event,

+  IN VOID                    *Context

+  )

+{

+  //

+  // Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK

+  //

+  QueueDpc (TPL_CALLBACK, Ip6OnFrameSentDpc, Context);

+}

+

+/**

+  Send a frame from the interface. If the next hop is a multicast address,

+  it is transmitted immediately. If the next hop is a unicast,

+  and the NextHop's MAC is not known, it will perform address resolution.

+  If an error occurred, the CallBack won't be called. So, the caller

+  must test the return value, and take action when there is an error.

+

+  @param[in]  Interface         The interface to send the frame from

+  @param[in]  IpInstance        The IP child that request the transmission.

+                                NULL if it is the IP6 driver itself.

+  @param[in]  Packet            The packet to transmit.

+  @param[in]  NextHop           The immediate destination to transmit the packet to.

+  @param[in]  CallBack          Function to call back when transmit finished.

+  @param[in]  Context           Opaque parameter to the callback.

+

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to send the frame.

+  @retval EFI_NO_MAPPING        Can't resolve the MAC for the nexthop.

+  @retval EFI_SUCCESS           The packet successfully transmitted.

+

+**/

+EFI_STATUS

+Ip6SendFrame (

+  IN  IP6_INTERFACE         *Interface,

+  IN  IP6_PROTOCOL          *IpInstance      OPTIONAL,

+  IN  NET_BUF               *Packet,

+  IN  EFI_IPv6_ADDRESS      *NextHop,

+  IN  IP6_FRAME_CALLBACK    CallBack,

+  IN  VOID                  *Context

+  )

+{

+  IP6_SERVICE               *IpSb;

+  IP6_LINK_TX_TOKEN         *Token;

+  EFI_STATUS                Status;

+  IP6_NEIGHBOR_ENTRY        *NeighborCache;

+  LIST_ENTRY                *Entry;

+  IP6_NEIGHBOR_ENTRY        *ArpQue;

+

+  IpSb = Interface->Service;

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  //

+  // Only when link local address is performing DAD, the interface could be used in unconfigured.

+  //

+  if (IpSb->LinkLocalOk) {

+    ASSERT (Interface->Configured);

+  }

+

+  Token = Ip6CreateLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);

+

+  if (Token == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  if (IP6_IS_MULTICAST (NextHop)) {

+    Status = Ip6GetMulticastMac (IpSb->Mnp, NextHop, &Token->DstMac);

+    if (EFI_ERROR (Status)) {

+      goto Error;

+    }

+

+    goto SendNow;

+  }

+

+  //

+  // If send to itself, directly send out

+  //

+  if (EFI_IP6_EQUAL (&Packet->Ip.Ip6->DestinationAddress, &Packet->Ip.Ip6->SourceAddress)) {

+    IP6_COPY_LINK_ADDRESS (&Token->DstMac, &IpSb->SnpMode.CurrentAddress);

+    goto SendNow;

+  }

+

+  //

+  // If unicast, check the neighbor state.

+  //

+

+  NeighborCache = Ip6FindNeighborEntry (IpSb, NextHop);

+  ASSERT (NeighborCache != NULL);

+

+  if (NeighborCache->Interface == NULL) {

+    NeighborCache->Interface = Interface;

+  }

+

+  switch (NeighborCache->State) {

+  case EfiNeighborStale:

+    NeighborCache->State = EfiNeighborDelay;

+    NeighborCache->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);

+    //

+    // Fall through

+    //

+  case EfiNeighborReachable:

+  case EfiNeighborDelay:

+  case EfiNeighborProbe:

+    IP6_COPY_LINK_ADDRESS (&Token->DstMac, &NeighborCache->LinkAddress);

+    goto SendNow;

+    break;

+

+  default:

+    break;

+  }

+

+  //

+  // Have to do asynchronous ARP resolution. First check whether there is

+  // already a pending request.

+  //

+  NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {

+    ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);

+    if (ArpQue == NeighborCache) {

+      InsertTailList (&NeighborCache->Frames, &Token->Link);

+      NeighborCache->ArpFree = TRUE;

+      return EFI_SUCCESS;

+    }

+  }

+

+  //

+  // First frame requires ARP.

+  //

+  InsertTailList (&NeighborCache->Frames, &Token->Link);

+  InsertTailList (&Interface->ArpQues, &NeighborCache->ArpList);

+

+  NeighborCache->ArpFree = TRUE;

+

+  return EFI_SUCCESS;

+

+SendNow:

+ //

+  // Insert the tx token into the SentFrames list before calling Mnp->Transmit.

+  // Remove it if the returned status is not EFI_SUCCESS.

+  //

+  InsertTailList (&Interface->SentFrames, &Token->Link);

+  Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);

+  if (EFI_ERROR (Status)) {

+    RemoveEntryList (&Token->Link);

+    goto Error;

+  }

+

+  return EFI_SUCCESS;

+

+Error:

+  Ip6FreeLinkTxToken (Token);

+  return Status;

+}

+

+/**

+  The heartbeat timer of IP6 service instance. It times out

+  all of its IP6 children's received-but-not-delivered and

+  transmitted-but-not-recycle packets.

+

+  @param[in]  Event                 The IP6 service instance's heartbeat timer.

+  @param[in]  Context               The IP6 service instance.

+

+**/

+VOID

+EFIAPI

+Ip6TimerTicking (

+  IN EFI_EVENT              Event,

+  IN VOID                   *Context

+  )

+{

+  IP6_SERVICE               *IpSb;

+

+  IpSb = (IP6_SERVICE *) Context;

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  Ip6PacketTimerTicking (IpSb);

+  Ip6NdTimerTicking (IpSb);

+  Ip6MldTimerTicking (IpSb);

+}

diff --git a/NetworkPkg/Ip6Dxe/Ip6If.h b/NetworkPkg/Ip6Dxe/Ip6If.h
new file mode 100644
index 0000000..736035e
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6If.h
@@ -0,0 +1,267 @@
+/** @file

+  Definition for IP6 pesudo interface structure.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_IF_H__

+#define __EFI_IP6_IF_H__

+

+#define IP6_LINK_RX_SIGNATURE   SIGNATURE_32 ('I', 'P', '6', 'R')

+#define IP6_LINK_TX_SIGNATURE   SIGNATURE_32 ('I', 'P', '6', 'T')

+#define IP6_INTERFACE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'I')

+#define IP6_ADDR_INFO_SIGNATURE SIGNATURE_32 ('I', 'P', 'A', 'I')

+

+//

+// This prototype is used by both receive and transmission.

+// When receiving Netbuf is allocated by IP6_INTERFACE, and

+// released by IP6. Flag shows whether the frame is received

+// as unicast/multicast/anycast...

+//

+// When transmitting, the Netbuf is from IP6, and provided

+// to the callback as a reference. Flag isn't used.

+//

+// IpInstance can be NULL which means that it is the IP6 driver

+// itself sending the packets. IP6 driver may send packets that

+// don't belong to any instance, such as ICMP errors, ICMP

+// informational packets. IpInstance is used as a tag in

+// this module.

+//

+typedef

+VOID

+(*IP6_FRAME_CALLBACK) (

+  NET_BUF                   *Packet,

+  EFI_STATUS                IoStatus,

+  UINT32                    LinkFlag,

+  VOID                      *Context

+  );

+

+//

+// Each receive request is wrapped in an IP6_LINK_RX_TOKEN.

+// Upon completion, the Callback will be called. Only one

+// receive request is send to MNP. IpInstance is always NULL.

+// Reference MNP's spec for information.

+//

+typedef struct {

+  UINT32                                Signature;

+  IP6_FRAME_CALLBACK                    CallBack;

+  VOID                                  *Context;

+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  MnpToken;

+} IP6_LINK_RX_TOKEN;

+

+//

+// Each transmit request is wrapped in an IP6_LINK_TX_TOKEN.

+// Upon completion, the Callback will be called.

+//

+typedef struct {

+  UINT32                                Signature;

+  LIST_ENTRY                            Link;

+

+  IP6_PROTOCOL                          *IpInstance;

+  IP6_FRAME_CALLBACK                    CallBack;

+  NET_BUF                               *Packet;

+  VOID                                  *Context;

+

+  EFI_MAC_ADDRESS                       DstMac;

+  EFI_MAC_ADDRESS                       SrcMac;

+

+  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  MnpToken;

+  EFI_MANAGED_NETWORK_TRANSMIT_DATA     MnpTxData;

+} IP6_LINK_TX_TOKEN;

+

+struct _IP6_ADDRESS_INFO {

+  UINT32                  Signature;

+  LIST_ENTRY              Link;

+  EFI_IPv6_ADDRESS        Address;

+  BOOLEAN                 IsAnycast;

+  UINT8                   PrefixLength;

+  UINT32                  ValidLifetime;

+  UINT32                  PreferredLifetime;

+};

+

+//

+// Callback to select which frame to cancel. Caller can cancel a

+// single frame, or all the frame from an IP instance.

+//

+typedef

+BOOLEAN

+(*IP6_FRAME_TO_CANCEL) (

+  IP6_LINK_TX_TOKEN       *Frame,

+  VOID                    *Context

+  );

+

+struct _IP6_INTERFACE {

+  UINT32                        Signature;

+  LIST_ENTRY                    Link;

+  INTN                          RefCnt;

+

+  //

+  // IP address and prefix length of the interface.  The fileds

+  // are invalid if (Configured == FALSE)

+  //

+  LIST_ENTRY                    AddressList;

+  UINT32                        AddressCount;

+  BOOLEAN                       Configured;

+

+  IP6_SERVICE                   *Service;

+

+  EFI_HANDLE                    Controller;

+  EFI_HANDLE                    Image;

+

+

+  //

+  // Queues to keep the frames sent and waiting ARP request.

+  //

+  LIST_ENTRY                    ArpQues;

+  LIST_ENTRY                    SentFrames;

+

+

+  //

+  // The interface's configuration variables

+  //

+  UINT32                        DupAddrDetect;

+  LIST_ENTRY                    DupAddrDetectList;

+  LIST_ENTRY                    DelayJoinList;

+

+  //

+  // All the IP instances that have the same IP/SubnetMask are linked

+  // together through IpInstances. If any of the instance enables

+  // promiscuous receive, PromiscRecv is true.

+  //

+  LIST_ENTRY                    IpInstances;

+  BOOLEAN                       PromiscRecv;

+};

+

+/**

+  Create an IP6_INTERFACE.

+

+  @param[in]  IpSb                  The IP6 service binding instance.

+  @param[in]  LinkLocal             If TRUE, the instance is created for link-local address.

+                                    Otherwise, it is not for a link-local address.

+

+  @return Point to the created IP6_INTERFACE, otherwise NULL.

+

+**/

+IP6_INTERFACE *

+Ip6CreateInterface (

+  IN IP6_SERVICE            *IpSb,

+  IN BOOLEAN                LinkLocal

+  );

+

+/**

+  Free the interface used by IpInstance. All the IP instance with

+  the same Ip/prefix pair share the same interface. It is reference

+  counted. All the frames that haven't been sent will be cancelled.

+  Because the IpInstance is optional, the caller must remove

+  IpInstance from the interface's instance list.

+

+  @param[in]  Interface         The interface used by the IpInstance.

+  @param[in]  IpInstance        The IP instance that free the interface. NULL if

+                                the IP driver is releasing the default interface.

+

+**/

+VOID

+Ip6CleanInterface (

+  IN  IP6_INTERFACE         *Interface,

+  IN  IP6_PROTOCOL          *IpInstance           OPTIONAL

+  );

+

+/**

+  Free the link layer transmit token. It will close the event

+  then free the memory used.

+

+  @param[in]  Token                 Token to free.

+

+**/

+VOID

+Ip6FreeLinkTxToken (

+  IN IP6_LINK_TX_TOKEN      *Token

+  );

+

+/**

+  Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK

+

+  @param  Event                 The receive event delivered to MNP for receive.

+  @param  Context               Context for the callback.

+

+**/

+VOID

+EFIAPI

+Ip6OnFrameReceived (

+  IN EFI_EVENT                Event,

+  IN VOID                     *Context

+  );

+

+/**

+  Request to receive the packet from the interface.

+

+  @param[in]  CallBack          Function to call when the receive finished.

+  @param[in]  IpSb              Points to the IP6 service binding instance.

+

+  @retval EFI_ALREADY_STARTED   There is already a pending receive request.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to receive.

+  @retval EFI_SUCCESS           The recieve request has been started.

+

+**/

+EFI_STATUS

+Ip6ReceiveFrame (

+  IN  IP6_FRAME_CALLBACK    CallBack,

+  IN  IP6_SERVICE           *IpSb

+  );

+

+/**

+  Send a frame from the interface. If the next hop is multicast address,

+  it is transmitted immediately. If the next hop is a unicast,

+  and the NextHop's MAC is not known, it will perform address resolution.

+  If some error happened, the CallBack won't be called. So, the caller

+  must test the return value, and take action when there is an error.

+

+  @param[in]  Interface         The interface to send the frame from

+  @param[in]  IpInstance        The IP child that request the transmission.

+                                NULL if it is the IP6 driver itself.

+  @param[in]  Packet            The packet to transmit.

+  @param[in]  NextHop           The immediate destination to transmit the packet to.

+  @param[in]  CallBack          Function to call back when transmit finished.

+  @param[in]  Context           Opaque parameter to the call back.

+

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to send the frame.

+  @retval EFI_NO_MAPPING        Can't resolve the MAC for the nexthop.

+  @retval EFI_SUCCESS           The packet successfully transmitted.

+

+**/

+EFI_STATUS

+Ip6SendFrame (

+  IN  IP6_INTERFACE         *Interface,

+  IN  IP6_PROTOCOL          *IpInstance      OPTIONAL,

+  IN  NET_BUF               *Packet,

+  IN  EFI_IPv6_ADDRESS      *NextHop,

+  IN  IP6_FRAME_CALLBACK    CallBack,

+  IN  VOID                  *Context

+  );

+

+/**

+  The heartbeat timer of IP6 service instance. It times out

+  all of its IP6 children's received-but-not-delivered and

+  transmitted-but-not-recycle packets.

+

+  @param[in]  Event                 The IP6 service instance's heart beat timer.

+  @param[in]  Context               The IP6 service instance.

+

+**/

+VOID

+EFIAPI

+Ip6TimerTicking (

+  IN EFI_EVENT              Event,

+  IN VOID                   *Context

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6Impl.c b/NetworkPkg/Ip6Dxe/Ip6Impl.c
new file mode 100644
index 0000000..9b34ece
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Impl.c
@@ -0,0 +1,1857 @@
+/** @file

+  Implementation of EFI_IP6_PROTOCOL protocol interfaces.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+EFI_IPSEC_PROTOCOL    *mIpSec = NULL;

+

+EFI_IP6_PROTOCOL mEfiIp6ProtocolTemplete = {

+  EfiIp6GetModeData,

+  EfiIp6Configure,

+  EfiIp6Groups,

+  EfiIp6Routes,

+  EfiIp6Neighbors,

+  EfiIp6Transmit,

+  EfiIp6Receive,

+  EfiIp6Cancel,

+  EfiIp6Poll

+};

+

+/**

+  Gets the current operational settings for this instance of the EFI IPv6 Protocol driver.

+

+  The GetModeData() function returns the current operational mode data for this driver instance.

+  The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to

+  retrieve the operational mode data of underlying networks or drivers.

+

+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.

+  @param[out] Ip6ModeData        Pointer to the EFI IPv6 Protocol mode data structure.

+  @param[out] MnpConfigData      Pointer to the managed network configuration data structure.

+  @param[out] SnpModeData        Pointer to the simple network mode data structure.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_OUT_OF_RESOURCES   The required mode data could not be allocated.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6GetModeData (

+  IN EFI_IP6_PROTOCOL                 *This,

+  OUT EFI_IP6_MODE_DATA               *Ip6ModeData     OPTIONAL,

+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData   OPTIONAL,

+  OUT EFI_SIMPLE_NETWORK_MODE         *SnpModeData     OPTIONAL

+  )

+{

+  IP6_PROTOCOL              *IpInstance;

+  IP6_SERVICE               *IpSb;

+  IP6_INTERFACE             *IpIf;

+  EFI_IP6_CONFIG_DATA       *Config;

+  EFI_STATUS                Status;

+  EFI_TPL                   OldTpl;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  OldTpl     = gBS->RaiseTPL (TPL_CALLBACK);

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);

+  IpSb       = IpInstance->Service;

+  IpIf       = IpInstance->Interface;

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Ip6ModeData != NULL) {

+    //

+    // IsStarted is "whether the EfiIp6Configure has been called".

+    // IsConfigured is "whether the station address has been configured"

+    //

+    Ip6ModeData->IsStarted     = (BOOLEAN) (IpInstance->State == IP6_STATE_CONFIGED);

+    Ip6ModeData->MaxPacketSize = IpSb->MaxPacketSize;

+    CopyMem (&Ip6ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP6_CONFIG_DATA));

+    Ip6ModeData->IsConfigured  = FALSE;

+

+    Ip6ModeData->AddressCount  = 0;

+    Ip6ModeData->AddressList   = NULL;

+

+    Ip6ModeData->GroupCount    = IpInstance->GroupCount;

+    Ip6ModeData->GroupTable    = NULL;

+

+    Ip6ModeData->RouteCount    = 0;

+    Ip6ModeData->RouteTable    = NULL;

+

+    Ip6ModeData->NeighborCount = 0;

+    Ip6ModeData->NeighborCache = NULL;

+

+    Ip6ModeData->PrefixCount   = 0;

+    Ip6ModeData->PrefixTable   = NULL;

+

+    Ip6ModeData->IcmpTypeCount = 23;

+    Ip6ModeData->IcmpTypeList  = AllocateCopyPool (

+                                   Ip6ModeData->IcmpTypeCount * sizeof (EFI_IP6_ICMP_TYPE),

+                                   mIp6SupportedIcmp

+                                   );

+    if (Ip6ModeData->IcmpTypeList == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto Error;

+    }

+

+    //

+    // Return the currently configured IPv6 addresses and corresponding prefix lengths.

+    //

+    Status = Ip6BuildEfiAddressList (

+               IpSb,

+               &Ip6ModeData->AddressCount,

+               &Ip6ModeData->AddressList

+               );

+    if (EFI_ERROR (Status)) {

+      goto Error;

+    }

+

+    //

+    // Return the current station address for this IP child.

+    // If UseAnyStationAddress is set to TRUE, IP6 driver will

+    // select a source address from its address list. Otherwise use the

+    // StationAddress in config data.

+    //

+    if (Ip6ModeData->IsStarted) {

+      Config = &Ip6ModeData->ConfigData;

+

+      if (IpIf->Configured || NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {

+        Ip6ModeData->IsConfigured = TRUE;

+      } else {

+        Ip6ModeData->IsConfigured = FALSE;

+      }

+

+      //

+      // Build a EFI route table for user from the internal route table.

+      //

+      Status = Ip6BuildEfiRouteTable (

+                 IpSb->RouteTable,

+                 &Ip6ModeData->RouteCount,

+                 &Ip6ModeData->RouteTable

+                 );

+

+      if (EFI_ERROR (Status)) {

+        goto Error;

+      }

+    }

+

+    if (Ip6ModeData->IsConfigured) {

+      //

+      // Return the joined multicast group addresses.

+      //

+      if (IpInstance->GroupCount != 0) {

+        Ip6ModeData->GroupTable = AllocateCopyPool (

+                                    IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS),

+                                    IpInstance->GroupList

+                                    );

+        if (Ip6ModeData->GroupTable == NULL) {

+          Status = EFI_OUT_OF_RESOURCES;

+          goto Error;

+        }

+      }

+      //

+      // Return the neighbor cache entries

+      //

+      Status = Ip6BuildEfiNeighborCache (

+                 IpInstance,

+                 &Ip6ModeData->NeighborCount,

+                 &Ip6ModeData->NeighborCache

+                 );

+      if (EFI_ERROR (Status)) {

+        goto Error;

+      }

+

+      //

+      // Return the prefix table entries

+      //

+      Status = Ip6BuildPrefixTable (

+                 IpInstance,

+                 &Ip6ModeData->PrefixCount,

+                 &Ip6ModeData->PrefixTable

+                 );

+      if (EFI_ERROR (Status)) {

+        goto Error;

+      }

+

+    }

+  }

+

+  //

+  // Get fresh mode data from MNP, since underlying media status may change

+  //

+  Status = IpSb->Mnp->GetModeData (IpSb->Mnp, MnpConfigData, SnpModeData);

+

+  goto Exit;

+

+Error:

+  if (Ip6ModeData != NULL) {

+    if (Ip6ModeData->AddressList != NULL) {

+      FreePool (Ip6ModeData->AddressList);

+    }

+

+    if (Ip6ModeData->GroupTable != NULL) {

+      FreePool (Ip6ModeData->GroupTable);

+    }

+

+    if (Ip6ModeData->RouteTable != NULL) {

+      FreePool (Ip6ModeData->RouteTable);

+    }

+

+    if (Ip6ModeData->NeighborCache != NULL) {

+      FreePool (Ip6ModeData->NeighborCache);

+    }

+

+    if (Ip6ModeData->PrefixTable != NULL) {

+      FreePool (Ip6ModeData->PrefixTable);

+    }

+

+    if (Ip6ModeData->IcmpTypeList != NULL) {

+      FreePool (Ip6ModeData->IcmpTypeList);

+    }

+  }

+

+Exit:

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+/**

+  Validate that Ipv6 address is OK to be used as station address or next hop address/ neighbor.

+

+  @param[in]  IpSb               The IP6 service instance.

+  @param[in]  Ip                 The IPv6 address to validate.

+  @param[in]  Flag               If TRUE, validate if the address is OK to be used

+                                 as station address. If FALSE, validate if the

+                                 address is OK to be used as the next hop address/

+                                 neighbor.

+

+  @retval TRUE                   The Ip address is valid and could be used.

+  @retval FALSE                  Invalid Ip address.

+

+**/

+BOOLEAN

+Ip6IsValidAddress (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Ip,

+  IN BOOLEAN                Flag

+  )

+{

+  if (!NetIp6IsUnspecifiedAddr (Ip)) {

+    if (!NetIp6IsValidUnicast(Ip)) {

+      return FALSE;

+    }

+    if (Ip6IsOneOfSetAddress (IpSb, Ip, NULL, NULL)) {

+      return Flag;

+    }

+  } else {

+    return Flag;

+  }

+

+  return (BOOLEAN) !Flag;

+}

+

+/**

+  Validate whether the value of protocol is illegal or not. Protocol is the 'Next Header' field

+  in the last IPv6 extension header, or basic IPv6 header is there's no extension header.

+

+  @param[in]  Protocol           Default value of 'Next Header'

+

+  @retval TRUE                   The protocol is illegal.

+  @retval FALSE                  The protocol is legal.

+

+**/

+BOOLEAN

+Ip6IsIllegalProtocol (

+  IN UINT8                  Protocol

+  )

+{

+  if (Protocol == IP6_HOP_BY_HOP || Protocol == EFI_IP_PROTO_ICMP || Protocol == IP4_PROTO_IGMP) {

+    return TRUE;

+  }

+

+  if (Protocol == 41 || Protocol == 43 || Protocol == 44 || Protocol == 59 || Protocol == 60 || Protocol == 124) {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Intiialize the IP6_PROTOCOL structure to the unconfigured states.

+

+  @param[in]       IpSb                   The IP6 service instance.

+  @param[in, out]  IpInstance             The IP6 child instance.

+

+**/

+VOID

+Ip6InitProtocol (

+  IN IP6_SERVICE            *IpSb,

+  IN OUT IP6_PROTOCOL       *IpInstance

+  )

+{

+  ASSERT ((IpSb != NULL) && (IpInstance != NULL));

+

+  ZeroMem (IpInstance, sizeof (IP6_PROTOCOL));

+

+  IpInstance->Signature = IP6_PROTOCOL_SIGNATURE;

+  IpInstance->State     = IP6_STATE_UNCONFIGED;

+  IpInstance->Service   = IpSb;

+  IpInstance->GroupList = NULL;

+  CopyMem (&IpInstance->Ip6Proto, &mEfiIp6ProtocolTemplete, sizeof (EFI_IP6_PROTOCOL));

+

+  NetMapInit  (&IpInstance->RxTokens);

+  NetMapInit  (&IpInstance->TxTokens);

+  InitializeListHead (&IpInstance->Received);

+  InitializeListHead (&IpInstance->Delivered);

+

+  EfiInitializeLock (&IpInstance->RecycleLock, TPL_NOTIFY);

+}

+

+/**

+  Configure the IP6 child. If the child is already configured,

+  change the configuration parameter. Otherwise, configure it

+  for the first time. The caller should validate the configuration

+  before deliver them to it. It also don't do configure NULL.

+

+  @param[in, out]  IpInstance         The IP6 child to configure.

+  @param[in]       Config             The configure data.

+

+  @retval EFI_SUCCESS            The IP6 child is successfully configured.

+  @retval EFI_DEVICE_ERROR       Failed to free the pending transive or to

+                                 configure  underlying MNP, or other errors.

+  @retval EFI_NO_MAPPING         The IP6 child is configured to use the default

+                                 address, but the default address hasn't been

+                                 configured. The IP6 child doesn't need to be

+                                 reconfigured when the default address is configured.

+  @retval EFI_OUT_OF_RESOURCES   No more memory space is available.

+  @retval other                  Other error occurs.

+

+**/

+EFI_STATUS

+Ip6ConfigProtocol (

+  IN OUT IP6_PROTOCOL        *IpInstance,

+  IN     EFI_IP6_CONFIG_DATA *Config

+  )

+{

+  IP6_SERVICE               *IpSb;

+  IP6_INTERFACE             *IpIf;

+  EFI_STATUS                Status;

+  EFI_IP6_CONFIG_DATA       *Current;

+  IP6_ADDRESS_INFO          *AddressInfo;

+  BOOLEAN                   StationZero;

+  BOOLEAN                   DestZero;

+  EFI_IPv6_ADDRESS          Source;

+  BOOLEAN                   AddrOk;

+

+  IpSb    = IpInstance->Service;

+  Current = &IpInstance->ConfigData;

+

+  //

+  // User is changing packet filters. It must be stopped

+  // before the station address can be changed.

+  //

+  if (IpInstance->State == IP6_STATE_CONFIGED) {

+    //

+    // Cancel all the pending transmit/receive from upper layer

+    //

+    Status = Ip6Cancel (IpInstance, NULL);

+

+    if (EFI_ERROR (Status)) {

+      return EFI_DEVICE_ERROR;

+    }

+

+    CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Set up the interface.

+  //

+  StationZero = NetIp6IsUnspecifiedAddr (&Config->StationAddress);

+  DestZero    = NetIp6IsUnspecifiedAddr (&Config->DestinationAddress);

+

+  if (StationZero && DestZero) {

+    //

+    // StationAddress is still zero.

+    //

+

+    NET_GET_REF (IpSb->DefaultInterface);

+    IpInstance->Interface = IpSb->DefaultInterface;

+    InsertTailList (&IpSb->DefaultInterface->IpInstances, &IpInstance->AddrLink);

+

+    CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));

+    IpInstance->State = IP6_STATE_CONFIGED;

+

+    return EFI_SUCCESS;

+  }

+

+  if (StationZero && !DestZero) {

+    Status = Ip6SelectSourceAddress (IpSb, &Config->DestinationAddress, &Source);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+  } else {

+    IP6_COPY_ADDRESS (&Source, &Config->StationAddress);

+  }

+

+  AddrOk = Ip6IsOneOfSetAddress (IpSb, &Source, &IpIf, &AddressInfo);

+  if (AddrOk) {

+    if (AddressInfo != NULL) {

+      IpInstance->PrefixLength = AddressInfo->PrefixLength;

+    } else {

+      IpInstance->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;

+    }

+  } else {

+    //

+    // The specified source address is not one of the addresses IPv6 maintains.

+    //

+    return EFI_INVALID_PARAMETER;

+  }

+

+

+  NET_GET_REF (IpIf);

+  IpInstance->Interface = IpIf;

+  InsertTailList (&IpIf->IpInstances, &IpInstance->AddrLink);

+

+  CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));

+  IP6_COPY_ADDRESS (&Current->StationAddress, &Source);

+  IpInstance->State = IP6_STATE_CONFIGED;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Clean up the IP6 child, and release all the resources used by it.

+

+  @param[in, out]  IpInstance    The IP6 child to clean up.

+

+  @retval EFI_SUCCESS            The IP6 child is cleaned up.

+  @retval EFI_DEVICE_ERROR       Some resources failed to be released.

+

+**/

+EFI_STATUS

+Ip6CleanProtocol (

+  IN OUT IP6_PROTOCOL            *IpInstance

+  )

+{

+  if (EFI_ERROR (Ip6Cancel (IpInstance, NULL))) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  if (EFI_ERROR (Ip6Groups (IpInstance, FALSE, NULL))) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // Some packets haven't been recycled. It is because either the

+  // user forgets to recycle the packets, or because the callback

+  // hasn't been called. Just leave it alone.

+  //

+  if (!IsListEmpty (&IpInstance->Delivered)) {

+    ;

+  }

+

+  if (IpInstance->Interface != NULL) {

+    RemoveEntryList (&IpInstance->AddrLink);

+    Ip6CleanInterface (IpInstance->Interface, IpInstance);

+    IpInstance->Interface = NULL;

+  }

+

+  if (IpInstance->GroupList != NULL) {

+    FreePool (IpInstance->GroupList);

+    IpInstance->GroupList   = NULL;

+    IpInstance->GroupCount  = 0;

+  }

+

+  NetMapClean (&IpInstance->TxTokens);

+

+  NetMapClean (&IpInstance->RxTokens);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Configure the MNP parameter used by IP. The IP driver uses one MNP

+  child to transmit/receive frames. By default, it configures MNP

+  to receive unicast/multicast/broadcast. Also, it will enable/disable

+  the promiscuous receive according to whether there is IP child

+  enable that or not. If Force is FALSE, it will iterate through

+  all the IP children to check whether the promiscuous receive

+  setting has been changed. If it hasn't been changed, it won't

+  reconfigure the MNP. If Force is TRUE, the MNP is configured

+  whether that is changed or not.

+

+  @param[in]  IpSb               The IP6 service instance that is to be changed.

+  @param[in]  Force              Force the configuration or not.

+

+  @retval EFI_SUCCESS            The MNP successfully configured/reconfigured.

+  @retval Others                 Configuration failed.

+

+**/

+EFI_STATUS

+Ip6ServiceConfigMnp (

+  IN IP6_SERVICE            *IpSb,

+  IN BOOLEAN                Force

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *ProtoEntry;

+  IP6_INTERFACE             *IpIf;

+  IP6_PROTOCOL              *IpInstance;

+  BOOLEAN                   Reconfig;

+  BOOLEAN                   PromiscReceive;

+  EFI_STATUS                Status;

+

+  Reconfig       = FALSE;

+  PromiscReceive = FALSE;

+

+  if (!Force) {

+    //

+    // Iterate through the IP children to check whether promiscuous

+    // receive setting has been changed. Update the interface's receive

+    // filter also.

+    //

+    NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+

+      IpIf              = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);

+      IpIf->PromiscRecv = FALSE;

+

+      NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) {

+        IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP6_PROTOCOL, AddrLink);

+

+        if (IpInstance->ConfigData.AcceptPromiscuous) {

+          IpIf->PromiscRecv = TRUE;

+          PromiscReceive    = TRUE;

+        }

+      }

+    }

+

+    //

+    // If promiscuous receive isn't changed, it isn't necessary to reconfigure.

+    //

+    if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) {

+      return EFI_SUCCESS;

+    }

+

+    Reconfig  = TRUE;

+    IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive;

+  }

+

+  Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData);

+

+  //

+  // recover the original configuration if failed to set the configure.

+  //

+  if (EFI_ERROR (Status) && Reconfig) {

+    IpSb->MnpConfigData.EnablePromiscuousReceive = (BOOLEAN) !PromiscReceive;

+  }

+

+  return Status;

+}

+

+/**

+  Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance.

+

+  The Configure() function is used to set, change, or reset the operational parameters and filter

+  settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic

+  can be sent or received by this instance. Once the parameters have been reset (by calling this

+  function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these

+  parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped

+  independently of each other by enabling or disabling their receive filter settings with the

+  Configure() function.

+

+  If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required

+  to be one of the currently configured IPv6 addresses listed in the EFI IPv6 drivers, or else

+  EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is

+  unspecified, the IPv6 driver will bind a source address according to the source address selection

+  algorithm. Clients could frequently call GetModeData() to check get currently configured IPv6

+  address list in the EFI IPv6 driver. If both Ip6ConfigData.StationAddress and

+  Ip6ConfigData.Destination are unspecified, when transmitting the packet afterwards, the

+  source address filled in each outgoing IPv6 packet is decided based on the destination of this packet.

+

+  If operational parameters are reset or changed, any pending transmit and receive requests will be

+  cancelled. Their completion token status will be set to EFI_ABORTED and their events will be

+  signaled.

+

+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  Ip6ConfigData      Pointer to the EFI IPv6 Protocol configuration data structure.

+                                 If NULL, reset the configuration data.

+

+  @retval EFI_SUCCESS            The driver instance was successfully opened.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Ip6ConfigData.StationAddress is neither zero nor

+                                   a unicast IPv6 address.

+                                 - Ip6ConfigData.StationAddress is neither zero nor

+                                   one of the configured IP addresses in the EFI IPv6 driver.

+                                 - Ip6ConfigData.DefaultProtocol is illegal.

+  @retval EFI_OUT_OF_RESOURCES   The EFI IPv6 Protocol driver instance data could not be allocated.

+  @retval EFI_NO_MAPPING         The IPv6 driver was responsible for choosing a source address for

+                                 this instance, but no source address was available for use.

+  @retval EFI_ALREADY_STARTED    The interface is already open and must be stopped before the IPv6

+                                 address or prefix length can be changed.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred. The EFI IPv6

+                                 Protocol driver instance was not opened.

+  @retval EFI_UNSUPPORTED        Default protocol specified through

+                                 Ip6ConfigData.DefaulProtocol isn't supported.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Configure (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN EFI_IP6_CONFIG_DATA       *Ip6ConfigData OPTIONAL

+  )

+{

+  IP6_PROTOCOL              *IpInstance;

+  EFI_IP6_CONFIG_DATA       *Current;

+  EFI_TPL                   OldTpl;

+  EFI_STATUS                Status;

+  IP6_SERVICE               *IpSb;

+

+  //

+  // First, validate the parameters

+  //

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);

+  IpSb       = IpInstance->Service;

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  OldTpl     = gBS->RaiseTPL (TPL_CALLBACK);

+

+  Status     = EFI_INVALID_PARAMETER;

+

+  //

+  // Validate the configuration first.

+  //

+  if (Ip6ConfigData != NULL) {

+    //

+    // Check whether the station address is valid.

+    //

+    if (!Ip6IsValidAddress (IpSb, &Ip6ConfigData->StationAddress, TRUE)) {

+       goto Exit;

+    }

+    //

+    // Check whether the default protocol is valid.

+    //

+    if (Ip6IsIllegalProtocol (Ip6ConfigData->DefaultProtocol)) {

+      goto Exit;

+    }

+

+    //

+    // User can only update packet filters when already configured.

+    // If it wants to change the station address, it must configure(NULL)

+    // the instance firstly.

+    //

+    if (IpInstance->State == IP6_STATE_CONFIGED) {

+      Current = &IpInstance->ConfigData;

+

+      if (!EFI_IP6_EQUAL (&Current->StationAddress, &Ip6ConfigData->StationAddress)) {

+        Status = EFI_ALREADY_STARTED;

+        goto Exit;

+      }

+

+      if (NetIp6IsUnspecifiedAddr (&Current->StationAddress) && IP6_NO_MAPPING (IpInstance)) {

+        Status = EFI_NO_MAPPING;

+        goto Exit;

+      }

+    }

+  }

+

+  //

+  // Configure the instance or clean it up.

+  //

+  if (Ip6ConfigData != NULL) {

+    Status = Ip6ConfigProtocol (IpInstance, Ip6ConfigData);

+  } else {

+    Status = Ip6CleanProtocol (IpInstance);

+

+    //

+    // Don't change the state if it is DESTORY, consider the following

+    // valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped,

+    // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED,

+    // the unload fails miserably.

+    //

+    if (IpInstance->State == IP6_STATE_CONFIGED) {

+      IpInstance->State = IP6_STATE_UNCONFIGED;

+    }

+  }

+

+  //

+  // Update the MNP's configure data. Ip6ServiceConfigMnp will check

+  // whether it is necessary to reconfigure the MNP.

+  //

+  Ip6ServiceConfigMnp (IpInstance->Service, FALSE);

+

+  //

+  // Update the variable data.

+  //

+  Ip6SetVariableData (IpInstance->Service);

+

+Exit:

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+/**

+  Joins and leaves multicast groups.

+

+  The Groups() function is used to join and leave multicast group sessions. Joining a group will

+  enable reception of matching multicast packets. Leaving a group will disable reception of matching

+  multicast packets. Source-Specific Multicast isn't required to be supported.

+

+  If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.

+

+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  JoinFlag           Set to TRUE to join the multicast group session, and FALSE to leave.

+  @param[in]  GroupAddress       Pointer to the IPv6 multicast address.

+                                 This is an optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER  One or more of the following is TRUE:

+                                 - This is NULL.

+                                 - JoinFlag is TRUE and GroupAddress is NULL.

+                                 - GroupAddress is not NULL and *GroupAddress is

+                                   not a multicast IPv6 address.

+                                 - GroupAddress is not NULL and *GroupAddress is in the

+                                   range of SSM destination address.

+  @retval EFI_NOT_STARTED        This instance has not been started.

+  @retval EFI_OUT_OF_RESOURCES   System resources could not be allocated.

+  @retval EFI_UNSUPPORTED        This EFI IPv6 Protocol implementation does not support multicast groups.

+  @retval EFI_ALREADY_STARTED    The group address is already in the group table (when

+                                 JoinFlag is TRUE).

+  @retval EFI_NOT_FOUND          The group address is not in the group table (when JoinFlag is FALSE).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Groups (

+  IN EFI_IP6_PROTOCOL  *This,

+  IN BOOLEAN           JoinFlag,

+  IN EFI_IPv6_ADDRESS  *GroupAddress  OPTIONAL

+  )

+{

+  EFI_TPL                   OldTpl;

+  EFI_STATUS                Status;

+  IP6_PROTOCOL              *IpInstance;

+  IP6_SERVICE               *IpSb;

+

+  if ((This == NULL) || (JoinFlag && GroupAddress == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (GroupAddress != NULL && !IP6_IS_MULTICAST (GroupAddress)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);

+  IpSb       = IpInstance->Service;

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  OldTpl     = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (IpInstance->State != IP6_STATE_CONFIGED) {

+    Status = EFI_NOT_STARTED;

+    goto ON_EXIT;

+  }

+

+  Status = Ip6Groups (IpInstance, JoinFlag, GroupAddress);

+

+ON_EXIT:

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+/**

+  Adds and deletes routing table entries.

+

+  The Routes() function adds a route to, or deletes a route from, the routing table.

+

+  Routes are determined by comparing the leftmost PrefixLength bits of Destination with

+  the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the

+  configured station address.

+

+  The default route is added with Destination and PrefixLegth both set to all zeros. The

+  default route matches all destination IPv6 addresses that do not match any other routes.

+

+  All EFI IPv6 Protocol instances share a routing table.

+

+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  DeleteRoute        Set to TRUE to delete this route from the routing table. Set to

+                                 FALSE to add this route to the routing table. Destination,

+                                 PrefixLength and Gateway are used as the key to each

+                                 route entry.

+  @param[in]  Destination        The address prefix of the subnet that needs to be routed.

+                                 This is an optional parameter that may be NULL.

+  @param[in]  PrefixLength       The prefix length of Destination. Ignored if Destination

+                                 is NULL.

+  @param[in]  GatewayAddress     The unicast gateway IPv6 address for this route.

+                                 This is an optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_NOT_STARTED        The driver instance has not been started.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - When DeleteRoute is TRUE, both Destination and

+                                   GatewayAddress are NULL.

+                                 - When DeleteRoute is FALSE, either Destination or

+                                   GatewayAddress is NULL.

+                                 - *GatewayAddress is not a valid unicast IPv6 address.

+                                 - *GatewayAddress is one of the local configured IPv6

+                                   addresses.

+  @retval EFI_OUT_OF_RESOURCES   Could not add the entry to the routing table.

+  @retval EFI_NOT_FOUND          This route is not in the routing table (when DeleteRoute is TRUE).

+  @retval EFI_ACCESS_DENIED      The route is already defined in the routing table (when

+                                 DeleteRoute is FALSE).

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Routes (

+  IN EFI_IP6_PROTOCOL    *This,

+  IN BOOLEAN             DeleteRoute,

+  IN EFI_IPv6_ADDRESS    *Destination    OPTIONAL,

+  IN UINT8               PrefixLength,

+  IN EFI_IPv6_ADDRESS    *GatewayAddress OPTIONAL

+  )

+{

+  IP6_PROTOCOL              *IpInstance;

+  EFI_STATUS                Status;

+  EFI_TPL                   OldTpl;

+  IP6_SERVICE               *IpSb;

+

+  if ((This == NULL) || (PrefixLength >= IP6_PREFIX_NUM)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);

+  IpSb       = IpInstance->Service;

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  if (IpInstance->State != IP6_STATE_CONFIGED) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (DeleteRoute && (Destination == NULL) && (GatewayAddress == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (!DeleteRoute && (Destination == NULL || GatewayAddress == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (GatewayAddress != NULL) {

+    if (!Ip6IsValidAddress (IpSb, GatewayAddress, FALSE)) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (!NetIp6IsUnspecifiedAddr (GatewayAddress) &&

+        !NetIp6IsNetEqual (GatewayAddress, &IpInstance->ConfigData.StationAddress, PrefixLength)

+          ) {

+      return EFI_INVALID_PARAMETER;

+    }

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // Update the route table

+  //

+  if (DeleteRoute) {

+    Status = Ip6DelRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress);

+  } else {

+    Status = Ip6AddRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress);

+  }

+

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+/**

+  Add or delete Neighbor cache entries.

+

+  The Neighbors() function is used to add, update, or delete an entry from neighbor cache.

+  IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as

+  network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network

+  traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not

+  timeout) or dynamic (will timeout).

+

+  The implementation should follow the neighbor cache timeout mechanism which is defined in

+  RFC4861. The default neighbor cache timeout value should be tuned for the expected network

+  environment

+

+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  DeleteFlag         Set to TRUE to delete the specified cache entry, set to FALSE to

+                                 add (or update, if it already exists and Override is TRUE) the

+                                 specified cache entry. TargetIp6Address is used as the key

+                                 to find the requested cache entry.

+  @param[in]  TargetIp6Address   Pointer to the Target IPv6 address.

+  @param[in]  TargetLinkAddress  Pointer to the link-layer address of the target. Ignored if NULL.

+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor

+                                 cache, it will be deleted after Timeout. A value of zero means that

+                                 the entry is permanent. A non-zero value means that the entry is

+                                 dynamic.

+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will

+                                 be overridden and updated; if FALSE, EFI_ACCESS_DENIED

+                                 will be returned if a corresponding cache entry already existed.

+

+  @retval  EFI_SUCCESS           The data has been queued for transmission.

+  @retval  EFI_NOT_STARTED       This instance has not been started.

+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - TargetIpAddress is NULL.

+                                 - *TargetLinkAddress is invalid when not NULL.

+                                 - *TargetIpAddress is not a valid unicast IPv6 address.

+                                 - *TargetIpAddress is one of the local configured IPv6

+                                   addresses.

+  @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache.

+  @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache (when DeleteFlag  is

+                                 TRUE or when DeleteFlag  is FALSE while

+                                 TargetLinkAddress is NULL.).

+  @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,

+                                 and that entry is tagged as un-overridden (when Override

+                                 is FALSE).

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Neighbors (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN BOOLEAN                   DeleteFlag,

+  IN EFI_IPv6_ADDRESS          *TargetIp6Address,

+  IN EFI_MAC_ADDRESS           *TargetLinkAddress OPTIONAL,

+  IN UINT32                    Timeout,

+  IN BOOLEAN                   Override

+  )

+{

+  EFI_TPL                   OldTpl;

+  EFI_STATUS                Status;

+  IP6_PROTOCOL              *IpInstance;

+  IP6_SERVICE               *IpSb;

+

+  if (This == NULL || TargetIp6Address == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (NetIp6IsUnspecifiedAddr (TargetIp6Address)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);

+  IpSb       = IpInstance->Service;

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  if (!Ip6IsValidAddress (IpSb, TargetIp6Address, FALSE)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (TargetLinkAddress != NULL) {

+    if (!Ip6IsValidLinkAddress (IpSb, TargetLinkAddress)) {

+      return EFI_INVALID_PARAMETER;

+    }

+  }

+

+  if (Ip6IsOneOfSetAddress (IpSb, TargetIp6Address, NULL, NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+  if (IpInstance->State != IP6_STATE_CONFIGED) {

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  if (DeleteFlag) {

+    Status = Ip6DelNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override);

+  } else {

+    Status = Ip6AddNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override);

+  }

+

+Exit:

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+/**

+  Check whether the user's token or event has already

+  been enqueue on IP6's list.

+

+  @param[in]  Map                The container of either user's transmit or receive

+                                 token.

+  @param[in]  Item               Current item to check against.

+  @param[in]  Context            The Token to check againist.

+

+  @retval EFI_ACCESS_DENIED      The token or event has already been enqueued in IP

+  @retval EFI_SUCCESS            The current item isn't the same token/event as the

+                                 context.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6TokenExist (

+  IN NET_MAP                *Map,

+  IN NET_MAP_ITEM           *Item,

+  IN VOID                   *Context

+  )

+{

+  EFI_IP6_COMPLETION_TOKEN  *Token;

+  EFI_IP6_COMPLETION_TOKEN  *TokenInItem;

+

+  Token       = (EFI_IP6_COMPLETION_TOKEN *) Context;

+  TokenInItem = (EFI_IP6_COMPLETION_TOKEN *) Item->Key;

+

+  if (Token == TokenInItem || Token->Event == TokenInItem->Event) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Validate the user's token against the current station address.

+

+  @param[in]  Token              User's token to validate.

+

+  @retval EFI_INVALID_PARAMETER  Some parameters are invalid.

+  @retval EFI_BAD_BUFFER_SIZE    The user's option/data is too long.

+  @retval EFI_SUCCESS            The token is OK.

+

+**/

+EFI_STATUS

+Ip6TxTokenValid (

+  IN EFI_IP6_COMPLETION_TOKEN   *Token

+  )

+{

+  EFI_IP6_TRANSMIT_DATA     *TxData;

+  UINT32                    Index;

+  UINT32                    DataLength;

+

+  if (Token == NULL || Token->Event == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  TxData = Token->Packet.TxData;

+

+  if (TxData == NULL || (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (TxData->FragmentCount == 0 || TxData->DataLength == 0) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  for (DataLength = 0, Index = 0; Index < TxData->FragmentCount; Index++) {

+    if (TxData->FragmentTable[Index].FragmentLength == 0 || TxData->FragmentTable[Index].FragmentBuffer == NULL) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    DataLength += TxData->FragmentTable[Index].FragmentLength;

+  }

+

+  if (TxData->DataLength != DataLength) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // TODO: Token.Packet.TxData.DataLength is too short to transmit.

+  // return EFI_BUFFER_TOO_SMALL;

+  //

+

+  //

+  // If Token.Packet.TxData.DataLength is beyond the maximum that which can be

+  // described through the Fragment Offset field in Fragment header when performing

+  // fragmentation.

+  //

+  if (TxData->DataLength > 64 * 1024) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  The callback function for the net buffer which wraps the user's

+  transmit token. Although  this function seems simple, there

+  are some subtle aspects.

+  When user requests the IP to transmit a packet by passing it a

+  token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data

+  is wrapped in an net buffer. The net buffer's Free function is

+  set to Ip6FreeTxToken. The Token and token wrap are added to the

+  IP child's TxToken map. Then the buffer is passed to Ip6Output for

+  transmission. If an error happened before that, the buffer

+  is freed, which in turn frees the token wrap. The wrap may

+  have been added to the TxToken map or not, and the user's event

+  shouldn't be fired because we are still in the EfiIp6Transmit. If

+  the buffer has been sent by Ip6Output, it should be removed from

+  the TxToken map and user's event signaled. The token wrap and buffer

+  are bound together. Check the comments in Ip6Output for information

+  about IP fragmentation.

+

+  @param[in]  Context                The token's wrap.

+

+**/

+VOID

+EFIAPI

+Ip6FreeTxToken (

+  IN VOID                   *Context

+  )

+{

+  IP6_TXTOKEN_WRAP          *Wrap;

+  NET_MAP_ITEM              *Item;

+

+  Wrap = (IP6_TXTOKEN_WRAP *) Context;

+

+  //

+  // Signal IpSecRecycleEvent to inform IPsec free the memory

+  //

+  if (Wrap->IpSecRecycleSignal != NULL) {

+    gBS->SignalEvent (Wrap->IpSecRecycleSignal);

+  }

+

+  //

+  // Find the token in the instance's map. EfiIp6Transmit put the

+  // token to the map. If that failed, NetMapFindKey will return NULL.

+  //

+  Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token);

+

+  if (Item != NULL) {

+    NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL);

+  }

+

+  if (Wrap->Sent) {

+    gBS->SignalEvent (Wrap->Token->Event);

+

+    //

+    // Dispatch the DPC queued by the NotifyFunction of Token->Event.

+    //

+    DispatchDpc ();

+  }

+

+  FreePool (Wrap);

+}

+

+

+/**

+  The callback function to Ip6Output to update the transmit status.

+

+  @param[in]  Packet           The user's transmit packet.

+  @param[in]  IoStatus         The result of the transmission.

+  @param[in]  Flag             Not used during transmission.

+  @param[in]  Context          The token's wrap.

+

+**/

+VOID

+Ip6OnPacketSent (

+  IN NET_BUF                   *Packet,

+  IN EFI_STATUS                IoStatus,

+  IN UINT32                    Flag,

+  IN VOID                      *Context

+  )

+{

+  IP6_TXTOKEN_WRAP             *Wrap;

+

+  //

+  // This is the transmission request from upper layer,

+  // not the IP6 driver itself.

+  //

+  Wrap                = (IP6_TXTOKEN_WRAP *) Context;

+  Wrap->Token->Status = IoStatus;

+

+  NetbufFree (Wrap->Packet);

+}

+

+/**

+  Places outgoing data packets into the transmit queue.

+

+  The Transmit() function places a sending request in the transmit queue of this

+  EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some

+  errors occur, the event in the token will be signaled, and the status is updated.

+

+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to the transmit token.

+

+  @retval  EFI_SUCCESS           The data has been queued for transmission.

+  @retval  EFI_NOT_STARTED       This instance has not been started.

+  @retval  EFI_NO_MAPPING        The IPv6 driver was responsible for choosing

+                                 a source address for this transmission,

+                                 but no source address was available for use.

+  @retval  EFI_INVALID_PARAMETER One or more of the following is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token.Event is NULL.

+                                 - Token.Packet.TxData is NULL.

+                                 - Token.Packet.ExtHdrsLength is not zero and

+                                   Token.Packet.ExtHdrs is NULL.

+                                 - Token.Packet.FragmentCount is zero.

+                                 - One or more of the Token.Packet.TxData.

+                                   FragmentTable[].FragmentLength fields is zero.

+                                 - One or more of the Token.Packet.TxData.

+                                   FragmentTable[].FragmentBuffer fields is NULL.

+                                 - Token.Packet.TxData.DataLength is zero or not

+                                   equal to the sum of fragment lengths.

+                                 - Token.Packet.TxData.DestinationAddress is non

+                                   zero when DestinationAddress is configured as

+                                   non-zero when doing Configure() for this

+                                   EFI IPv6 protocol instance.

+                                 - Token.Packet.TxData.DestinationAddress is

+                                   unspecified when DestinationAddress is unspecified

+                                   when doing Configure() for this EFI IPv6 protocol

+                                   instance.

+  @retval  EFI_ACCESS_DENIED     The transmit completion token with the same Token.

+                                 Event was already in the transmit queue.

+  @retval  EFI_NOT_READY         The completion token could not be queued because

+                                 the transmit queue is full.

+  @retval  EFI_NOT_FOUND         Not route is found to destination address.

+  @retval  EFI_OUT_OF_RESOURCES  Could not queue the transmit data.

+  @retval  EFI_BUFFER_TOO_SMALL  Token.Packet.TxData.TotalDataLength is too

+                                 short to transmit.

+  @retval  EFI_BAD_BUFFER_SIZE   If Token.Packet.TxData.DataLength is beyond the

+                                 maximum that which can be described through the

+                                 Fragment Offset field in Fragment header when

+                                 performing fragmentation.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Transmit (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN EFI_IP6_COMPLETION_TOKEN  *Token

+  )

+{

+  IP6_SERVICE               *IpSb;

+  IP6_PROTOCOL              *IpInstance;

+  EFI_IP6_CONFIG_DATA       *Config;

+  EFI_STATUS                Status;

+  EFI_TPL                   OldTpl;

+  EFI_IP6_HEADER            Head;

+  EFI_IP6_TRANSMIT_DATA     *TxData;

+  EFI_IP6_OVERRIDE_DATA     *Override;

+  IP6_TXTOKEN_WRAP          *Wrap;

+  UINT8                     *ExtHdrs;

+

+  //

+  // Check input parameters.

+  //

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  ExtHdrs = NULL;

+

+  Status = Ip6TxTokenValid (Token);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);

+  IpSb       = IpInstance->Service;

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  OldTpl     = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (IpInstance->State != IP6_STATE_CONFIGED) {

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  Config = &IpInstance->ConfigData;

+

+  //

+  // Check whether the token or signal already existed.

+  //

+  if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip6TokenExist, Token))) {

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  //

+  // Build the IP header, fill in the information from ConfigData or OverrideData

+  //

+  ZeroMem (&Head, sizeof(EFI_IP6_HEADER));

+  TxData = Token->Packet.TxData;

+  IP6_COPY_ADDRESS (&Head.SourceAddress, &Config->StationAddress);

+  IP6_COPY_ADDRESS (&Head.DestinationAddress, &Config->DestinationAddress);

+

+  Status = EFI_INVALID_PARAMETER;

+

+  if (NetIp6IsUnspecifiedAddr (&TxData->DestinationAddress)) {

+    if (NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {

+      goto Exit;

+    }

+

+    ASSERT (!NetIp6IsUnspecifiedAddr (&Config->StationAddress));

+

+  } else {

+    //

+    // StationAddress is unspecified only when ConfigData.Dest is unspecified.

+    // Use TxData.Dest to override the DestinationAddress.

+    //

+    if (!NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {

+      goto Exit;

+    }

+

+    if (NetIp6IsUnspecifiedAddr (&Config->StationAddress)) {

+      Status = Ip6SelectSourceAddress (

+                 IpSb,

+                 &TxData->DestinationAddress,

+                 &Head.SourceAddress

+                 );

+      if (EFI_ERROR (Status)) {

+        goto Exit;

+      }

+    }

+

+    IP6_COPY_ADDRESS (&Head.DestinationAddress, &TxData->DestinationAddress);

+  }

+

+  //

+  // Fill in Head infos.

+  //

+  Head.NextHeader = Config->DefaultProtocol;

+  if (TxData->ExtHdrsLength != 0) {

+    Head.NextHeader = TxData->NextHeader;

+  }

+

+  if (TxData->OverrideData != NULL) {

+    Override        = TxData->OverrideData;

+    Head.NextHeader = Override->Protocol;

+    Head.HopLimit   = Override->HopLimit;

+    Head.FlowLabelL = HTONS ((UINT16) Override->FlowLabel);

+    Head.FlowLabelH = (UINT8) ((Override->FlowLabel >> 16) & 0x0F);

+  } else {

+    Head.HopLimit   = Config->HopLimit;

+    Head.FlowLabelL = HTONS ((UINT16) Config->FlowLabel);

+    Head.FlowLabelH = (UINT8) ((Config->FlowLabel >> 16) & 0x0F);

+  }

+

+  Head.PayloadLength = HTONS ((UINT16) (TxData->ExtHdrsLength + TxData->DataLength));

+

+  //

+  // OK, it survives all the validation check. Wrap the token in

+  // a IP6_TXTOKEN_WRAP and the data in a netbuf

+  //

+  Status = EFI_OUT_OF_RESOURCES;

+  Wrap   = AllocateZeroPool (sizeof (IP6_TXTOKEN_WRAP));

+  if (Wrap == NULL) {

+    goto Exit;

+  }

+

+  Wrap->IpInstance  = IpInstance;

+  Wrap->Token       = Token;

+  Wrap->Sent        = FALSE;

+  Wrap->Life        = IP6_US_TO_SEC (Config->TransmitTimeout);

+  Wrap->Packet      = NetbufFromExt (

+                        (NET_FRAGMENT *) TxData->FragmentTable,

+                        TxData->FragmentCount,

+                        IP6_MAX_HEADLEN,

+                        0,

+                        Ip6FreeTxToken,

+                        Wrap

+                        );

+

+  if (Wrap->Packet == NULL) {

+    FreePool (Wrap);

+    goto Exit;

+  }

+

+  Token->Status = EFI_NOT_READY;

+

+  Status = NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap);

+  if (EFI_ERROR (Status)) {

+    //

+    // NetbufFree will call Ip6FreeTxToken, which in turn will

+    // free the IP6_TXTOKEN_WRAP. Now, the token wrap hasn't been

+    // enqueued.

+    //

+    NetbufFree (Wrap->Packet);

+    goto Exit;

+  }

+

+  //

+  // Allocate a new buffer to store IPv6 extension headers to avoid updating

+  // the original data in EFI_IP6_COMPLETION_TOKEN.

+  //

+  if (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs != NULL) {

+    ExtHdrs = (UINT8 *) AllocateCopyPool (TxData->ExtHdrsLength, TxData->ExtHdrs);

+    if (ExtHdrs == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto Exit;

+    }

+  }

+

+  //

+  // Mark the packet sent before output it. Mark it not sent again if the

+  // returned status is not EFI_SUCCESS;

+  //

+  Wrap->Sent = TRUE;

+

+  Status = Ip6Output (

+             IpSb,

+             NULL,

+             IpInstance,

+             Wrap->Packet,

+             &Head,

+             ExtHdrs,

+             TxData->ExtHdrsLength,

+             Ip6OnPacketSent,

+             Wrap

+             );

+  if (EFI_ERROR (Status)) {

+    Wrap->Sent = FALSE;

+    NetbufFree (Wrap->Packet);

+  }

+

+Exit:

+  gBS->RestoreTPL (OldTpl);

+

+  if (ExtHdrs != NULL) {

+    FreePool (ExtHdrs);

+  }

+

+  return Status;

+}

+

+/**

+  Places a receiving request into the receiving queue.

+

+  The Receive() function places a completion token into the receive packet queue.

+  This function is always asynchronous.

+

+  The Token.Event field in the completion token must be filled in by the caller

+  and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol

+  driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event

+  is signaled.

+

+  Current Udp implementation creates an IP child for each Udp child.

+  It initates a asynchronous receive immediately no matter whether

+  there is no mapping or not. Therefore, disable the returning EFI_NO_MAPPING for now.

+  To enable it, the following check must be performed:

+

+  if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) {

+    Status = EFI_NO_MAPPING;

+    goto Exit;

+  }

+

+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to a token that is associated with the receive data descriptor.

+

+  @retval EFI_SUCCESS            The receive completion token was cached.

+  @retval EFI_NOT_STARTED        This EFI IPv6 Protocol instance has not been started.

+  @retval EFI_NO_MAPPING         When IP6 driver responsible for binding source address to this instance,

+                                 while no source address is available for use.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued due to a lack of system

+                                 resources (usually memory).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+                                 The EFI IPv6 Protocol instance has been reset to startup defaults.

+  @retval EFI_ACCESS_DENIED      The receive completion token with the same Token.Event was already

+                                 in the receive queue.

+  @retval EFI_NOT_READY          The receive request could not be queued because the receive queue is full.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Receive (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN EFI_IP6_COMPLETION_TOKEN  *Token

+  )

+{

+  IP6_PROTOCOL              *IpInstance;

+  EFI_STATUS                Status;

+  EFI_TPL                   OldTpl;

+  IP6_SERVICE               *IpSb;

+

+  if (This == NULL || Token == NULL || Token->Event == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);

+  IpSb       = IpInstance->Service;

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (IpInstance->State != IP6_STATE_CONFIGED) {

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  //

+  // Check whether the toke is already on the receive queue.

+  //

+  Status = NetMapIterate (&IpInstance->RxTokens, Ip6TokenExist, Token);

+

+  if (EFI_ERROR (Status)) {

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  //

+  // Queue the token then check whether there is pending received packet.

+  //

+  Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL);

+

+  if (EFI_ERROR (Status)) {

+    goto Exit;

+  }

+

+  Status = Ip6InstanceDeliverPacket (IpInstance);

+

+  //

+  // Dispatch the DPC queued by the NotifyFunction of this instane's receive

+  // event.

+  //

+  DispatchDpc ();

+

+Exit:

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+

+/**

+  Cancel the transmitted but not recycled packet. If a matching

+  token is found, it will call Ip6CancelPacket to cancel the

+  packet. Ip6CancelPacket cancels all the fragments of the

+  packet. When all the fragments are freed, the IP6_TXTOKEN_WRAP

+  is deleted from the Map, and user's event is signalled.

+  Because Ip6CancelPacket and other functions are all called in

+  line, after Ip6CancelPacket returns, the Item has been freed.

+

+  @param[in]  Map                The IP6 child's transmit queue.

+  @param[in]  Item               The current transmitted packet to test.

+  @param[in]  Context            The user's token to cancel.

+

+  @retval EFI_SUCCESS            Continue to check the next Item.

+  @retval EFI_ABORTED            The user's Token (Token != NULL) is cancelled.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6CancelTxTokens (

+  IN NET_MAP                *Map,

+  IN NET_MAP_ITEM           *Item,

+  IN VOID                   *Context

+  )

+{

+  EFI_IP6_COMPLETION_TOKEN  *Token;

+  IP6_TXTOKEN_WRAP          *Wrap;

+

+  Token = (EFI_IP6_COMPLETION_TOKEN *) Context;

+

+  //

+  // Return EFI_SUCCESS to check the next item in the map if

+  // this one doesn't match.

+  //

+  if ((Token != NULL) && (Token != Item->Key)) {

+    return EFI_SUCCESS;

+  }

+

+  Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;

+  ASSERT (Wrap != NULL);

+

+  //

+  // Don't access the Item, Wrap and Token's members after this point.

+  // Item and wrap has been freed. And we no longer own the Token.

+  //

+  Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);

+

+  //

+  // If only one item is to be cancel, return EFI_ABORTED to stop

+  // iterating the map any more.

+  //

+  if (Token != NULL) {

+    return EFI_ABORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Cancel the receive request. This is simple, because

+  it is only enqueued in our local receive map.

+

+  @param[in]  Map                The IP6 child's receive queue.

+  @param[in]  Item               Current receive request to cancel.

+  @param[in]  Context            The user's token to cancel.

+

+

+  @retval EFI_SUCCESS            Continue to check the next receive request on the

+                                 queue.

+  @retval EFI_ABORTED            The user's token (token != NULL) has been

+                                 cancelled.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6CancelRxTokens (

+  IN NET_MAP                *Map,

+  IN NET_MAP_ITEM           *Item,

+  IN VOID                   *Context

+  )

+{

+  EFI_IP6_COMPLETION_TOKEN  *Token;

+  EFI_IP6_COMPLETION_TOKEN  *This;

+

+  Token = (EFI_IP6_COMPLETION_TOKEN *) Context;

+  This  = Item->Key;

+

+  if ((Token != NULL) && (Token != This)) {

+    return EFI_SUCCESS;

+  }

+

+  NetMapRemoveItem (Map, Item, NULL);

+

+  This->Status        = EFI_ABORTED;

+  This->Packet.RxData = NULL;

+  gBS->SignalEvent (This->Event);

+

+  if (Token != NULL) {

+    return EFI_ABORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Cancel the user's receive/transmit request. It is the worker function of

+  EfiIp6Cancel API.

+

+  @param[in]  IpInstance         The IP6 child.

+  @param[in]  Token              The token to cancel. If NULL, all token will be

+                                 cancelled.

+

+  @retval EFI_SUCCESS            The token is cancelled.

+  @retval EFI_NOT_FOUND          The token isn't found on either the

+                                 transmit/receive queue.

+  @retval EFI_DEVICE_ERROR       Not all tokens are cancelled when Token is NULL.

+

+**/

+EFI_STATUS

+Ip6Cancel (

+  IN IP6_PROTOCOL             *IpInstance,

+  IN EFI_IP6_COMPLETION_TOKEN *Token          OPTIONAL

+  )

+{

+  EFI_STATUS                Status;

+

+  //

+  // First check the transmitted packet. Ip6CancelTxTokens returns

+  // EFI_ABORTED to mean that the token has been cancelled when

+  // token != NULL. So, return EFI_SUCCESS for this condition.

+  //

+  Status = NetMapIterate (&IpInstance->TxTokens, Ip6CancelTxTokens, Token);

+  if (EFI_ERROR (Status)) {

+    if ((Token != NULL) && (Status == EFI_ABORTED)) {

+      return EFI_SUCCESS;

+    }

+

+    return Status;

+  }

+

+  //

+  // Check the receive queue. Ip6CancelRxTokens also returns EFI_ABORT

+  // for Token!=NULL and it is cancelled.

+  //

+  Status = NetMapIterate (&IpInstance->RxTokens, Ip6CancelRxTokens, Token);

+  //

+  // Dispatch the DPCs queued by the NotifyFunction of the canceled rx token's

+  // events.

+  //

+  DispatchDpc ();

+  if (EFI_ERROR (Status)) {

+    if ((Token != NULL) && (Status == EFI_ABORTED)) {

+      return EFI_SUCCESS;

+    }

+

+    return Status;

+  }

+

+  //

+  // OK, if the Token is found when Token != NULL, the NetMapIterate

+  // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS.

+  //

+  if (Token != NULL) {

+    return EFI_NOT_FOUND;

+  }

+

+  //

+  // If Token == NULL, cancel all the tokens. return error if not

+  // all of them are cancelled.

+  //

+  if (!NetMapIsEmpty (&IpInstance->TxTokens) || !NetMapIsEmpty (&IpInstance->RxTokens)) {

+

+    return EFI_DEVICE_ERROR;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Abort an asynchronous transmit or receive request.

+

+  The Cancel() function is used to abort a pending transmit or receive request.

+  If the token is in the transmit or receive request queues, after calling this

+  function, Token->Status will be set to EFI_ABORTED, and then Token->Event will

+  be signaled. If the token is not in one of the queues, which usually means the

+  asynchronous operation has completed, this function will not signal the token,

+  and EFI_NOT_FOUND is returned.

+

+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to a token that has been issued by

+                                 EFI_IP6_PROTOCOL.Transmit() or

+                                 EFI_IP6_PROTOCOL.Receive(). If NULL, all pending

+                                 tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is

+                                 defined in EFI_IP6_PROTOCOL.Transmit().

+

+  @retval EFI_SUCCESS            The asynchronous I/O request was aborted and

+                                 Token->Event was signaled. When Token is NULL, all

+                                 pending requests were aborted, and their events were signaled.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_NOT_STARTED        This instance has not been started.

+  @retval EFI_NOT_FOUND          When Token is not NULL, the asynchronous I/O request was

+                                 not found in the transmit or receive queue. It has either completed

+                                 or was not issued by Transmit() and Receive().

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Cancel (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN EFI_IP6_COMPLETION_TOKEN  *Token    OPTIONAL

+  )

+{

+  IP6_PROTOCOL              *IpInstance;

+  IP6_SERVICE               *IpSb;

+  EFI_STATUS                Status;

+  EFI_TPL                   OldTpl;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);

+  IpSb       = IpInstance->Service;

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (IpInstance->State != IP6_STATE_CONFIGED) {

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  Status = Ip6Cancel (IpInstance, Token);

+

+Exit:

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+/**

+  Polls for incoming data packets, and processes outgoing data packets.

+

+  The Poll() function polls for incoming data packets and processes outgoing data

+  packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll()

+  function to increase the rate that data packets are moved between the communications

+  device and the transmit and receive queues.

+

+  In some systems the periodic timer event may not poll the underlying communications

+  device fast enough to transmit and/or receive all data packets without missing

+  incoming packets or dropping outgoing packets. Drivers and applications that are

+  experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function

+  more often.

+

+  @param[in]  This               Pointer to the EFI_IP6_PROTOCOL instance.

+

+  @retval  EFI_SUCCESS           Incoming or outgoing data was processed.

+  @retval  EFI_NOT_STARTED       This EFI IPv6 Protocol instance has not been started.

+  @retval  EFI_INVALID_PARAMETER This is NULL.

+  @retval  EFI_DEVICE_ERROR      An unexpected system error or network error occurred.

+  @retval  EFI_NOT_READY         No incoming or outgoing data was processed.

+  @retval  EFI_TIMEOUT           Data was dropped out of the transmit and/or receive queue.

+                                 Consider increasing the polling rate.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Poll (

+  IN EFI_IP6_PROTOCOL          *This

+  )

+{

+  IP6_PROTOCOL                  *IpInstance;

+  IP6_SERVICE                   *IpSb;

+  EFI_MANAGED_NETWORK_PROTOCOL  *Mnp;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);

+  IpSb       = IpInstance->Service;

+

+  if (IpSb->LinkLocalDadFail) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  if (IpInstance->State == IP6_STATE_UNCONFIGED) {

+    return EFI_NOT_STARTED;

+  }

+

+  Mnp = IpInstance->Service->Mnp;

+

+  //

+  // Don't lock the Poll function to enable the deliver of

+  // the packet polled up.

+  //

+  return Mnp->Poll (Mnp);

+

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Impl.h b/NetworkPkg/Ip6Dxe/Ip6Impl.h
new file mode 100644
index 0000000..524de5e
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Impl.h
@@ -0,0 +1,751 @@
+/** @file

+  Implementation of EFI_IP6_PROTOCOL protocol interfaces and type definitions.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_IMPL_H__

+#define __EFI_IP6_IMPL_H__

+

+#include <Uefi.h>

+

+#include <Protocol/ServiceBinding.h>

+#include <Protocol/ManagedNetwork.h>

+#include <Protocol/IpSec.h>

+#include <Protocol/Ip6.h>

+#include <Protocol/Ip6Config.h>

+#include <Protocol/Dhcp6.h>

+#include <Protocol/DevicePath.h>

+#include <Protocol/HiiConfigRouting.h>

+#include <Protocol/HiiConfigAccess.h>

+

+#include <Library/DebugLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiRuntimeServicesTableLib.h>

+#include <Library/BaseLib.h>

+#include <Library/UefiLib.h>

+#include <Library/NetLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/DpcLib.h>

+#include <Library/HiiLib.h>

+#include <Library/UefiHiiServicesLib.h>

+#include <Library/DevicePathLib.h>

+#include <Library/PrintLib.h>

+

+#include <Guid/MdeModuleHii.h>

+

+#include "Ip6Common.h"

+#include "Ip6Driver.h"

+#include "Ip6Icmp.h"

+#include "Ip6If.h"

+#include "Ip6Input.h"

+#include "Ip6Mld.h"

+#include "Ip6Nd.h"

+#include "Ip6Option.h"

+#include "Ip6Output.h"

+#include "Ip6Route.h"

+#include "Ip6ConfigNv.h"

+#include "Ip6ConfigImpl.h"

+

+#define IP6_PROTOCOL_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'P')

+#define IP6_SERVICE_SIGNATURE  SIGNATURE_32 ('I', 'P', '6', 'S')

+

+//

+// The state of IP6 protocol. It starts from UNCONFIGED. if it is

+// successfully configured, it goes to CONFIGED. if configure NULL

+// is called, it becomes UNCONFIGED again. If (partly) destroyed, it

+// becomes DESTROY.

+//

+#define IP6_STATE_UNCONFIGED   0

+#define IP6_STATE_CONFIGED     1

+#define IP6_STATE_DESTROY      2

+

+//

+// The state of IP6 service. It starts from UNSTARTED. It transits

+// to STARTED if autoconfigure is started. If default address is

+// configured, it becomes CONFIGED. and if partly destroyed, it goes

+// to DESTROY.

+//

+#define IP6_SERVICE_UNSTARTED  0

+#define IP6_SERVICE_STARTED    1

+#define IP6_SERVICE_CONFIGED   2

+#define IP6_SERVICE_DESTROY    3

+

+#define IP6_INSTANCE_FROM_PROTOCOL(Ip6) \

+          CR ((Ip6), IP6_PROTOCOL, Ip6Proto, IP6_PROTOCOL_SIGNATURE)

+

+#define IP6_SERVICE_FROM_PROTOCOL(Sb)   \

+          CR ((Sb), IP6_SERVICE, ServiceBinding, IP6_SERVICE_SIGNATURE)

+

+#define IP6_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured)

+

+extern EFI_IPSEC_PROTOCOL *mIpSec;

+

+//

+// IP6_TXTOKEN_WRAP wraps the upper layer's transmit token.

+// The user's data is kept in the Packet. When fragment is

+// needed, each fragment of the Packet has a reference to the

+// Packet, no data is actually copied. The Packet will be

+// released when all the fragments of it have been recycled by

+// MNP. Upon then, the IP6_TXTOKEN_WRAP will be released, and

+// user's event signalled.

+//

+typedef struct {

+  IP6_PROTOCOL              *IpInstance;

+  EFI_IP6_COMPLETION_TOKEN  *Token;

+  EFI_EVENT                 IpSecRecycleSignal;

+  NET_BUF                   *Packet;

+  BOOLEAN                   Sent;

+  INTN                      Life;

+} IP6_TXTOKEN_WRAP;

+

+typedef struct {

+  EFI_EVENT                 IpSecRecycleSignal;

+  NET_BUF                   *Packet;

+} IP6_IPSEC_WRAP;

+

+//

+// IP6_RXDATA_WRAP wraps the data IP6 child delivers to the

+// upper layers. The received packet is kept in the Packet.

+// The Packet itself may be constructured from some fragments.

+// All the fragments of the Packet is organized by a

+// IP6_ASSEMBLE_ENTRY structure. If the Packet is recycled by

+// the upper layer, the assemble entry and its associated

+// fragments will be freed at last.

+//

+typedef struct {

+  LIST_ENTRY                Link;

+  IP6_PROTOCOL              *IpInstance;

+  NET_BUF                   *Packet;

+  EFI_IP6_RECEIVE_DATA      RxData;

+} IP6_RXDATA_WRAP;

+

+struct _IP6_PROTOCOL {

+  UINT32                    Signature;

+

+  EFI_IP6_PROTOCOL          Ip6Proto;

+  EFI_HANDLE                Handle;

+  INTN                      State;

+

+  IP6_SERVICE               *Service;

+  LIST_ENTRY                Link; // Link to all the IP protocol from the service

+

+  UINT8                     PrefixLength; // PrefixLength of the configured station address.

+  //

+  // User's transmit/receive tokens, and received/deliverd packets

+  //

+  NET_MAP                   RxTokens;

+  NET_MAP                   TxTokens;   // map between (User's Token, IP6_TXTOKE_WRAP)

+  LIST_ENTRY                Received;   // Received but not delivered packet

+  LIST_ENTRY                Delivered;  // Delivered and to be recycled packets

+  EFI_LOCK                  RecycleLock;

+

+  IP6_INTERFACE             *Interface;

+  LIST_ENTRY                AddrLink;   // Ip instances with the same IP address.

+

+  EFI_IPv6_ADDRESS          *GroupList; // stored in network order.

+  UINT32                    GroupCount;

+

+  EFI_IP6_CONFIG_DATA       ConfigData;

+};

+

+struct _IP6_SERVICE {

+  UINT32                          Signature;

+  EFI_SERVICE_BINDING_PROTOCOL    ServiceBinding;

+  INTN                            State;

+  BOOLEAN                         InDestroy;

+

+  //

+  // List of all the IP instances and interfaces, and default

+  // interface and route table and caches.

+  //

+  UINTN                           NumChildren;

+  LIST_ENTRY                      Children;

+

+  LIST_ENTRY                      Interfaces;

+

+  IP6_INTERFACE                   *DefaultInterface;

+  IP6_ROUTE_TABLE                 *RouteTable;

+

+  IP6_LINK_RX_TOKEN               RecvRequest;

+

+  //

+  // Ip reassemble utilities and MLD data

+  //

+  IP6_ASSEMBLE_TABLE              Assemble;

+  IP6_MLD_SERVICE_DATA            MldCtrl;

+

+  EFI_IPv6_ADDRESS                LinkLocalAddr;

+  BOOLEAN                         LinkLocalOk;

+  BOOLEAN                         LinkLocalDadFail;

+  BOOLEAN                         Dhcp6NeedStart;

+  BOOLEAN                         Dhcp6NeedInfoRequest;

+

+  //

+  // ND data

+  //

+  UINT8                           CurHopLimit;

+  UINT32                          LinkMTU;

+  UINT32                          BaseReachableTime;

+  UINT32                          ReachableTime;

+  UINT32                          RetransTimer;

+  LIST_ENTRY                      NeighborTable;

+

+  LIST_ENTRY                      OnlinkPrefix;

+  LIST_ENTRY                      AutonomousPrefix;

+

+  LIST_ENTRY                      DefaultRouterList;

+  UINT32                          RoundRobin;

+

+  UINT8                           InterfaceIdLen;

+  UINT8                           *InterfaceId;

+

+  BOOLEAN                         RouterAdvertiseReceived;

+  UINT8                           SolicitTimer;

+  UINT32                          Ticks;

+

+  //

+  // Low level protocol used by this service instance

+  //

+  EFI_HANDLE                      Image;

+  EFI_HANDLE                      Controller;

+

+  EFI_HANDLE                      MnpChildHandle;

+  EFI_MANAGED_NETWORK_PROTOCOL    *Mnp;

+

+  EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;

+  EFI_SIMPLE_NETWORK_MODE         SnpMode;

+

+  EFI_EVENT                       Timer;

+  EFI_EVENT                       FasterTimer;

+

+  //

+  // IPv6 Configuration Protocol instance

+  //

+  IP6_CONFIG_INSTANCE             Ip6ConfigInstance;

+

+  //

+  // The string representation of the current mac address of the

+  // NIC this IP6_SERVICE works on.

+  //

+  CHAR16                          *MacString;

+  UINT32                          MaxPacketSize;

+  UINT32                          OldMaxPacketSize;

+};

+

+/**

+  The callback function for the net buffer which wraps the user's

+  transmit token. Although this function seems simple,

+  there are some subtle aspects.

+  When a user requests the IP to transmit a packet by passing it a

+  token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data

+  is wrapped in a net buffer. The net buffer's Free function is

+  set to Ip6FreeTxToken. The Token and token wrap are added to the

+  IP child's TxToken map. Then the buffer is passed to Ip6Output for

+  transmission. If an error occurs before that, the buffer

+  is freed, which in turn frees the token wrap. The wrap may

+  have been added to the TxToken map or not, and the user's event

+  shouldn't be signaled because we are still in the EfiIp6Transmit. If

+  the buffer has been sent by Ip6Output, it should be removed from

+  the TxToken map and the user's event signaled. The token wrap and buffer

+  are bound together. Refer to the comments in Ip6Output for information

+  about IP fragmentation.

+

+  @param[in]  Context                The token's wrap.

+

+**/

+VOID

+EFIAPI

+Ip6FreeTxToken (

+  IN VOID                   *Context

+  );

+

+/**

+  Config the MNP parameter used by IP. The IP driver use one MNP

+  child to transmit/receive frames. By default, it configures MNP

+  to receive unicast/multicast/broadcast. And it will enable/disable

+  the promiscuous receive according to whether there is IP child

+  enable that or not. If Force is FALSE, it will iterate through

+  all the IP children to check whether the promiscuous receive

+  setting has been changed. If it hasn't been changed, it won't

+  reconfigure the MNP. If Force is TRUE, the MNP is configured

+  whether that is changed or not.

+

+  @param[in]  IpSb               The IP6 service instance that is to be changed.

+  @param[in]  Force              Force the configuration or not.

+

+  @retval EFI_SUCCESS            The MNP successfully configured/reconfigured.

+  @retval Others                 The configuration failed.

+

+**/

+EFI_STATUS

+Ip6ServiceConfigMnp (

+  IN IP6_SERVICE            *IpSb,

+  IN BOOLEAN                Force

+  );

+

+/**

+  Cancel the user's receive/transmit request. It is the worker function of

+  EfiIp6Cancel API.

+

+  @param[in]  IpInstance         The IP6 child.

+  @param[in]  Token              The token to cancel. If NULL, all tokens will be

+                                 cancelled.

+

+  @retval EFI_SUCCESS            The token was cancelled.

+  @retval EFI_NOT_FOUND          The token isn't found on either the

+                                 transmit or receive queue.

+  @retval EFI_DEVICE_ERROR       Not all tokens are cancelled when Token is NULL.

+

+**/

+EFI_STATUS

+Ip6Cancel (

+  IN IP6_PROTOCOL             *IpInstance,

+  IN EFI_IP6_COMPLETION_TOKEN *Token          OPTIONAL

+  );

+

+/**

+  Initialize the IP6_PROTOCOL structure to the unconfigured states.

+

+  @param[in]       IpSb                   The IP6 service instance.

+  @param[in, out]  IpInstance             The IP6 child instance.

+

+**/

+VOID

+Ip6InitProtocol (

+  IN IP6_SERVICE            *IpSb,

+  IN OUT IP6_PROTOCOL       *IpInstance

+  );

+

+/**

+  Clean up the IP6 child, release all the resources used by it.

+

+  @param[in, out]  IpInstance    The IP6 child to clean up.

+

+  @retval EFI_SUCCESS            The IP6 child was cleaned up

+  @retval EFI_DEVICE_ERROR       Some resources failed to be released.

+

+**/

+EFI_STATUS

+Ip6CleanProtocol (

+  IN OUT IP6_PROTOCOL            *IpInstance

+  );

+

+//

+// EFI_IP6_PROTOCOL interface prototypes

+//

+

+/**

+  Gets the current operational settings for this instance of the EFI IPv6 Protocol driver.

+

+  The GetModeData() function returns the current operational mode data for this driver instance.

+  The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to

+  retrieve the operational mode data of underlying networks or drivers.

+

+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.

+  @param[out] Ip6ModeData        The pointer to the EFI IPv6 Protocol mode data structure.

+  @param[out] MnpConfigData      The pointer to the managed network configuration data structure.

+  @param[out] SnpModeData        The pointer to the simple network mode data structure.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_OUT_OF_RESOURCES   The required mode data could not be allocated.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6GetModeData (

+  IN EFI_IP6_PROTOCOL                 *This,

+  OUT EFI_IP6_MODE_DATA               *Ip6ModeData     OPTIONAL,

+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData   OPTIONAL,

+  OUT EFI_SIMPLE_NETWORK_MODE         *SnpModeData     OPTIONAL

+  );

+

+/**

+  Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance.

+

+  The Configure() function is used to set, change, or reset the operational parameters and filter

+  settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic

+  can be sent or received by this instance. Once the parameters have been reset (by calling this

+  function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these

+  parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped

+  independently of each other by enabling or disabling their receive filter settings with the

+  Configure() function.

+

+  If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required

+  to be one of the currently configured IPv6 addresses list in the EFI IPv6 drivers, or else

+  EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is

+  unspecified, the IPv6 driver will bind a source address according to the source address selection

+  algorithm. Clients could frequently call GetModeData() to check get a currently configured IPv6.

+  If both Ip6ConfigData.StationAddress and Ip6ConfigData.Destination are unspecified, when

+  transmitting the packet afterwards, the source address filled in each outgoing IPv6 packet

+  is decided based on the destination of this packet.

+

+  If operational parameters are reset or changed, any pending transmit and receive requests will be

+  cancelled. Their completion token status will be set to EFI_ABORTED, and their events will be

+  signaled.

+

+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  Ip6ConfigData      The pointer to the EFI IPv6 Protocol configuration data structure.

+                                 If NULL, reset the configuration data.

+

+  @retval EFI_SUCCESS            The driver instance was successfully opened.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Ip6ConfigData.StationAddress is neither zero nor

+                                   a unicast IPv6 address.

+                                 - Ip6ConfigData.StationAddress is neither zero nor

+                                   one of the configured IP addresses in the EFI IPv6 driver.

+                                 - Ip6ConfigData.DefaultProtocol is illegal.

+  @retval EFI_OUT_OF_RESOURCES   The EFI IPv6 Protocol driver instance data could not be allocated.

+  @retval EFI_NO_MAPPING         The IPv6 driver was responsible for choosing a source address for

+                                 this instance, but no source address was available for use.

+  @retval EFI_ALREADY_STARTED    The interface is already open and must be stopped before the IPv6

+                                 address or prefix length can be changed.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred. The EFI IPv6

+                                 Protocol driver instance was not opened.

+  @retval EFI_UNSUPPORTED        Default protocol specified through

+                                 Ip6ConfigData.DefaulProtocol isn't supported.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Configure (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN EFI_IP6_CONFIG_DATA       *Ip6ConfigData OPTIONAL

+  );

+

+/**

+  Joins and leaves multicast groups.

+

+  The Groups() function is used to join and leave multicast group sessions. Joining a group will

+  enable reception of matching multicast packets. Leaving a group will disable reception of matching

+  multicast packets. Source-Specific Multicast isn't required to be supported.

+

+  If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.

+

+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  JoinFlag           Set to TRUE to join the multicast group session and FALSE to leave.

+  @param[in]  GroupAddress       The pointer to the IPv6 multicast address.

+                                 This is an optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER  One or more of the following is TRUE:

+                                 - This is NULL.

+                                 - JoinFlag is TRUE and GroupAddress is NULL.

+                                 - GroupAddress is not NULL and *GroupAddress is

+                                   not a multicast IPv6 address.

+                                 - GroupAddress is not NULL and *GroupAddress is in the

+                                   range of SSM destination address.

+  @retval EFI_NOT_STARTED        This instance has not been started.

+  @retval EFI_OUT_OF_RESOURCES   System resources could not be allocated.

+  @retval EFI_UNSUPPORTED        This EFI IPv6 Protocol implementation does not support multicast groups.

+  @retval EFI_ALREADY_STARTED    The group address is already in the group table (when

+                                 JoinFlag is TRUE).

+  @retval EFI_NOT_FOUND          The group address is not in the group table (when JoinFlag is FALSE).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Groups (

+  IN EFI_IP6_PROTOCOL  *This,

+  IN BOOLEAN           JoinFlag,

+  IN EFI_IPv6_ADDRESS  *GroupAddress  OPTIONAL

+  );

+

+/**

+  Adds and deletes routing table entries.

+

+  The Routes() function adds a route to or deletes a route from the routing table.

+

+  Routes are determined by comparing the leftmost PrefixLength bits of Destination with

+  the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the

+  configured station address.

+

+  The default route is added with Destination and PrefixLegth both set to all zeros. The

+  default route matches all destination IPv6 addresses that do not match any other routes.

+

+  All EFI IPv6 Protocol instances share a routing table.

+

+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  DeleteRoute        Set to TRUE to delete this route from the routing table. Set to

+                                 FALSE to add this route to the routing table. Destination,

+                                 PrefixLength and Gateway are used as the key to each

+                                 route entry.

+  @param[in]  Destination        The address prefix of the subnet that needs to be routed.

+                                 This is an optional parameter that may be NULL.

+  @param[in]  PrefixLength       The prefix length of Destination. Ignored if Destination

+                                 is NULL.

+  @param[in]  GatewayAddress     The unicast gateway IPv6 address for this route.

+                                 This is an optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_NOT_STARTED        The driver instance has not been started.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - When DeleteRoute is TRUE, both Destination and

+                                   GatewayAddress are NULL.

+                                 - When DeleteRoute is FALSE, either Destination or

+                                   GatewayAddress is NULL.

+                                 - *GatewayAddress is not a valid unicast IPv6 address.

+                                 - *GatewayAddress is one of the local configured IPv6

+                                   addresses.

+  @retval EFI_OUT_OF_RESOURCES   Could not add the entry to the routing table.

+  @retval EFI_NOT_FOUND          This route is not in the routing table (when DeleteRoute is TRUE).

+  @retval EFI_ACCESS_DENIED      The route is already defined in the routing table (when

+                                 DeleteRoute is FALSE).

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Routes (

+  IN EFI_IP6_PROTOCOL    *This,

+  IN BOOLEAN             DeleteRoute,

+  IN EFI_IPv6_ADDRESS    *Destination    OPTIONAL,

+  IN UINT8               PrefixLength,

+  IN EFI_IPv6_ADDRESS    *GatewayAddress OPTIONAL

+  );

+

+/**

+  Add or delete Neighbor cache entries.

+

+  The Neighbors() function is used to add, update, or delete an entry from a neighbor cache.

+  IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as

+  network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network

+  traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not

+  timeout) or dynamic (will timeout).

+

+  The implementation should follow the neighbor cache timeout mechanism defined in

+  RFC4861. The default neighbor cache timeout value should be tuned for the expected network

+  environment.

+

+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  DeleteFlag         Set to TRUE to delete the specified cache entry. Set to FALSE to

+                                 add (or update, if it already exists and Override is TRUE) the

+                                 specified cache entry. TargetIp6Address is used as the key

+                                 to find the requested cache entry.

+  @param[in]  TargetIp6Address   The pointer to the Target IPv6 address.

+  @param[in]  TargetLinkAddress  The pointer to link-layer address of the target. Ignored if NULL.

+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor

+                                 cache, it will be deleted after Timeout. A value of zero means that

+                                 the entry is permanent. A non-zero value means that the entry is

+                                 dynamic.

+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will

+                                 be overridden and updated; if FALSE, EFI_ACCESS_DENIED

+                                 will be returned if a corresponding cache entry already exists.

+

+  @retval  EFI_SUCCESS           The data has been queued for transmission.

+  @retval  EFI_NOT_STARTED       This instance has not been started.

+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - TargetIpAddress is NULL.

+                                 - *TargetLinkAddress is invalid when not NULL.

+                                 - *TargetIpAddress is not a valid unicast IPv6 address.

+                                 - *TargetIpAddress is one of the local configured IPv6

+                                   addresses.

+  @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache.

+  @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache (when DeleteFlag  is

+                                 TRUE or when DeleteFlag  is FALSE while

+                                 TargetLinkAddress is NULL.).

+  @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,

+                                 and that entry is tagged as un-overridden (when Override

+                                 is FALSE).

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Neighbors (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN BOOLEAN                   DeleteFlag,

+  IN EFI_IPv6_ADDRESS          *TargetIp6Address,

+  IN EFI_MAC_ADDRESS           *TargetLinkAddress OPTIONAL,

+  IN UINT32                    Timeout,

+  IN BOOLEAN                   Override

+  );

+

+/**

+  Places outgoing data packets into the transmit queue.

+

+  The Transmit() function places a sending request in the transmit queue of this

+  EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some

+  errors occur, the event in the token will be signaled and the status is updated.

+

+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  Token              The pointer to the transmit token.

+

+  @retval  EFI_SUCCESS           The data has been queued for transmission.

+  @retval  EFI_NOT_STARTED       This instance has not been started.

+  @retval  EFI_NO_MAPPING        The IPv6 driver was responsible for choosing

+                                 a source address for this transmission,

+                                 but no source address was available for use.

+  @retval  EFI_INVALID_PARAMETER One or more of the following is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token.Event is NULL.

+                                 - Token.Packet.TxData is NULL.

+                                 - Token.Packet.ExtHdrsLength is not zero and

+                                   Token.Packet.ExtHdrs is NULL.

+                                 - Token.Packet.FragmentCount is zero.

+                                 - One or more of the Token.Packet.TxData.

+                                   FragmentTable[].FragmentLength fields is zero.

+                                 - One or more of the Token.Packet.TxData.

+                                   FragmentTable[].FragmentBuffer fields is NULL.

+                                 - Token.Packet.TxData.DataLength is zero or not

+                                   equal to the sum of fragment lengths.

+                                 - Token.Packet.TxData.DestinationAddress is non-

+                                   zero when DestinationAddress is configured as

+                                   non-zero when doing Configure() for this

+                                   EFI IPv6 protocol instance.

+                                 - Token.Packet.TxData.DestinationAddress is

+                                   unspecified when DestinationAddress is unspecified

+                                   when doing Configure() for this EFI IPv6 protocol

+                                   instance.

+  @retval  EFI_ACCESS_DENIED     The transmit completion token with the same Token.

+                                 The event was already in the transmit queue.

+  @retval  EFI_NOT_READY         The completion token could not be queued because

+                                 the transmit queue is full.

+  @retval  EFI_NOT_FOUND         Not route is found to the destination address.

+  @retval  EFI_OUT_OF_RESOURCES  Could not queue the transmit data.

+  @retval  EFI_BUFFER_TOO_SMALL  Token.Packet.TxData.TotalDataLength is too

+                                 short to transmit.

+  @retval  EFI_BAD_BUFFER_SIZE   If Token.Packet.TxData.DataLength is beyond the

+                                 maximum that which can be described through the

+                                 Fragment Offset field in Fragment header when

+                                 performing fragmentation.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Transmit (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN EFI_IP6_COMPLETION_TOKEN  *Token

+  );

+

+/**

+  Places a receiving request into the receiving queue.

+

+  The Receive() function places a completion token into the receive packet queue.

+  This function is always asynchronous.

+

+  The Token.Event field in the completion token must be filled in by the caller

+  and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol

+  driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event

+  is signaled.

+

+  Current Udp implementation creates an IP child for each Udp child.

+  It initates a asynchronous receive immediately whether or not

+  there is no mapping. Therefore, disable the returning EFI_NO_MAPPING for now.

+  To enable it, the following check must be performed:

+

+  if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) {

+    Status = EFI_NO_MAPPING;

+    goto Exit;

+  }

+

+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  Token              The pointer to a token that is associated with the

+                                 receive data descriptor.

+

+  @retval EFI_SUCCESS            The receive completion token was cached.

+  @retval EFI_NOT_STARTED        This EFI IPv6 Protocol instance has not been started.

+  @retval EFI_NO_MAPPING         When IP6 driver responsible for binding source address to this instance,

+                                 while no source address is available for use.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued due to a lack of system

+                                 resources (usually memory).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+                                 The EFI IPv6 Protocol instance has been reset to startup defaults.

+  @retval EFI_ACCESS_DENIED      The receive completion token with the same Token.Event was already

+                                 in the receive queue.

+  @retval EFI_NOT_READY          The receive request could not be queued because the receive queue is full.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Receive (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN EFI_IP6_COMPLETION_TOKEN  *Token

+  );

+

+/**

+  Abort an asynchronous transmit or receive request.

+

+  The Cancel() function is used to abort a pending transmit or receive request.

+  If the token is in the transmit or receive request queues, after calling this

+  function, Token->Status will be set to EFI_ABORTED, and then Token->Event will

+  be signaled. If the token is not in one of the queues, which usually means the

+  asynchronous operation has completed, this function will not signal the token,

+  and EFI_NOT_FOUND is returned.

+

+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.

+  @param[in]  Token              The pointer to a token that has been issued by

+                                 EFI_IP6_PROTOCOL.Transmit() or

+                                 EFI_IP6_PROTOCOL.Receive(). If NULL, all pending

+                                 tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is

+                                 defined in EFI_IP6_PROTOCOL.Transmit().

+

+  @retval EFI_SUCCESS            The asynchronous I/O request was aborted and

+                                 Token->Event was signaled. When Token is NULL, all

+                                 pending requests were aborted, and their events were signaled.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_NOT_STARTED        This instance has not been started.

+  @retval EFI_NOT_FOUND          When Token is not NULL, the asynchronous I/O request was

+                                 not found in the transmit or receive queue. It has either completed

+                                 or was not issued by Transmit() and Receive().

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Cancel (

+  IN EFI_IP6_PROTOCOL          *This,

+  IN EFI_IP6_COMPLETION_TOKEN  *Token    OPTIONAL

+  );

+

+/**

+  Polls for incoming data packets and processes outgoing data packets.

+

+  The Poll() function polls for incoming data packets and processes outgoing data

+  packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll()

+  function to increase the rate that data packets are moved between the communications

+  device and the transmit and receive queues.

+

+  In some systems the periodic timer event may not poll the underlying communications

+  device fast enough to transmit and/or receive all data packets without missing

+  incoming packets or dropping outgoing packets. Drivers and applications that are

+  experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function

+  more often.

+

+  @param[in]  This               The pointer to the EFI_IP6_PROTOCOL instance.

+

+  @retval  EFI_SUCCESS           Incoming or outgoing data was processed.

+  @retval  EFI_NOT_STARTED       This EFI IPv6 Protocol instance has not been started.

+  @retval  EFI_INVALID_PARAMETER This is NULL.

+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+  @retval  EFI_NOT_READY         No incoming or outgoing data was processed.

+  @retval  EFI_TIMEOUT           Data was dropped out of the transmit and/or receive queue.

+                                 Consider increasing the polling rate.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIp6Poll (

+  IN EFI_IP6_PROTOCOL          *This

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6Input.c b/NetworkPkg/Ip6Dxe/Ip6Input.c
new file mode 100644
index 0000000..c18811b
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Input.c
@@ -0,0 +1,1710 @@
+/** @file

+  IP6 internal functions to process the incoming packets.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+/**

+  Create an empty assemble entry for the packet identified by

+  (Dst, Src, Id). The default life for the packet is 60 seconds.

+

+  @param[in]  Dst                    The destination address.

+  @param[in]  Src                    The source address.

+  @param[in]  Id                     The ID field in the IP header.

+

+  @return NULL if failed to allocate memory for the entry. Otherwise,

+          the pointer to the just created reassemble entry.

+

+**/

+IP6_ASSEMBLE_ENTRY *

+Ip6CreateAssembleEntry (

+  IN EFI_IPv6_ADDRESS       *Dst,

+  IN EFI_IPv6_ADDRESS       *Src,

+  IN UINT32                 Id

+  )

+{

+  IP6_ASSEMBLE_ENTRY        *Assemble;

+

+  Assemble = AllocatePool (sizeof (IP6_ASSEMBLE_ENTRY));

+  if (Assemble == NULL) {

+    return NULL;

+  }

+

+  IP6_COPY_ADDRESS (&Assemble->Dst, Dst);

+  IP6_COPY_ADDRESS (&Assemble->Src, Src);

+  InitializeListHead (&Assemble->Fragments);

+

+  Assemble->Id       = Id;

+  Assemble->Life     = IP6_FRAGMENT_LIFE + 1;

+

+  Assemble->TotalLen = 0;

+  Assemble->CurLen   = 0;

+  Assemble->Head     = NULL;

+  Assemble->Info     = NULL;

+  Assemble->Packet   = NULL;

+

+  return Assemble;

+}

+

+/**

+  Release all the fragments of a packet, then free the assemble entry.

+

+  @param[in]  Assemble               The assemble entry to free.

+

+**/

+VOID

+Ip6FreeAssembleEntry (

+  IN IP6_ASSEMBLE_ENTRY     *Assemble

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  NET_BUF                   *Fragment;

+

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {

+    Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);

+

+    RemoveEntryList (Entry);

+    NetbufFree (Fragment);

+  }

+

+  if (Assemble->Packet != NULL) {

+    NetbufFree (Assemble->Packet);

+  }

+

+  FreePool (Assemble);

+}

+

+/**

+  Release all the fragments of the packet. This is the callback for

+  the assembled packet's OnFree. It will free the assemble entry,

+  which in turn frees all the fragments of the packet.

+

+  @param[in]  Arg                    The assemble entry to free.

+

+**/

+VOID

+EFIAPI

+Ip6OnFreeFragments (

+  IN VOID                   *Arg

+  )

+{

+  Ip6FreeAssembleEntry ((IP6_ASSEMBLE_ENTRY *) Arg);

+}

+

+/**

+  Trim the packet to fit in [Start, End), and update per the

+  packet information.

+

+  @param[in, out]  Packet   Packet to trim.

+  @param[in]       Start    The sequence of the first byte to fit in.

+  @param[in]       End      One beyond the sequence of last byte to fit in.

+

+**/

+VOID

+Ip6TrimPacket (

+  IN OUT NET_BUF            *Packet,

+  IN INTN                   Start,

+  IN INTN                   End

+  )

+{

+  IP6_CLIP_INFO             *Info;

+  INTN                      Len;

+

+  Info = IP6_GET_CLIP_INFO (Packet);

+

+  ASSERT (Info->Start + Info->Length == Info->End);

+  ASSERT ((Info->Start < End) && (Start < Info->End));

+

+   if (Info->Start < Start) {

+    Len = Start - Info->Start;

+

+    NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);

+    Info->Start   = (UINT32) Start;

+    Info->Length -= (UINT32) Len;

+  }

+

+  if (End < Info->End) {

+    Len = End - Info->End;

+

+    NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);

+    Info->End     = (UINT32) End;

+    Info->Length -= (UINT32) Len;

+  }

+}

+

+/**

+  Reassemble the IP fragments. If all the fragments of the packet

+  have been received, it will wrap the packet in a net buffer then

+  return it to caller. If the packet can't be assembled, NULL is

+  returned.

+

+  @param[in, out] Table  The assemble table used. A new assemble entry will be created

+                         if the Packet is from a new chain of fragments.

+  @param[in]      Packet The fragment to assemble. It might be freed if the fragment

+                         can't be re-assembled.

+

+  @return NULL if the packet can't be reassembled. The pointer to the just assembled

+          packet if all the fragments of the packet have arrived.

+

+**/

+NET_BUF *

+Ip6Reassemble (

+  IN OUT IP6_ASSEMBLE_TABLE *Table,

+  IN NET_BUF                *Packet

+  )

+{

+  EFI_IP6_HEADER            *Head;

+  IP6_CLIP_INFO             *This;

+  IP6_CLIP_INFO             *Node;

+  IP6_ASSEMBLE_ENTRY        *Assemble;

+  IP6_ASSEMBLE_ENTRY        *Entry;

+  LIST_ENTRY                *ListHead;

+  LIST_ENTRY                *Prev;

+  LIST_ENTRY                *Cur;

+  NET_BUF                   *Fragment;

+  NET_BUF                   *TmpPacket;

+  NET_BUF                   *NewPacket;

+  NET_BUF                   *Duplicate;

+  UINT8                     *DupHead;

+  INTN                      Index;

+  UINT16                    UnFragmentLen;

+  UINT8                     *NextHeader;

+

+  Head = Packet->Ip.Ip6;

+  This = IP6_GET_CLIP_INFO (Packet);

+

+  ASSERT (Head != NULL);

+

+  //

+  // Find the corresponding assemble entry by (Dst, Src, Id)

+  //

+  Assemble  = NULL;

+  Index     = IP6_ASSEMBLE_HASH (&Head->DestinationAddress, &Head->SourceAddress, This->Id);

+

+  NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {

+    Entry = NET_LIST_USER_STRUCT (Cur, IP6_ASSEMBLE_ENTRY, Link);

+

+    if (Entry->Id == This->Id &&

+        EFI_IP6_EQUAL (&Entry->Src, &Head->SourceAddress) &&

+        EFI_IP6_EQUAL (&Entry->Dst, &Head->DestinationAddress)

+          ) {

+      Assemble = Entry;

+      break;

+    }

+  }

+

+  //

+  // Create a new entry if can not find an existing one, insert it to assemble table

+  //

+  if (Assemble == NULL) {

+    Assemble = Ip6CreateAssembleEntry (

+                 &Head->DestinationAddress,

+                 &Head->SourceAddress,

+                 This->Id

+                 );

+

+    if (Assemble == NULL) {

+      goto Error;

+    }

+

+    InsertHeadList (&Table->Bucket[Index], &Assemble->Link);

+  }

+

+  //

+  // Find the point to insert the packet: before the first

+  // fragment with THIS.Start < CUR.Start. the previous one

+  // has PREV.Start <= THIS.Start < CUR.Start.

+  //

+  ListHead = &Assemble->Fragments;

+

+  NET_LIST_FOR_EACH (Cur, ListHead) {

+    Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);

+

+    if (This->Start < IP6_GET_CLIP_INFO (Fragment)->Start) {

+      break;

+    }

+  }

+

+  //

+  // Check whether the current fragment overlaps with the previous one.

+  // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to

+  // check whether THIS.Start < PREV.End for overlap. If two fragments

+  // overlaps, trim the overlapped part off THIS fragment.

+  //

+  if ((Cur != ListHead) && ((Prev = Cur->BackLink) != ListHead)) {

+    Fragment  = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);

+    Node      = IP6_GET_CLIP_INFO (Fragment);

+

+    if (This->Start < Node->End) {

+      if (This->End <= Node->End) {

+        goto Error;

+      }

+

+      //

+      // Trim the previous fragment from tail.

+      //

+      Ip6TrimPacket (Fragment, Node->Start, This->Start);

+    }

+  }

+

+  //

+  // Insert the fragment into the packet. The fragment may be removed

+  // from the list by the following checks.

+  //

+  NetListInsertBefore (Cur, &Packet->List);

+

+  //

+  // Check the packets after the insert point. It holds that:

+  // THIS.Start <= NODE.Start < NODE.End. The equality holds

+  // if PREV and NEXT are continuous. THIS fragment may fill

+  // several holes. Remove the completely overlapped fragments

+  //

+  while (Cur != ListHead) {

+    Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);

+    Node     = IP6_GET_CLIP_INFO (Fragment);

+

+    //

+    // Remove fragments completely overlapped by this fragment

+    //

+    if (Node->End <= This->End) {

+      Cur = Cur->ForwardLink;

+

+      RemoveEntryList (&Fragment->List);

+      Assemble->CurLen -= Node->Length;

+

+      NetbufFree (Fragment);

+      continue;

+    }

+

+    //

+    // The conditions are: THIS.Start <= NODE.Start, and THIS.End <

+    // NODE.End. Two fragments overlaps if NODE.Start < THIS.End.

+    // If two fragments start at the same offset, remove THIS fragment

+    // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).

+    //

+    if (Node->Start < This->End) {

+      if (This->Start == Node->Start) {

+        RemoveEntryList (&Packet->List);

+        goto Error;

+      }

+

+      Ip6TrimPacket (Packet, This->Start, Node->Start);

+    }

+

+    break;

+  }

+

+  //

+  // Update the assemble info: increase the current length. If it is

+  // the frist fragment, update the packet's IP head and per packet

+  // info. If it is the last fragment, update the total length.

+  //

+  Assemble->CurLen += This->Length;

+

+  if (This->Start == 0) {

+    //

+    // Once the first fragment is enqueued, it can't be removed

+    // from the fragment list. So, Assemble->Head always point

+    // to valid memory area.

+    //

+    if ((Assemble->Head != NULL) || (Assemble->Packet != NULL)) {

+      goto Error;

+    }

+

+    //

+    // Backup the first fragment in case the reasembly of that packet fail.

+    //

+    Duplicate = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));

+    if (Duplicate == NULL) {

+      goto Error;

+    }

+

+    //

+    // Revert IP head to network order.

+    //

+    DupHead = NetbufGetByte (Duplicate, 0, NULL);

+    ASSERT (DupHead != NULL);

+    Duplicate->Ip.Ip6 = Ip6NtohHead ((EFI_IP6_HEADER *) DupHead);

+    Assemble->Packet  = Duplicate;

+

+    //

+    // Adjust the unfragmentable part in first fragment

+    //

+    UnFragmentLen = (UINT16) (This->HeadLen - sizeof (EFI_IP6_HEADER));

+    if (UnFragmentLen == 0) {

+      //

+      // There is not any unfragmentable extension header.

+      //

+      ASSERT (Head->NextHeader == IP6_FRAGMENT);

+      Head->NextHeader = This->NextHeader;

+    } else {

+      NextHeader = NetbufGetByte (

+                     Packet,

+                     This->FormerNextHeader + sizeof (EFI_IP6_HEADER),

+                     0

+                     );

+      if (NextHeader == NULL) {

+        goto Error;

+      }

+

+      *NextHeader = This->NextHeader;

+    }

+

+    Assemble->Head = Head;

+    Assemble->Info = IP6_GET_CLIP_INFO (Packet);

+  }

+

+  //

+  // Don't update the length more than once.

+  //

+  if ((This->LastFrag != 0) && (Assemble->TotalLen == 0)) {

+    Assemble->TotalLen = This->End;

+  }

+

+  //

+  // Deliver the whole packet if all the fragments received.

+  // All fragments received if:

+  //  1. received the last one, so, the totoal length is know

+  //  2. received all the data. If the last fragment on the

+  //     queue ends at the total length, all data is received.

+  //

+  if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {

+

+    RemoveEntryList (&Assemble->Link);

+

+    //

+    // If the packet is properly formated, the last fragment's End

+    // equals to the packet's total length. Otherwise, the packet

+    // is a fake, drop it now.

+    //

+    Fragment = NET_LIST_USER_STRUCT (ListHead->BackLink, NET_BUF, List);

+    if (IP6_GET_CLIP_INFO (Fragment)->End != (INTN) Assemble->TotalLen) {

+      Ip6FreeAssembleEntry (Assemble);

+      goto Error;

+    }

+

+    Fragment = NET_LIST_HEAD (ListHead, NET_BUF, List);

+    This     = Assemble->Info;

+

+    //

+    // This TmpPacket is used to hold the unfragmentable part, i.e.,

+    // the IPv6 header and the unfragmentable extension headers. Be noted that

+    // the Fragment Header is exluded.

+    //

+    TmpPacket = NetbufGetFragment (Fragment, 0, This->HeadLen, 0);

+    ASSERT (TmpPacket != NULL);

+

+    NET_LIST_FOR_EACH (Cur, ListHead) {

+      //

+      // Trim off the unfragment part plus the fragment header from all fragments.

+      //

+      Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);

+      NetbufTrim (Fragment, This->HeadLen + sizeof (IP6_FRAGMENT_HEADER), TRUE);

+    }

+

+    InsertHeadList (ListHead, &TmpPacket->List);

+

+    //

+    // Wrap the packet in a net buffer then deliver it up

+    //

+    NewPacket = NetbufFromBufList (

+                  &Assemble->Fragments,

+                  0,

+                  0,

+                  Ip6OnFreeFragments,

+                  Assemble

+                  );

+

+    if (NewPacket == NULL) {

+      Ip6FreeAssembleEntry (Assemble);

+      goto Error;

+    }

+

+    NewPacket->Ip.Ip6 = Assemble->Head;

+

+    CopyMem (IP6_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP6_CLIP_INFO));

+

+    return NewPacket;

+  }

+

+  return NULL;

+

+Error:

+  NetbufFree (Packet);

+  return NULL;

+}

+

+

+/**

+  The callback function for the net buffer that wraps the packet processed by

+  IPsec. It releases the wrap packet and also signals IPsec to free the resources.

+

+  @param[in]  Arg       The wrap context.

+

+**/

+VOID

+EFIAPI

+Ip6IpSecFree (

+  IN VOID                   *Arg

+  )

+{

+  IP6_IPSEC_WRAP            *Wrap;

+

+  Wrap = (IP6_IPSEC_WRAP *) Arg;

+

+  if (Wrap->IpSecRecycleSignal != NULL) {

+    gBS->SignalEvent (Wrap->IpSecRecycleSignal);

+  }

+

+  NetbufFree (Wrap->Packet);

+

+  FreePool (Wrap);

+

+  return;

+}

+

+/**

+  The work function to locate the IPsec protocol to process the inbound or

+  outbound IP packets. The process routine handles the packet with the following

+  actions: bypass the packet, discard the packet, or protect the packet.

+

+  @param[in]       IpSb          The IP6 service instance.

+  @param[in]       Head          The caller-supplied IP6 header.

+  @param[in, out]  LastHead      The next header field of last IP header.

+  @param[in, out]  Netbuf        The IP6 packet to be processed by IPsec.

+  @param[in]       ExtHdrs       The caller-supplied options.

+  @param[in]       ExtHdrsLen    The length of the option.

+  @param[in]       Direction     The directionality in an SPD entry,

+                                 EfiIPsecInBound, or EfiIPsecOutBound.

+  @param[in]       Context       The token's wrap.

+

+  @retval EFI_SUCCESS            The IPsec protocol is not available or disabled.

+  @retval EFI_SUCCESS            The packet was bypassed, and all buffers remain the same.

+  @retval EFI_SUCCESS            The packet was protected.

+  @retval EFI_ACCESS_DENIED      The packet was discarded.

+  @retval EFI_OUT_OF_RESOURCES   There are not suffcient resources to complete the operation.

+  @retval EFI_BUFFER_TOO_SMALL   The number of non-empty blocks is bigger than the

+                                 number of input data blocks when building a fragment table.

+

+**/

+EFI_STATUS

+Ip6IpSecProcessPacket (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN OUT UINT8              *LastHead,

+  IN OUT NET_BUF            **Netbuf,

+  IN VOID                   *ExtHdrs,

+  IN UINT32                 ExtHdrsLen,

+  IN EFI_IPSEC_TRAFFIC_DIR  Direction,

+  IN VOID                   *Context

+  )

+{

+  NET_FRAGMENT              *FragmentTable;

+  UINT32                    FragmentCount;

+  EFI_EVENT                 RecycleEvent;

+  NET_BUF                   *Packet;

+  IP6_TXTOKEN_WRAP          *TxWrap;

+  IP6_IPSEC_WRAP            *IpSecWrap;

+  EFI_STATUS                Status;

+  EFI_IP6_HEADER            *PacketHead;

+  UINT8                     *Buf;

+

+  Status        = EFI_SUCCESS;

+  Packet        = *Netbuf;

+  RecycleEvent  = NULL;

+  IpSecWrap     = NULL;

+  FragmentTable = NULL;

+  PacketHead    = NULL;

+  Buf           = NULL;

+  TxWrap        = (IP6_TXTOKEN_WRAP *) Context;

+  FragmentCount = Packet->BlockOpNum;

+

+  if (mIpSec == NULL) {

+    gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &mIpSec);

+

+    //

+    // Check whether the ipsec protocol is available.

+    //

+    if (mIpSec == NULL) {

+      goto ON_EXIT;

+    }

+  }

+

+  //

+  // Check whether the ipsec enable variable is set.

+  //

+  if (mIpSec->DisabledFlag) {

+    //

+    // If IPsec is disabled, restore the original MTU

+    //

+    IpSb->MaxPacketSize = IpSb->OldMaxPacketSize;

+    goto ON_EXIT;

+  } else {

+    //

+    // If IPsec is enabled, use the MTU which reduce the IPsec header length.

+    //

+    IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP6_MAX_IPSEC_HEADLEN;

+  }

+

+

+  //

+  // Bypass all multicast inbound or outbound traffic.

+  //

+  if (IP6_IS_MULTICAST (&Head->DestinationAddress) || IP6_IS_MULTICAST (&Head->SourceAddress)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Rebuild fragment table from netbuf to ease ipsec process.

+  //

+  FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT));

+

+  if (FragmentTable == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount);

+

+  if (EFI_ERROR(Status)) {

+    FreePool (FragmentTable);

+    goto ON_EXIT;

+  }

+

+  //

+  // Convert host byte order to network byte order

+  //

+  Ip6NtohHead (Head);

+

+  Status = mIpSec->Process (

+                     mIpSec,

+                     IpSb->Controller,

+                     IP_VERSION_6,

+                     (VOID *) Head,

+                     LastHead,

+                     NULL,

+                     0,

+                     (EFI_IPSEC_FRAGMENT_DATA  **) (&FragmentTable),

+                     &FragmentCount,

+                     Direction,

+                     &RecycleEvent

+                     );

+  //

+  // Convert back to host byte order

+  //

+  Ip6NtohHead (Head);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  if (Direction == EfiIPsecOutBound && TxWrap != NULL) {

+

+    TxWrap->IpSecRecycleSignal = RecycleEvent;

+    TxWrap->Packet             = NetbufFromExt (

+                                   FragmentTable,

+                                   FragmentCount,

+                                   IP6_MAX_HEADLEN,

+                                   0,

+                                   Ip6FreeTxToken,

+                                   TxWrap

+                                   );

+    if (TxWrap->Packet == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_EXIT;

+    }

+

+    *Netbuf = TxWrap->Packet;

+

+  } else {

+

+    IpSecWrap = AllocateZeroPool (sizeof (IP6_IPSEC_WRAP));

+

+    if (IpSecWrap == NULL) {

+      goto ON_EXIT;

+    }

+

+    IpSecWrap->IpSecRecycleSignal = RecycleEvent;

+    IpSecWrap->Packet             = Packet;

+    Packet                        = NetbufFromExt (

+                                      FragmentTable,

+                                      FragmentCount,

+                                      IP6_MAX_HEADLEN,

+                                      0,

+                                      Ip6IpSecFree,

+                                      IpSecWrap

+                                      );

+

+    if (Packet == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_EXIT;

+    }

+

+    if (Direction == EfiIPsecInBound) {

+

+      PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (

+                                        Packet,

+                                        sizeof (EFI_IP6_HEADER) + ExtHdrsLen,

+                                        NET_BUF_HEAD

+                                        );

+      if (PacketHead == NULL) {

+        Status = EFI_OUT_OF_RESOURCES;

+        goto ON_EXIT;

+      }

+

+      CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));

+      Packet->Ip.Ip6 = PacketHead;

+

+      if (ExtHdrs != NULL) {

+        Buf = (UINT8 *) (PacketHead + 1);

+        CopyMem (Buf, ExtHdrs, ExtHdrsLen);

+      }

+

+      NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE);

+      CopyMem (

+        IP6_GET_CLIP_INFO (Packet),

+        IP6_GET_CLIP_INFO (IpSecWrap->Packet),

+        sizeof (IP6_CLIP_INFO)

+        );

+    }

+

+    *Netbuf = Packet;

+  }

+

+ON_EXIT:

+  return Status;

+}

+

+/**

+  The IP6 input routine. It is called by the IP6_INTERFACE when an

+  IP6 fragment is received from MNP.

+

+  @param[in]  Packet             The IP6 packet received.

+  @param[in]  IoStatus           The return status of receive request.

+  @param[in]  Flag               The link layer flag for the packet received, such

+                                 as multicast.

+  @param[in]  Context            The IP6 service instance that owns the MNP.

+

+**/

+VOID

+Ip6AcceptFrame (

+  IN NET_BUF                *Packet,

+  IN EFI_STATUS             IoStatus,

+  IN UINT32                 Flag,

+  IN VOID                   *Context

+  )

+{

+  IP6_SERVICE               *IpSb;

+  IP6_CLIP_INFO             *Info;

+  EFI_IP6_HEADER            *Head;

+  UINT16                    PayloadLen;

+  UINT8                     *Payload;

+  UINT16                    TotalLen;

+  UINT8                     *LastHead;

+  UINT32                    FormerHeadOffset;

+  UINT32                    UnFragmentLen;

+  UINT32                    ExtHdrsLen;

+  UINT32                    HeadLen;

+  BOOLEAN                   Fragmented;

+  IP6_FRAGMENT_HEADER       *FragmentHead;

+  UINT16                    FragmentOffset;

+  EFI_STATUS                Status;

+  EFI_IPv6_ADDRESS          Loopback;

+

+  IpSb = (IP6_SERVICE *) Context;

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  Payload = NULL;

+

+  //

+  // Check input parameters

+  //

+  if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) {

+    goto Drop;

+  }

+

+  //

+  // Check whether the input packet is a valid packet

+  //

+  if (Packet->TotalSize < IP6_MIN_HEADLEN) {

+    goto Restart;

+  }

+

+  //

+  // Get header information of the packet.

+  //

+  Head = (EFI_IP6_HEADER *) NetbufGetByte (Packet, 0, NULL);

+  if (Head == NULL) {

+    goto Restart;

+  }

+

+  //

+  // Multicast addresses must not be used as source addresses in IPv6 packets.

+  //

+  if ((Head->Version != 6) || (IP6_IS_MULTICAST (&Head->SourceAddress))) {

+    goto Restart;

+  }

+

+  //

+  // A packet with a destination address of loopback ::1/128 or unspecified must be dropped.

+  //

+  ZeroMem (&Loopback, sizeof (EFI_IPv6_ADDRESS));

+  Loopback.Addr[15] = 0x1;

+  if ((CompareMem (&Loopback, &Head->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) ||

+      (NetIp6IsUnspecifiedAddr (&Head->DestinationAddress))) {

+    goto Restart;

+  }

+

+  //

+  // Convert the IP header to host byte order.

+  //

+  Packet->Ip.Ip6 = Ip6NtohHead (Head);

+

+  //

+  // Get the per packet info.

+  //

+  Info           = IP6_GET_CLIP_INFO (Packet);

+  Info->LinkFlag = Flag;

+  Info->CastType = 0;

+

+  if (IpSb->MnpConfigData.EnablePromiscuousReceive) {

+    Info->CastType = Ip6Promiscuous;

+  }

+

+  if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {

+    Info->CastType = Ip6Unicast;

+  } else if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {

+    if (Ip6FindMldEntry (IpSb, &Head->DestinationAddress) != NULL) {

+      Info->CastType = Ip6Multicast;

+    }

+  }

+

+  //

+  // Drop the packet that is not delivered to us.

+  //

+  if (Info->CastType == 0) {

+    goto Restart;

+  }

+

+

+  PayloadLen = Head->PayloadLength;

+

+  Info->Start    = 0;

+  Info->Length   = PayloadLen;

+  Info->End      = Info->Start + Info->Length;

+  Info->HeadLen  = (UINT16) sizeof (EFI_IP6_HEADER);

+  Info->Status   = EFI_SUCCESS;

+  Info->LastFrag = FALSE;

+

+  TotalLen   = (UINT16) (PayloadLen + sizeof (EFI_IP6_HEADER));

+

+  //

+  // Mnp may deliver frame trailer sequence up, trim it off.

+  //

+  if (TotalLen < Packet->TotalSize) {

+    NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE);

+  }

+

+  if (TotalLen != Packet->TotalSize) {

+    goto Restart;

+  }

+

+  //

+  // Check the extension headers, if exist validate them

+  //

+  if (PayloadLen != 0) {

+    Payload = AllocatePool ((UINTN) PayloadLen);

+    if (Payload == NULL) {

+      goto Restart;

+    }

+

+    NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);

+  }

+

+  LastHead   = NULL;

+  if (!Ip6IsExtsValid (

+         IpSb,

+         Packet,

+         &Head->NextHeader,

+         Payload,

+         (UINT32) PayloadLen,

+         TRUE,

+         &FormerHeadOffset,

+         &LastHead,

+         &ExtHdrsLen,

+         &UnFragmentLen,

+         &Fragmented

+         )) {

+    goto Restart;

+  }

+

+  HeadLen        = sizeof (EFI_IP6_HEADER) + UnFragmentLen;

+

+  if (Fragmented) {

+    //

+    // Get the fragment offset from the Fragment header

+    //

+    FragmentHead = (IP6_FRAGMENT_HEADER *) NetbufGetByte (Packet, HeadLen, NULL);

+    if (FragmentHead == NULL) {

+      goto Restart;

+    }

+

+    FragmentOffset = NTOHS (FragmentHead->FragmentOffset);

+

+    if ((FragmentOffset & 0x1) == 0) {

+      Info->LastFrag = TRUE;

+    }

+

+    FragmentOffset &= (~0x1);

+

+    //

+    // This is the first fragment of the packet

+    //

+    if (FragmentOffset == 0) {

+      Info->NextHeader = FragmentHead->NextHeader;

+    }

+

+    Info->HeadLen          = (UINT16) HeadLen;

+    HeadLen                += sizeof (IP6_FRAGMENT_HEADER);

+    Info->Start            = FragmentOffset;

+    Info->Length           = TotalLen - (UINT16) HeadLen;

+    Info->End              = Info->Start + Info->Length;

+    Info->Id               = FragmentHead->Identification;

+    Info->FormerNextHeader = FormerHeadOffset;

+

+    //

+    // Fragments should in the unit of 8 octets long except the last one.

+    //

+    if ((Info->LastFrag == 0) && (Info->Length % 8 != 0)) {

+      goto Restart;

+    }

+

+    //

+    // Reassemble the packet.

+    //

+    Packet = Ip6Reassemble (&IpSb->Assemble, Packet);

+    if (Packet == NULL) {

+      goto Restart;

+    }

+

+    //

+    // Re-check the assembled packet to get the right values.

+    //

+    Head       = Packet->Ip.Ip6;

+    PayloadLen = Head->PayloadLength;

+    if (PayloadLen != 0) {

+      if (Payload != NULL) {

+        FreePool (Payload);

+      }

+

+      Payload = AllocatePool ((UINTN) PayloadLen);

+      if (Payload == NULL) {

+        goto Restart;

+      }

+

+      NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);

+    }

+

+    if (!Ip6IsExtsValid (

+           IpSb,

+           Packet,

+           &Head->NextHeader,

+           Payload,

+           (UINT32) PayloadLen,

+           TRUE,

+           NULL,

+           &LastHead,

+           &ExtHdrsLen,

+           &UnFragmentLen,

+           &Fragmented

+           )) {

+      goto Restart;

+    }

+  }

+

+  //

+  // Trim the head off, after this point, the packet is headless.

+  // and Packet->TotalLen == Info->Length.

+  //

+  NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE);

+

+  //

+  // After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer,

+  // and no need consider any other ahead ext headers.

+  //

+  Status = Ip6IpSecProcessPacket (

+             IpSb,

+             Head,

+             LastHead, // need get the lasthead value for input

+             &Packet,

+             NULL,

+             0,

+             EfiIPsecInBound,

+             NULL

+             );

+

+  if (EFI_ERROR(Status)) {

+    goto Restart;

+  }

+

+  //

+  // TODO: may check the last head again, the same as the output routine

+  //

+

+  //

+  // Packet may have been changed. The ownership of the packet

+  // is transfered to the packet process logic.

+  //

+  Head  = Packet->Ip.Ip6;

+  IP6_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;

+

+  switch (*LastHead) {

+  case IP6_ICMP:

+    Ip6IcmpHandle (IpSb, Head, Packet);

+    break;

+  default:

+    Ip6Demultiplex (IpSb, Head, Packet);

+  }

+

+  Packet = NULL;

+

+  //

+  // Dispatch the DPCs queued by the NotifyFunction of the rx token's events

+  // which are signaled with received data.

+  //

+  DispatchDpc ();

+

+Restart:

+  if (Payload != NULL) {

+    FreePool (Payload);

+  }

+

+  Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);

+

+Drop:

+  if (Packet != NULL) {

+    NetbufFree (Packet);

+  }

+

+  return ;

+}

+

+/**

+  Initialize an already allocated assemble table. This is generally

+  the assemble table embedded in the IP6 service instance.

+

+  @param[in, out]  Table    The assemble table to initialize.

+

+**/

+VOID

+Ip6CreateAssembleTable (

+  IN OUT IP6_ASSEMBLE_TABLE *Table

+  )

+{

+  UINT32                    Index;

+

+  for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {

+    InitializeListHead (&Table->Bucket[Index]);

+  }

+}

+

+/**

+  Clean up the assemble table by removing all of the fragments

+  and assemble entries.

+

+  @param[in, out]  Table    The assemble table to clean up.

+

+**/

+VOID

+Ip6CleanAssembleTable (

+  IN OUT IP6_ASSEMBLE_TABLE *Table

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_ASSEMBLE_ENTRY        *Assemble;

+  UINT32                    Index;

+

+  for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {

+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {

+      Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);

+

+      RemoveEntryList (Entry);

+      Ip6FreeAssembleEntry (Assemble);

+    }

+  }

+}

+

+

+/**

+  The signal handle of IP6's recycle event. It is called back

+  when the upper layer releases the packet.

+

+  @param[in]  Event         The IP6's recycle event.

+  @param[in]  Context       The context of the handle, which is a IP6_RXDATA_WRAP.

+

+**/

+VOID

+EFIAPI

+Ip6OnRecyclePacket (

+  IN EFI_EVENT              Event,

+  IN VOID                   *Context

+  )

+{

+  IP6_RXDATA_WRAP           *Wrap;

+

+  Wrap = (IP6_RXDATA_WRAP *) Context;

+

+  EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock);

+  RemoveEntryList (&Wrap->Link);

+  EfiReleaseLock (&Wrap->IpInstance->RecycleLock);

+

+  ASSERT (!NET_BUF_SHARED (Wrap->Packet));

+  NetbufFree (Wrap->Packet);

+

+  gBS->CloseEvent (Wrap->RxData.RecycleSignal);

+  FreePool (Wrap);

+}

+

+/**

+  Wrap the received packet to a IP6_RXDATA_WRAP, which will be

+  delivered to the upper layer. Each IP6 child that accepts the

+  packet will get a not-shared copy of the packet which is wrapped

+  in the IP6_RXDATA_WRAP. The IP6_RXDATA_WRAP->RxData is passed

+  to the upper layer. The upper layer will signal the recycle event in

+  it when it is done with the packet.

+

+  @param[in]  IpInstance    The IP6 child to receive the packet.

+  @param[in]  Packet        The packet to deliver up.

+

+  @return NULL if it failed to wrap the packet; otherwise, the wrapper.

+

+**/

+IP6_RXDATA_WRAP *

+Ip6WrapRxData (

+  IN IP6_PROTOCOL           *IpInstance,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_RXDATA_WRAP           *Wrap;

+  EFI_IP6_RECEIVE_DATA      *RxData;

+  EFI_STATUS                Status;

+

+  Wrap = AllocatePool (IP6_RXDATA_WRAP_SIZE (Packet->BlockOpNum));

+

+  if (Wrap == NULL) {

+    return NULL;

+  }

+

+  InitializeListHead (&Wrap->Link);

+

+  Wrap->IpInstance  = IpInstance;

+  Wrap->Packet      = Packet;

+  RxData            = &Wrap->RxData;

+

+  ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  Ip6OnRecyclePacket,

+                  Wrap,

+                  &RxData->RecycleSignal

+                  );

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Wrap);

+    return NULL;

+  }

+

+  ASSERT (Packet->Ip.Ip6 != NULL);

+

+  //

+  // The application expects a network byte order header.

+  //

+  RxData->HeaderLength  = sizeof (EFI_IP6_HEADER);

+  RxData->Header        = (EFI_IP6_HEADER *) Ip6NtohHead (Packet->Ip.Ip6);

+  RxData->DataLength    = Packet->TotalSize;

+

+  //

+  // Build the fragment table to be delivered up.

+  //

+  RxData->FragmentCount = Packet->BlockOpNum;

+  NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);

+

+  return Wrap;

+}

+

+/**

+  Check whether this IP child accepts the packet.

+

+  @param[in]  IpInstance    The IP child to check.

+  @param[in]  Head          The IP header of the packet.

+  @param[in]  Packet        The data of the packet.

+

+  @retval     TRUE          The child wants to receive the packet.

+  @retval     FALSE         The child does not want to receive the packet.

+

+**/

+BOOLEAN

+Ip6InstanceFrameAcceptable (

+  IN IP6_PROTOCOL           *IpInstance,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_ERROR_HEAD       Icmp;

+  EFI_IP6_CONFIG_DATA       *Config;

+  IP6_CLIP_INFO             *Info;

+  UINT8                     *Proto;

+  UINT32                    Index;

+  UINT8                     *ExtHdrs;

+  UINT16                    ErrMsgPayloadLen;

+  UINT8                     *ErrMsgPayload;

+

+  Config = &IpInstance->ConfigData;

+  Proto  = NULL;

+

+  //

+  // Dirty trick for the Tiano UEFI network stack implmentation. If

+  // ReceiveTimeout == -1, the receive of the packet for this instance

+  // is disabled. The UEFI spec don't have such captibility. We add

+  // this to improve the performance because IP will make a copy of

+  // the received packet for each accepting instance. Some IP instances

+  // used by UDP/TCP only send packets, they don't wants to receive.

+  //

+  if (Config->ReceiveTimeout == (UINT32)(-1)) {

+    return FALSE;

+  }

+

+  if (Config->AcceptPromiscuous) {

+    return TRUE;

+  }

+

+  //

+  // Check whether the protocol is acceptable.

+  //

+  ExtHdrs = NetbufGetByte (Packet, 0, NULL);

+

+  if (!Ip6IsExtsValid (

+         IpInstance->Service,

+         Packet,

+         &Head->NextHeader,

+         ExtHdrs,

+         (UINT32) Head->PayloadLength,

+         TRUE,

+         NULL,

+         &Proto,

+         NULL,

+         NULL,

+         NULL

+         )) {

+    return FALSE;

+  }

+

+  //

+  // The upper layer driver may want to receive the ICMPv6 error packet

+  // invoked by its packet, like UDP.

+  //

+  if ((*Proto == IP6_ICMP) && (!Config->AcceptAnyProtocol) && (*Proto != Config->DefaultProtocol)) {

+    NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

+

+    if (Icmp.Head.Type <= ICMP_V6_ERROR_MAX) {

+      if (!Config->AcceptIcmpErrors) {

+        return FALSE;

+      }

+

+      //

+      // Get the protocol of the invoking packet of ICMPv6 error packet.

+      //

+      ErrMsgPayloadLen = NTOHS (Icmp.IpHead.PayloadLength);

+      ErrMsgPayload    = NetbufGetByte (Packet, sizeof (Icmp), NULL);

+

+      if (!Ip6IsExtsValid (

+             NULL,

+             NULL,

+             &Icmp.IpHead.NextHeader,

+             ErrMsgPayload,

+             ErrMsgPayloadLen,

+             TRUE,

+             NULL,

+             &Proto,

+             NULL,

+             NULL,

+             NULL

+             )) {

+        return FALSE;

+      }

+    }

+  }

+

+  //

+  // Match the protocol

+  //

+  if (!Config->AcceptAnyProtocol && (*Proto != Config->DefaultProtocol)) {

+    return FALSE;

+  }

+

+  //

+  // Check for broadcast, the caller has computed the packet's

+  // cast type for this child's interface.

+  //

+  Info = IP6_GET_CLIP_INFO (Packet);

+

+  //

+  // If it is a multicast packet, check whether we are in the group.

+  //

+  if (Info->CastType == Ip6Multicast) {

+    //

+    // Receive the multicast if the instance wants to receive all packets.

+    //

+    if (NetIp6IsUnspecifiedAddr (&IpInstance->ConfigData.StationAddress)) {

+      return TRUE;

+    }

+

+    for (Index = 0; Index < IpInstance->GroupCount; Index++) {

+      if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, &Head->DestinationAddress)) {

+        break;

+      }

+    }

+

+    return (BOOLEAN)(Index < IpInstance->GroupCount);

+  }

+

+  return TRUE;

+}

+

+/**

+  Enqueue a shared copy of the packet to the IP6 child if the

+  packet is acceptable to it. Here the data of the packet is

+  shared, but the net buffer isn't.

+

+  @param  IpInstance             The IP6 child to enqueue the packet to.

+  @param  Head                   The IP header of the received packet.

+  @param  Packet                 The data of the received packet.

+

+  @retval EFI_NOT_STARTED        The IP child hasn't been configured.

+  @retval EFI_INVALID_PARAMETER  The child doesn't want to receive the packet.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources

+  @retval EFI_SUCCESS            A shared copy the packet is enqueued to the child.

+

+**/

+EFI_STATUS

+Ip6InstanceEnquePacket (

+  IN IP6_PROTOCOL           *IpInstance,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_CLIP_INFO             *Info;

+  NET_BUF                   *Clone;

+

+  //

+  // Check whether the packet is acceptable to this instance.

+  //

+  if (IpInstance->State != IP6_STATE_CONFIGED) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (!Ip6InstanceFrameAcceptable (IpInstance, Head, Packet)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Enque a shared copy of the packet.

+  //

+  Clone = NetbufClone (Packet);

+

+  if (Clone == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Set the receive time out for the assembled packet. If it expires,

+  // packet will be removed from the queue.

+  //

+  Info        = IP6_GET_CLIP_INFO (Clone);

+  Info->Life  = IP6_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);

+

+  InsertTailList (&IpInstance->Received, &Clone->List);

+  return EFI_SUCCESS;

+}

+

+/**

+  Deliver the received packets to the upper layer if there are both received

+  requests and enqueued packets. If the enqueued packet is shared, it will

+  duplicate it to a non-shared packet, release the shared packet, then

+  deliver the non-shared packet up.

+

+  @param[in]  IpInstance         The IP child to deliver the packet up.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to deliver the

+                                 packets.

+  @retval EFI_SUCCESS            All the enqueued packets that can be delivered

+                                 are delivered up.

+

+**/

+EFI_STATUS

+Ip6InstanceDeliverPacket (

+  IN IP6_PROTOCOL           *IpInstance

+  )

+{

+  EFI_IP6_COMPLETION_TOKEN  *Token;

+  IP6_RXDATA_WRAP           *Wrap;

+  NET_BUF                   *Packet;

+  NET_BUF                   *Dup;

+  UINT8                     *Head;

+

+  //

+  // Deliver a packet if there are both a packet and a receive token.

+  //

+  while (!IsListEmpty (&IpInstance->Received) && !NetMapIsEmpty (&IpInstance->RxTokens)) {

+

+    Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);

+

+    if (!NET_BUF_SHARED (Packet)) {

+      //

+      // If this is the only instance that wants the packet, wrap it up.

+      //

+      Wrap = Ip6WrapRxData (IpInstance, Packet);

+

+      if (Wrap == NULL) {

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      RemoveEntryList (&Packet->List);

+

+    } else {

+      //

+      // Create a duplicated packet if this packet is shared

+      //

+      Dup = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));

+

+      if (Dup == NULL) {

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      //

+      // Copy the IP head over. The packet to deliver up is

+      // headless. Trim the head off after copy. The IP head

+      // may be not continuous before the data.

+      //

+      Head        = NetbufAllocSpace (Dup, sizeof (EFI_IP6_HEADER), NET_BUF_HEAD);

+      ASSERT (Head != NULL);

+      Dup->Ip.Ip6 = (EFI_IP6_HEADER *) Head;

+

+      CopyMem (Head, Packet->Ip.Ip6, sizeof (EFI_IP6_HEADER));

+      NetbufTrim (Dup, sizeof (EFI_IP6_HEADER), TRUE);

+

+      Wrap = Ip6WrapRxData (IpInstance, Dup);

+

+      if (Wrap == NULL) {

+        NetbufFree (Dup);

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      RemoveEntryList (&Packet->List);

+      NetbufFree (Packet);

+

+      Packet = Dup;

+    }

+

+    //

+    // Insert it into the delivered packet, then get a user's

+    // receive token, pass the wrapped packet up.

+    //

+    EfiAcquireLockOrFail (&IpInstance->RecycleLock);

+    InsertHeadList (&IpInstance->Delivered, &Wrap->Link);

+    EfiReleaseLock (&IpInstance->RecycleLock);

+

+    Token                = NetMapRemoveHead (&IpInstance->RxTokens, NULL);

+    Token->Status        = IP6_GET_CLIP_INFO (Packet)->Status;

+    Token->Packet.RxData = &Wrap->RxData;

+

+    gBS->SignalEvent (Token->Event);

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Enqueue a received packet to all the IP children that share

+  the same interface.

+

+  @param[in]  IpSb          The IP6 service instance that receive the packet.

+  @param[in]  Head          The header of the received packet.

+  @param[in]  Packet        The data of the received packet.

+  @param[in]  IpIf          The interface to enqueue the packet to.

+

+  @return The number of the IP6 children that accepts the packet.

+

+**/

+INTN

+Ip6InterfaceEnquePacket (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet,

+  IN IP6_INTERFACE          *IpIf

+  )

+{

+  IP6_PROTOCOL              *IpInstance;

+  IP6_CLIP_INFO             *Info;

+  LIST_ENTRY                *Entry;

+  INTN                      Enqueued;

+  INTN                      LocalType;

+  INTN                      SavedType;

+

+  //

+  // First, check that the packet is acceptable to this interface

+  // and find the local cast type for the interface.

+  //

+  LocalType = 0;

+  Info      = IP6_GET_CLIP_INFO (Packet);

+

+  if (IpIf->PromiscRecv) {

+    LocalType = Ip6Promiscuous;

+  } else {

+    LocalType = Info->CastType;

+  }

+

+  //

+  // Iterate through the ip instances on the interface, enqueue

+  // the packet if filter passed. Save the original cast type,

+  // and pass the local cast type to the IP children on the

+  // interface. The global cast type will be restored later.

+  //

+  SavedType       = Info->CastType;

+  Info->CastType  = (UINT32) LocalType;

+

+  Enqueued        = 0;

+

+  NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {

+    IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);

+    NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);

+

+    if (Ip6InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {

+      Enqueued++;

+    }

+  }

+

+  Info->CastType = (UINT32) SavedType;

+  return Enqueued;

+}

+

+/**

+  Deliver the packet for each IP6 child on the interface.

+

+  @param[in]  IpSb          The IP6 service instance that received the packet.

+  @param[in]  IpIf          The IP6 interface to deliver the packet.

+

+**/

+VOID

+Ip6InterfaceDeliverPacket (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_INTERFACE          *IpIf

+  )

+{

+  IP6_PROTOCOL              *IpInstance;

+  LIST_ENTRY                *Entry;

+

+  NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {

+    IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);

+    Ip6InstanceDeliverPacket (IpInstance);

+  }

+}

+

+/**

+  De-multiplex the packet. the packet delivery is processed in two

+  passes. The first pass will enqueue a shared copy of the packet

+  to each IP6 child that accepts the packet. The second pass will

+  deliver a non-shared copy of the packet to each IP6 child that

+  has pending receive requests. Data is copied if more than one

+  child wants to consume the packet, because each IP child needs

+  its own copy of the packet to make changes.

+

+  @param[in]  IpSb          The IP6 service instance that received the packet.

+  @param[in]  Head          The header of the received packet.

+  @param[in]  Packet        The data of the received packet.

+

+  @retval EFI_NOT_FOUND     No IP child accepts the packet.

+  @retval EFI_SUCCESS       The packet is enqueued or delivered to some IP

+                            children.

+

+**/

+EFI_STATUS

+Ip6Demultiplex (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+

+  LIST_ENTRY                *Entry;

+  IP6_INTERFACE             *IpIf;

+  INTN                      Enqueued;

+

+  //

+  // Two pass delivery: first, enque a shared copy of the packet

+  // to each instance that accept the packet.

+  //

+  Enqueued = 0;

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);

+

+    if (IpIf->Configured) {

+      Enqueued += Ip6InterfaceEnquePacket (IpSb, Head, Packet, IpIf);

+    }

+  }

+

+  //

+  // Second: deliver a duplicate of the packet to each instance.

+  // Release the local reference first, so that the last instance

+  // getting the packet will not copy the data.

+  //

+  NetbufFree (Packet);

+  Packet = NULL;

+

+  if (Enqueued == 0) {

+    return EFI_NOT_FOUND;

+  }

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);

+

+    if (IpIf->Configured) {

+      Ip6InterfaceDeliverPacket (IpSb, IpIf);

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Decrease the life of the transmitted packets. If it is

+  decreased to zero, cancel the packet. This function is

+  called by Ip6packetTimerTicking that provides timeout for both the

+  received-but-not-delivered and transmitted-but-not-recycle

+  packets.

+

+  @param[in]  Map           The IP6 child's transmit map.

+  @param[in]  Item          Current transmitted packet.

+  @param[in]  Context       Not used.

+

+  @retval EFI_SUCCESS       Always returns EFI_SUCCESS.

+

+**/

+EFI_STATUS

+EFIAPI

+Ip6SentPacketTicking (

+  IN NET_MAP                *Map,

+  IN NET_MAP_ITEM           *Item,

+  IN VOID                   *Context

+  )

+{

+  IP6_TXTOKEN_WRAP          *Wrap;

+

+  Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;

+  ASSERT (Wrap != NULL);

+

+  if ((Wrap->Life > 0) && (--Wrap->Life == 0)) {

+    Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Timeout the fragments, and the enqueued, and transmitted packets.

+

+  @param[in]  IpSb          The IP6 service instance to timeout.

+

+**/

+VOID

+Ip6PacketTimerTicking (

+  IN IP6_SERVICE            *IpSb

+  )

+{

+  LIST_ENTRY                *InstanceEntry;

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_PROTOCOL              *IpInstance;

+  IP6_ASSEMBLE_ENTRY        *Assemble;

+  NET_BUF                   *Packet;

+  IP6_CLIP_INFO             *Info;

+  UINT32                    Index;

+

+  //

+  // First, time out the fragments. The packet's life is counting down

+  // once the first-arriving fragment of that packet was received.

+  //

+  for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {

+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &(IpSb->Assemble.Bucket[Index])) {

+      Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);

+

+      if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {

+        //

+        // If the first fragment (the one with a Fragment Offset of zero)

+        // has been received, an ICMP Time Exceeded - Fragment Reassembly

+        // Time Exceeded message should be sent to the source of that fragment.

+        //

+        if ((Assemble->Packet != NULL) &&

+            !IP6_IS_MULTICAST (&Assemble->Head->DestinationAddress)) {

+          Ip6SendIcmpError (

+            IpSb,

+            Assemble->Packet,

+            NULL,

+            &Assemble->Head->SourceAddress,

+            ICMP_V6_TIME_EXCEEDED,

+            ICMP_V6_TIMEOUT_REASSEMBLE,

+            NULL

+            );

+        }

+

+        //

+        // If reassembly of a packet is not completed within 60 seconds of

+        // the reception of the first-arriving fragment of that packet, the

+        // reassembly must be abandoned and all the fragments that have been

+        // received for that packet must be discarded.

+        //

+        RemoveEntryList (Entry);

+        Ip6FreeAssembleEntry (Assemble);

+      }

+    }

+  }

+

+  NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {

+    IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP6_PROTOCOL, Link);

+

+    //

+    // Second, time out the assembled packets enqueued on each IP child.

+    //

+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {

+      Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);

+      Info   = IP6_GET_CLIP_INFO (Packet);

+

+      if ((Info->Life > 0) && (--Info->Life == 0)) {

+        RemoveEntryList (Entry);

+        NetbufFree (Packet);

+      }

+    }

+

+    //

+    // Third: time out the transmitted packets.

+    //

+    NetMapIterate (&IpInstance->TxTokens, Ip6SentPacketTicking, NULL);

+  }

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Input.h b/NetworkPkg/Ip6Dxe/Ip6Input.h
new file mode 100644
index 0000000..8594896
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Input.h
@@ -0,0 +1,235 @@
+/** @file

+  IP6 internal functions and definitions to process the incoming packets.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_INPUT_H__

+#define __EFI_IP6_INPUT_H__

+

+#define IP6_MIN_HEADLEN       40

+#define IP6_MAX_HEADLEN       120

+///

+/// 8(ESP header) + 16(max IV) + 16(max padding) + 2(ESP tail) + 12(max ICV) = 54

+///

+#define IP6_MAX_IPSEC_HEADLEN 54

+

+

+#define IP6_ASSEMLE_HASH_SIZE 127

+///

+/// Lift time in seconds.

+///

+#define IP6_FRAGMENT_LIFE     60

+#define IP6_MAX_PACKET_SIZE   65535

+

+

+#define IP6_GET_CLIP_INFO(Packet) ((IP6_CLIP_INFO *) ((Packet)->ProtoData))

+

+#define IP6_ASSEMBLE_HASH(Dst, Src, Id)  \

+          ((*((UINT32 *) (Dst)) + *((UINT32 *) (Src)) + (Id)) % IP6_ASSEMLE_HASH_SIZE)

+

+#define IP6_RXDATA_WRAP_SIZE(NumFrag) \

+          (sizeof (IP6_RXDATA_WRAP) + sizeof (EFI_IP6_FRAGMENT_DATA) * ((NumFrag) - 1))

+

+//

+// Per packet information for input process. LinkFlag specifies whether

+// the packet is received as Link layer unicast, multicast or broadcast.

+// The CastType is the IP layer cast type, such as IP multicast or unicast.

+// Start, End and Length are staffs used to assemble the packets. Start

+// is the sequence number of the first byte of data in the packet. Length

+// is the number of bytes of data. End = Start + Length, that is, the

+// sequence number of last byte + 1. Each assembled packet has a count down

+// life. If it isn't consumed before Life reaches zero, the packet is released.

+//

+typedef struct {

+  UINT32                    LinkFlag;

+  INT32                     CastType;

+  INT32                     Start;

+  INT32                     End;

+  INT32                     Length;

+  UINT32                    Life;

+  EFI_STATUS                Status;

+  UINT32                    Id;

+  UINT16                    HeadLen;

+  UINT8                     NextHeader;

+  UINT8                     LastFrag;

+  UINT32                    FormerNextHeader;

+} IP6_CLIP_INFO;

+

+//

+// Structure used to assemble IP packets.

+//

+typedef struct {

+  LIST_ENTRY                Link;

+  LIST_ENTRY                Fragments;  // List of all the fragments of this packet

+

+  //

+  // Identity of one IP6 packet. Each fragment of a packet has

+  // the same (Dst, Src, Id).

+  //

+  EFI_IPv6_ADDRESS          Dst;

+  EFI_IPv6_ADDRESS          Src;

+  UINT32                    Id;

+

+  UINT32                    TotalLen;

+  UINT32                    CurLen;

+  UINT32                    Life;       // Count down life for the packet.

+

+  EFI_IP6_HEADER            *Head;      // IP head of the first fragment

+  IP6_CLIP_INFO             *Info;      // Per packet information of the first fragment

+  NET_BUF                   *Packet;    // The first fragment of the packet

+} IP6_ASSEMBLE_ENTRY;

+

+//

+// Each Ip service instance has an assemble table to reassemble

+// the packets before delivery to its children. It is organized

+// as hash table.

+//

+typedef struct {

+  LIST_ENTRY  Bucket[IP6_ASSEMLE_HASH_SIZE];

+} IP6_ASSEMBLE_TABLE;

+

+/**

+  The IP6 input routine. It is called by the IP6_INTERFACE when an

+  IP6 fragment is received from MNP.

+

+  @param[in]  Packet             The IP6 packet received.

+  @param[in]  IoStatus           The return status of receive request.

+  @param[in]  Flag               The link layer flag for the packet received, such

+                                 as multicast.

+  @param[in]  Context            The IP6 service instance that own the MNP.

+

+**/

+VOID

+Ip6AcceptFrame (

+  IN NET_BUF                *Packet,

+  IN EFI_STATUS             IoStatus,

+  IN UINT32                 Flag,

+  IN VOID                   *Context

+  );

+

+/**

+  Deliver the received packets to upper layer if there are both received

+  requests and enqueued packets. If the enqueued packet is shared, it will

+  duplicate it to a non-shared packet, release the shared packet, then

+  deliver the non-shared packet up.

+

+  @param[in]  IpInstance         The IP child to deliver the packet up.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to deliver the

+                                 packets.

+  @retval EFI_SUCCESS            All the enqueued packets that can be delivered

+                                 are delivered up.

+

+**/

+EFI_STATUS

+Ip6InstanceDeliverPacket (

+  IN IP6_PROTOCOL           *IpInstance

+  );

+

+/**

+  The work function to locate IPsec protocol to process the inbound or

+  outbound IP packets. The process routine handls the packet with the following

+  actions: bypass the packet, discard the packet, or protect the packet.

+

+  @param[in]       IpSb          The IP6 service instance.

+  @param[in]       Head          The caller supplied IP6 header.

+  @param[in, out]  LastHead      The next header field of last IP header.

+  @param[in, out]  Netbuf        The IP6 packet to be processed by IPsec.

+  @param[in]       ExtHdrs       The caller supplied options.

+  @param[in]       ExtHdrsLen    The length of the option.

+  @param[in]       Direction     The directionality in an SPD entry,

+                                 EfiIPsecInBound or EfiIPsecOutBound.

+  @param[in]       Context       The token's wrap.

+

+  @retval EFI_SUCCESS            The IPsec protocol is not available or disabled.

+  @retval EFI_SUCCESS            The packet was bypassed and all buffers remain the same.

+  @retval EFI_SUCCESS            The packet was protected.

+  @retval EFI_ACCESS_DENIED      The packet was discarded.

+  @retval EFI_OUT_OF_RESOURCES   There are not suffcient resources to complete the operation.

+  @retval EFI_BUFFER_TOO_SMALL   The number of non-empty block is bigger than the

+                                 number of input data blocks when building a fragment table.

+

+**/

+EFI_STATUS

+Ip6IpSecProcessPacket (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN OUT UINT8              *LastHead,

+  IN OUT NET_BUF            **Netbuf,

+  IN VOID                   *ExtHdrs,

+  IN UINT32                 ExtHdrsLen,

+  IN EFI_IPSEC_TRAFFIC_DIR  Direction,

+  IN VOID                   *Context

+  );

+

+/**

+  Initialize an already allocated assemble table. This is generally

+  the assemble table embedded in the IP6 service instance.

+

+  @param[in, out]  Table    The assemble table to initialize.

+

+**/

+VOID

+Ip6CreateAssembleTable (

+  IN OUT IP6_ASSEMBLE_TABLE *Table

+  );

+

+/**

+  Clean up the assemble table: remove all the fragments

+  and assemble entries.

+

+  @param[in, out]  Table    The assemble table to clean up.

+

+**/

+VOID

+Ip6CleanAssembleTable (

+  IN OUT IP6_ASSEMBLE_TABLE *Table

+  );

+

+/**

+  Demultiple the packet. the packet delivery is processed in two

+  passes. The first pass will enque a shared copy of the packet

+  to each IP6 child that accepts the packet. The second pass will

+  deliver a non-shared copy of the packet to each IP6 child that

+  has pending receive requests. Data is copied if more than one

+  child wants to consume the packet bacause each IP child need

+  its own copy of the packet to make changes.

+

+  @param[in]  IpSb          The IP6 service instance that received the packet.

+  @param[in]  Head          The header of the received packet.

+  @param[in]  Packet        The data of the received packet.

+

+  @retval EFI_NOT_FOUND     No IP child accepts the packet.

+  @retval EFI_SUCCESS       The packet is enqueued or delivered to some IP

+                            children.

+

+**/

+EFI_STATUS

+Ip6Demultiplex (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  );

+

+/**

+  Timeout the fragmented, enqueued, and transmitted packets.

+

+  @param[in]  IpSb          The IP6 service instance to timeout.

+

+**/

+VOID

+Ip6PacketTimerTicking (

+  IN IP6_SERVICE            *IpSb

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6Mld.c b/NetworkPkg/Ip6Dxe/Ip6Mld.c
new file mode 100644
index 0000000..4a418fa
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Mld.c
@@ -0,0 +1,908 @@
+/** @file

+  Multicast Listener Discovery support routines.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+/**

+  Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data.

+

+  @param[in, out]  IpSb          Points to IP6 service binding instance.

+  @param[in]       MulticastAddr The IPv6 multicast address to be recorded.

+  @param[in]       DelayTimer    The maximum allowed delay before sending a responding

+                                 report, in units of milliseconds.

+  @return The created IP6_ML_GROUP list entry or NULL.

+

+**/

+IP6_MLD_GROUP *

+Ip6CreateMldEntry (

+  IN OUT IP6_SERVICE        *IpSb,

+  IN EFI_IPv6_ADDRESS       *MulticastAddr,

+  IN UINT32                 DelayTimer

+  )

+{

+  IP6_MLD_GROUP             *Entry;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));

+

+  Entry = AllocatePool (sizeof (IP6_MLD_GROUP));

+  if (Entry != NULL) {

+    Entry->RefCnt     = 1;

+    Entry->DelayTimer = DelayTimer;

+    Entry->SendByUs   = FALSE;

+    IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr);

+    InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link);

+  }

+

+  return Entry;

+}

+

+/**

+  Search a IP6_MLD_GROUP list entry node from a list array.

+

+  @param[in]       IpSb          Points to IP6 service binding instance.

+  @param[in]       MulticastAddr The IPv6 multicast address to be searched.

+

+  @return The found IP6_ML_GROUP list entry or NULL.

+

+**/

+IP6_MLD_GROUP *

+Ip6FindMldEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *MulticastAddr

+  )

+{

+  LIST_ENTRY                *Entry;

+  IP6_MLD_GROUP             *Group;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {

+    Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);

+    if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) {

+      return Group;

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  Count the number of IP6 multicast groups that are mapped to the

+  same MAC address. Several IP6 multicast address may be mapped to

+  the same MAC address.

+

+  @param[in]  MldCtrl              The MLD control block to search in.

+  @param[in]  Mac                  The MAC address to search.

+

+  @return The number of the IP6 multicast group that mapped to the same

+          multicast group Mac.

+

+**/

+INTN

+Ip6FindMac (

+  IN IP6_MLD_SERVICE_DATA   *MldCtrl,

+  IN EFI_MAC_ADDRESS        *Mac

+  )

+{

+  LIST_ENTRY                *Entry;

+  IP6_MLD_GROUP             *Group;

+  INTN                      Count;

+

+  Count = 0;

+

+  NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) {

+    Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);

+

+    if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {

+      Count++;

+    }

+  }

+

+  return Count;

+}

+

+/**

+  Generate MLD report message and send it out to MulticastAddr.

+

+  @param[in]  IpSb               The IP service to send the packet.

+  @param[in]  Interface          The IP interface to send the packet.

+                                 If NULL, a system interface will be selected.

+  @param[in]  MulticastAddr      The specific IPv6 multicast address to which

+                                 the message sender is listening.

+

+  @retval EFI_OUT_OF_RESOURCES   There are not sufficient resources to complete the

+                                 operation.

+  @retval EFI_SUCCESS            The MLD report message was successfully sent out.

+

+**/

+EFI_STATUS

+Ip6SendMldReport (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_INTERFACE          *Interface OPTIONAL,

+  IN EFI_IPv6_ADDRESS       *MulticastAddr

+  )

+{

+  IP6_MLD_HEAD              *MldHead;

+  NET_BUF                   *Packet;

+  EFI_IP6_HEADER            Head;

+  UINT16                    PayloadLen;

+  UINTN                     OptionLen;

+  UINT8                     *Options;

+  EFI_STATUS                Status;

+  UINT16                    HeadChecksum;

+  UINT16                    PseudoChecksum;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));

+

+  //

+  // Generate the packet to be sent

+  // IPv6 basic header + Hop by Hop option + MLD message

+  //

+

+  OptionLen = 0;

+  Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);

+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);

+

+  PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));

+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Create the basic IPv6 header.

+  // RFC3590: Use link-local address as source address if it is available,

+  // otherwise use the unspecified address.

+  //

+  Head.FlowLabelL     = 0;

+  Head.FlowLabelH     = 0;

+  Head.PayloadLength  = HTONS (PayloadLen);

+  Head.NextHeader     = IP6_HOP_BY_HOP;

+  Head.HopLimit       = 1;

+  IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr);

+

+  //

+  // If Link-Local address is not ready, we use unspecified address.

+  //

+  IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);

+

+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));

+

+  //

+  // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header

+  //

+  Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);

+  ASSERT (Options != NULL);

+  Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);

+  if (EFI_ERROR (Status)) {

+    NetbufFree (Packet);

+    Packet = NULL;

+    return Status;

+  }

+

+  //

+  // Fill in MLD message - Report

+  //

+  MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);

+  ASSERT (MldHead != NULL);

+  ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));

+  MldHead->Head.Type = ICMP_V6_LISTENER_REPORT;

+  MldHead->Head.Code = 0;

+  IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);

+

+  HeadChecksum   = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));

+  PseudoChecksum = NetIp6PseudoHeadChecksum (

+                     &Head.SourceAddress,

+                     &Head.DestinationAddress,

+                     IP6_ICMP,

+                     sizeof (IP6_MLD_HEAD)

+                     );

+

+  MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);

+

+  //

+  // Transmit the packet

+  //

+  return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);

+}

+

+/**

+  Generate MLD Done message and send it out to MulticastAddr.

+

+  @param[in]  IpSb               The IP service to send the packet.

+  @param[in]  MulticastAddr      The specific IPv6 multicast address to which

+                                 the message sender is ceasing to listen.

+

+  @retval EFI_OUT_OF_RESOURCES   There are not sufficient resources to complete the

+                                 operation.

+  @retval EFI_SUCCESS            The MLD report message was successfully sent out.

+

+**/

+EFI_STATUS

+Ip6SendMldDone (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *MulticastAddr

+  )

+{

+  IP6_MLD_HEAD              *MldHead;

+  NET_BUF                   *Packet;

+  EFI_IP6_HEADER            Head;

+  UINT16                    PayloadLen;

+  UINTN                     OptionLen;

+  UINT8                     *Options;

+  EFI_STATUS                Status;

+  EFI_IPv6_ADDRESS          Destination;

+  UINT16                    HeadChecksum;

+  UINT16                    PseudoChecksum;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));

+

+  //

+  // Generate the packet to be sent

+  // IPv6 basic header + Hop by Hop option + MLD message

+  //

+

+  OptionLen = 0;

+  Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);

+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);

+

+  PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));

+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Create the basic IPv6 header.

+  //

+  Head.FlowLabelL     = 0;

+  Head.FlowLabelH     = 0;

+  Head.PayloadLength  = HTONS (PayloadLen);

+  Head.NextHeader     = IP6_HOP_BY_HOP;

+  Head.HopLimit       = 1;

+

+  //

+  // If Link-Local address is not ready, we use unspecified address.

+  //

+  IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);

+

+  Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination);

+  IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination);

+

+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));

+

+  //

+  // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header

+  //

+  Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);

+  ASSERT (Options != NULL);

+  Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);

+  if (EFI_ERROR (Status)) {

+    NetbufFree (Packet);

+    Packet = NULL;

+    return Status;

+  }

+

+  //

+  // Fill in MLD message - Done

+  //

+  MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);

+  ASSERT (MldHead != NULL);

+  ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));

+  MldHead->Head.Type = ICMP_V6_LISTENER_DONE;

+  MldHead->Head.Code = 0;

+  IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);

+

+  HeadChecksum   = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));

+  PseudoChecksum = NetIp6PseudoHeadChecksum (

+                     &Head.SourceAddress,

+                     &Head.DestinationAddress,

+                     IP6_ICMP,

+                     sizeof (IP6_MLD_HEAD)

+                     );

+

+  MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);

+

+  //

+  // Transmit the packet

+  //

+  return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);

+}

+

+/**

+  Init the MLD data of the IP6 service instance. Configure

+  MNP to receive ALL SYSTEM multicast.

+

+  @param[in]  IpSb              The IP6 service whose MLD is to be initialized.

+

+  @retval EFI_OUT_OF_RESOURCES  There are not sufficient resourcet to complete the

+                                operation.

+  @retval EFI_SUCCESS           The MLD module successfully initialized.

+

+**/

+EFI_STATUS

+Ip6InitMld (

+  IN IP6_SERVICE            *IpSb

+  )

+{

+  EFI_IPv6_ADDRESS          AllNodes;

+  IP6_MLD_GROUP             *Group;

+  EFI_STATUS                Status;

+

+  //

+  // Join the link-scope all-nodes multicast address (FF02::1).

+  // This address is started in Idle Listener state and never transitions to

+  // another state, and never sends a Report or Done for that address.

+  //

+

+  Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);

+

+  Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32) IP6_INFINIT_LIFETIME);

+  if (Group == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac);

+  if (EFI_ERROR (Status)) {

+    goto ERROR;

+  }

+

+  //

+  // Configure MNP to receive all-nodes multicast

+  //

+  Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);

+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {

+    goto ERROR;

+  }

+

+  return EFI_SUCCESS;

+

+ERROR:

+  RemoveEntryList (&Group->Link);

+  FreePool (Group);

+  return Status;

+}

+

+/**

+  Add a group address to the array of group addresses.

+  The caller should make sure that no duplicated address

+  existed in the array.

+

+  @param[in, out]  IpInstance       Points to an IP6_PROTOCOL instance.

+  @param[in]       Group            The IP6 multicast address to add.

+

+  @retval EFI_OUT_OF_RESOURCES      There are not sufficient resources to complete

+                                    the operation.

+  @retval EFI_SUCESS                The address is added to the group address array.

+

+**/

+EFI_STATUS

+Ip6CombineGroups (

+  IN OUT IP6_PROTOCOL *IpInstance,

+  IN EFI_IPv6_ADDRESS *Group

+  )

+{

+  EFI_IPv6_ADDRESS     *GroupList;

+

+  NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);

+  ASSERT (Group != NULL && IP6_IS_MULTICAST (Group));

+

+  IpInstance->GroupCount++;

+

+  GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS));

+  if (GroupList == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  if (IpInstance->GroupCount > 1) {

+    ASSERT (IpInstance->GroupList != NULL);

+

+    CopyMem (

+      GroupList,

+      IpInstance->GroupList,

+      (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS)

+      );

+

+    FreePool (IpInstance->GroupList);

+  }

+

+  IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group);

+

+  IpInstance->GroupList = GroupList;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Remove a group address from the array of group addresses.

+  Although the function doesn't assume the byte order of Group,

+  the network byte order is used by the caller.

+

+  @param[in, out]  IpInstance       Points to an IP6_PROTOCOL instance.

+  @param[in]       Group            The IP6 multicast address to remove.

+

+  @retval EFI_NOT_FOUND             Cannot find the to be removed group address.

+  @retval EFI_SUCCESS               The group address was successfully removed.

+

+**/

+EFI_STATUS

+Ip6RemoveGroup (

+  IN OUT IP6_PROTOCOL *IpInstance,

+  IN EFI_IPv6_ADDRESS *Group

+  )

+{

+  UINT32                    Index;

+  UINT32                    Count;

+

+  Count = IpInstance->GroupCount;

+

+  for (Index = 0; Index < Count; Index++) {

+    if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) {

+      break;

+    }

+  }

+

+  if (Index == Count) {

+    return EFI_NOT_FOUND;

+  }

+

+  while (Index < Count - 1) {

+    IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1);

+    Index++;

+  }

+

+  ASSERT (IpInstance->GroupCount > 0);

+  IpInstance->GroupCount--;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Join the multicast group on behalf of this IP6 service binding instance.

+

+  @param[in]  IpSb               The IP6 service binding instance.

+  @param[in]  Interface          Points to an IP6_INTERFACE structure.

+  @param[in]  Address            The group address to join.

+

+  @retval EFI_SUCCESS            Successfully join the multicast group.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.

+  @retval Others                 Failed to join the multicast group.

+

+**/

+EFI_STATUS

+Ip6JoinGroup (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_INTERFACE          *Interface,

+  IN EFI_IPv6_ADDRESS       *Address

+  )

+{

+  IP6_MLD_GROUP            *Group;

+  EFI_STATUS               Status;

+

+  Group = Ip6FindMldEntry (IpSb, Address);

+  if (Group != NULL) {

+    Group->RefCnt++;

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Repeat the report once or twcie after short delays [Unsolicited Report Interval] (default:10s)

+  // Simulate this operation as a Multicast-Address-Specific Query was received for that addresss.

+  //

+  Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL);

+  if (Group == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Group->SendByUs = TRUE;

+

+  Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);

+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {

+    goto ERROR;

+  }

+

+  //

+  // Send unsolicited report when a node starts listening to a multicast address

+  //

+  Status = Ip6SendMldReport (IpSb, Interface, Address);

+  if (EFI_ERROR (Status)) {

+    goto ERROR;

+  }

+

+  return EFI_SUCCESS;

+

+ERROR:

+  RemoveEntryList (&Group->Link);

+  FreePool (Group);

+  return Status;

+}

+

+/**

+  Leave the IP6 multicast group.

+

+  @param[in]  IpSb               The IP6 service binding instance.

+  @param[in]  Address            The group address to leave.

+

+  @retval EFI_NOT_FOUND          The IP6 service instance isn't in the group.

+  @retval EFI_SUCCESS            Successfully leave the multicast group..

+  @retval Others                 Failed to leave the multicast group.

+

+**/

+EFI_STATUS

+Ip6LeaveGroup (

+ IN IP6_SERVICE            *IpSb,

+ IN EFI_IPv6_ADDRESS       *Address

+  )

+{

+  IP6_MLD_GROUP            *Group;

+  EFI_STATUS               Status;

+

+  Group = Ip6FindMldEntry (IpSb, Address);

+  if (Group == NULL) {

+    return EFI_NOT_FOUND;

+  }

+

+  //

+  // If more than one instance is in the group, decrease

+  // the RefCnt then return.

+  //

+  if ((Group->RefCnt > 0) && (--Group->RefCnt > 0)) {

+    return EFI_SUCCESS;

+  }

+

+  //

+  // If multiple IP6 group addresses are mapped to the same

+  // multicast MAC address, don't configure the MNP to leave

+  // the MAC.

+  //

+  if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) {

+    Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac);

+    if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {

+      return Status;

+    }

+  }

+

+  //

+  // Send a leave report if we are the last node to report

+  //

+  if (Group->SendByUs) {

+    Status = Ip6SendMldDone (IpSb, Address);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+  }

+

+  RemoveEntryList (&Group->Link);

+  FreePool (Group);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Worker function for EfiIp6Groups(). The caller

+  should make sure that the parameters are valid.

+

+  @param[in]  IpInstance        The IP6 child to change the setting.

+  @param[in]  JoinFlag          TRUE to join the group, otherwise leave it.

+  @param[in]  GroupAddress      The target group address. If NULL, leave all

+                                the group addresses.

+

+  @retval EFI_ALREADY_STARTED   Wants to join the group, but is already a member of it

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate sufficient resources.

+  @retval EFI_DEVICE_ERROR      Failed to set the group configuraton.

+  @retval EFI_SUCCESS           Successfully updated the group setting.

+  @retval EFI_NOT_FOUND         Try to leave the group which it isn't a member.

+

+**/

+EFI_STATUS

+Ip6Groups (

+  IN IP6_PROTOCOL           *IpInstance,

+  IN BOOLEAN                JoinFlag,

+  IN EFI_IPv6_ADDRESS       *GroupAddress       OPTIONAL

+  )

+{

+  EFI_STATUS                Status;

+  IP6_SERVICE               *IpSb;

+  UINT32                    Index;

+  EFI_IPv6_ADDRESS          *Group;

+

+  IpSb = IpInstance->Service;

+

+  if (JoinFlag) {

+    ASSERT (GroupAddress != NULL);

+

+    for (Index = 0; Index < IpInstance->GroupCount; Index++) {

+      if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) {

+        return EFI_ALREADY_STARTED;

+      }

+    }

+

+    Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress);

+    if (!EFI_ERROR (Status)) {

+      return Ip6CombineGroups (IpInstance, GroupAddress);

+    }

+

+    return Status;

+  }

+

+  //

+  // Leave the group. Leave all the groups if GroupAddress is NULL.

+  //

+  for (Index = IpInstance->GroupCount; Index > 0; Index--) {

+    Group = IpInstance->GroupList + (Index - 1);

+

+    if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) {

+      Status = Ip6LeaveGroup (IpInstance->Service, Group);

+      if (EFI_ERROR (Status)) {

+        return Status;

+      }

+

+      Ip6RemoveGroup (IpInstance, Group);

+

+      if (IpInstance->GroupCount == 0) {

+        ASSERT (Index == 1);

+        FreePool (IpInstance->GroupList);

+        IpInstance->GroupList = NULL;

+      }

+

+      if (GroupAddress != NULL) {

+        return EFI_SUCCESS;

+      }

+    }

+  }

+

+  return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS);

+}

+

+/**

+  Set a random value of the delay timer for the multicast address from the range

+  [0, Maximum Response Delay]. If a timer for any address is already

+  running, it is reset to the new random value only if the requested

+  Maximum Response Delay is less than the remaining value of the

+  running timer.  If the Query packet specifies a Maximum Response

+  Delay of zero, each timer is effectively set to zero, and the action

+  specified below for timer expiration is performed immediately.

+

+  @param[in]      IpSb               The IP6 service binding instance.

+  @param[in]      MaxRespDelay       The Maximum Response Delay, in milliseconds.

+  @param[in]      MulticastAddr      The multicast address.

+  @param[in, out] Group              Points to a IP6_MLD_GROUP list entry node.

+

+  @retval EFI_SUCCESS                The delay timer is successfully updated or

+                                     timer expiration is performed immediately.

+  @retval Others                     Failed to send out MLD report message.

+

+**/

+EFI_STATUS

+Ip6UpdateDelayTimer (

+  IN IP6_SERVICE            *IpSb,

+  IN UINT16                 MaxRespDelay,

+  IN EFI_IPv6_ADDRESS       *MulticastAddr,

+  IN OUT IP6_MLD_GROUP      *Group

+  )

+{

+  UINT32                    Delay;

+

+  //

+  // If the Query packet specifies a Maximum Response Delay of zero, perform timer

+  // expiration immediately.

+  //

+  if (MaxRespDelay == 0) {

+    Group->DelayTimer = 0;

+    return Ip6SendMldReport (IpSb, NULL, MulticastAddr);

+  }

+

+  Delay = (UINT32) (MaxRespDelay / 1000);

+

+  //

+  // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay]

+  // If a timer is already running, resets it if the request Maximum Response Delay

+  // is less than the remaining value of the running timer.

+  //

+  if (Group->DelayTimer == 0 || Delay < Group->DelayTimer) {

+    Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ());

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Process the Multicast Listener Query message.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the MLD query packet.

+  @param[in]  Packet             The content of the MLD query packet with IP head

+                                 removed.

+

+  @retval EFI_SUCCESS            The MLD query packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval Others                 Failed to process the packet.

+

+**/

+EFI_STATUS

+Ip6ProcessMldQuery (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  EFI_IPv6_ADDRESS          AllNodes;

+  IP6_MLD_GROUP             *Group;

+  IP6_MLD_HEAD              MldPacket;

+  LIST_ENTRY                *Entry;

+  EFI_STATUS                Status;

+

+  Status = EFI_INVALID_PARAMETER;

+

+  //

+  // Check the validity of the packet, generic query or specific query

+  //

+  if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {

+    goto Exit;

+  }

+

+  if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {

+    goto Exit;

+  }

+

+  //

+  // The Packet points to MLD report raw data without Hop-By-Hop option.

+  //

+  NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);

+  MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay);

+

+  Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);

+  if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) {

+    //

+    // Receives a Multicast-Address-Specific Query, check it firstly

+    //

+    if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {

+      goto Exit;

+    }

+    //

+    // The node is not listening but it receives the specific query. Just return.

+    //

+    Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);

+    if (Group == NULL) {

+      Status = EFI_SUCCESS;

+      goto Exit;

+    }

+

+    Status = Ip6UpdateDelayTimer (

+               IpSb,

+               MldPacket.MaxRespDelay,

+               &MldPacket.Group,

+               Group

+               );

+    goto Exit;

+  }

+

+  //

+  // Receives a General Query, sets a delay timer for each multicast address it is listening

+  //

+  NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {

+    Group  = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);

+    Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group);

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+  }

+

+  Status = EFI_SUCCESS;

+

+Exit:

+  NetbufFree (Packet);

+  return Status;

+}

+

+/**

+  Process the Multicast Listener Report message.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the MLD report packet.

+  @param[in]  Packet             The content of the MLD report packet with IP head

+                                 removed.

+

+  @retval EFI_SUCCESS            The MLD report packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+

+**/

+EFI_STATUS

+Ip6ProcessMldReport (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_MLD_HEAD              MldPacket;

+  IP6_MLD_GROUP             *Group;

+  EFI_STATUS                Status;

+

+  Status = EFI_INVALID_PARAMETER;

+

+  //

+  // Validate the incoming message, if invalid, drop it.

+  //

+  if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {

+    goto Exit;

+  }

+

+  if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {

+    goto Exit;

+  }

+

+  //

+  // The Packet points to MLD report raw data without Hop-By-Hop option.

+  //

+  NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);

+  if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {

+    goto Exit;

+  }

+

+  Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);

+  if (Group == NULL) {

+    goto Exit;

+  }

+

+  //

+  // The report is sent by another node, stop its own timer relates to the multicast address and clear

+  //

+

+  if (!Group->SendByUs) {

+    Group->DelayTimer = 0;

+  }

+

+  Status = EFI_SUCCESS;

+

+Exit:

+  NetbufFree (Packet);

+  return Status;

+}

+

+/**

+  The heartbeat timer of MLD module. It sends out a solicited MLD report when

+  DelayTimer expires.

+

+  @param[in]  IpSb              The IP6 service binding instance.

+

+**/

+VOID

+Ip6MldTimerTicking (

+  IN IP6_SERVICE            *IpSb

+  )

+{

+  IP6_MLD_GROUP             *Group;

+  LIST_ENTRY                *Entry;

+

+  //

+  // Send solicited report when timer expires

+  //

+  NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {

+    Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);

+    if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) {

+      Ip6SendMldReport (IpSb, NULL, &Group->Address);

+    }

+  }

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Mld.h b/NetworkPkg/Ip6Dxe/Ip6Mld.h
new file mode 100644
index 0000000..e69f5d5
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Mld.h
@@ -0,0 +1,198 @@
+/** @file

+  Multicast Listener Discovery support routines.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_MLD_H__

+#define __EFI_IP6_MLD_H__

+

+#define IP6_UNSOLICITED_REPORT_INTERVAL 10

+

+#pragma pack(1)

+typedef struct {

+  IP6_ICMP_HEAD           Head;

+  UINT16                  MaxRespDelay;

+  UINT16                  Reserved;

+  EFI_IPv6_ADDRESS        Group;

+} IP6_MLD_HEAD;

+#pragma pack()

+

+//

+// The status of multicast group. It isn't necessary to maintain

+// explicit state of host state diagram. A group with finity

+// DelayTime (less than 0xffffffff) is in "delaying listener" state. otherwise, it is in

+// "idle listener" state.

+//

+typedef struct {

+  LIST_ENTRY              Link;

+  INTN                    RefCnt;

+  EFI_IPv6_ADDRESS        Address;

+  UINT32                  DelayTimer;

+  BOOLEAN                 SendByUs;

+  EFI_MAC_ADDRESS         Mac;

+} IP6_MLD_GROUP;

+

+//

+// The MLD status. Each IP6 service instance has a MLD_SERVICE_DATA

+// attached. The Mldv1QuerySeen remember whether the server on this

+// connected network is v1 or v2.

+//

+typedef struct {

+  INTN                    Mldv1QuerySeen;

+  LIST_ENTRY              Groups;

+} IP6_MLD_SERVICE_DATA;

+

+/**

+  Search a IP6_MLD_GROUP list entry node from a list array.

+

+  @param[in]       IpSb          Points to an IP6 service binding instance.

+  @param[in]       MulticastAddr The IPv6 multicast address to be searched.

+

+  @return The found IP6_ML_GROUP list entry or NULL.

+

+**/

+IP6_MLD_GROUP *

+Ip6FindMldEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *MulticastAddr

+  );

+

+/**

+  Init the MLD data of the IP6 service instance, configure

+  MNP to receive ALL SYSTEM multicasts.

+

+  @param[in]  IpSb              The IP6 service whose MLD is to be initialized.

+

+  @retval EFI_OUT_OF_RESOURCES  There are not sufficient resources to complete the

+                                operation.

+  @retval EFI_SUCCESS           The MLD module successfully initialized.

+

+**/

+EFI_STATUS

+Ip6InitMld (

+  IN IP6_SERVICE            *IpSb

+  );

+

+/**

+  Join the multicast group on behalf of this IP6 service binding instance.

+

+  @param[in]  IpSb               The IP6 service binding instance.

+  @param[in]  Interface          Points to an IP6_INTERFACE structure.

+  @param[in]  Address            The group address to join.

+

+  @retval EFI_SUCCESS            Successfully joined the multicast group.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.

+  @retval Others                 Failed to join the multicast group.

+

+**/

+EFI_STATUS

+Ip6JoinGroup (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_INTERFACE          *Interface,

+  IN EFI_IPv6_ADDRESS       *Address

+  );

+

+/**

+  Leave the IP6 multicast group.

+

+  @param[in]  IpSb               The IP6 service binding instance.

+  @param[in]  Address            The group address to leave.

+

+  @retval EFI_NOT_FOUND          The IP6 service instance isn't in the group.

+  @retval EFI_SUCCESS            Successfully left the multicast group.

+  @retval Others                 Failed to leave the multicast group.

+

+**/

+EFI_STATUS

+Ip6LeaveGroup (

+ IN IP6_SERVICE            *IpSb,

+ IN EFI_IPv6_ADDRESS       *Address

+  );

+

+/**

+  Worker function for EfiIp6Groups(). The caller

+  should verify that the parameters are valid.

+

+  @param[in]  IpInstance        The IP6 child to change the setting.

+  @param[in]  JoinFlag          TRUE to join the group, otherwise leave it.

+  @param[in]  GroupAddress      The target group address. If NULL, leave all

+                                the group addresses.

+

+  @retval EFI_ALREADY_STARTED   Wants to join the group, but is already a member of it.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate some resources.

+  @retval EFI_DEVICE_ERROR      Failed to set the group configuraton.

+  @retval EFI_SUCCESS           Successfully updated the group setting.

+  @retval EFI_NOT_FOUND         Tried to leave a group of whom it isn't a member.

+

+**/

+EFI_STATUS

+Ip6Groups (

+  IN IP6_PROTOCOL           *IpInstance,

+  IN BOOLEAN                JoinFlag,

+  IN EFI_IPv6_ADDRESS       *GroupAddress       OPTIONAL

+  );

+

+/**

+  Process the Multicast Listener Query message.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the MLD query packet.

+  @param[in]  Packet             The content of the MLD query packet with IP head

+                                 removed.

+

+  @retval EFI_SUCCESS            The MLD query packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval Others                 Failed to process the packet.

+

+**/

+EFI_STATUS

+Ip6ProcessMldQuery (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  );

+

+/**

+  Process the Multicast Listener Report message.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the MLD report packet.

+  @param[in]  Packet             The content of the MLD report packet with IP head

+                                 removed.

+

+  @retval EFI_SUCCESS            The MLD report packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+

+**/

+EFI_STATUS

+Ip6ProcessMldReport (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  );

+

+

+/**

+  The heartbeat timer of the MLD module. It sends out solicited MLD report when

+  DelayTimer expires.

+

+  @param[in]  IpSb              The IP6 service binding instance.

+

+**/

+VOID

+Ip6MldTimerTicking (

+  IN IP6_SERVICE            *IpSb

+  );

+

+#endif

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Nd.c b/NetworkPkg/Ip6Dxe/Ip6Nd.c
new file mode 100644
index 0000000..f2a47a8
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Nd.c
@@ -0,0 +1,3141 @@
+/** @file

+  Implementation of Neighbor Discovery support routines.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+EFI_MAC_ADDRESS mZeroMacAddress;

+

+/**

+  Update the ReachableTime in IP6 service binding instance data, in milliseconds.

+

+  @param[in, out] IpSb     Points to the IP6_SERVICE.

+

+**/

+VOID

+Ip6UpdateReachableTime (

+  IN OUT IP6_SERVICE  *IpSb

+  )

+{

+  UINT32              Random;

+

+  Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE;

+  Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED;

+  IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE;

+}

+

+/**

+  Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number

+  of EFI_IP6_NEIGHBOR_CACHE is also returned.

+

+  @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.

+  @param[out] NeighborCount     The number of returned neighbor cache entries.

+  @param[out] NeighborCache     The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.

+

+  @retval EFI_SUCCESS           The EFI_IP6_NEIGHBOR_CACHE successfully built.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.

+

+**/

+EFI_STATUS

+Ip6BuildEfiNeighborCache (

+  IN IP6_PROTOCOL            *IpInstance,

+  OUT UINT32                 *NeighborCount,

+  OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache

+  )

+{

+  IP6_NEIGHBOR_ENTRY        *Neighbor;

+  LIST_ENTRY                *Entry;

+  IP6_SERVICE               *IpSb;

+  UINT32                    Count;

+  EFI_IP6_NEIGHBOR_CACHE    *EfiNeighborCache;

+  EFI_IP6_NEIGHBOR_CACHE    *NeighborCacheTmp;

+

+  NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);

+  ASSERT (NeighborCount != NULL && NeighborCache != NULL);

+

+  IpSb  = IpInstance->Service;

+  Count = 0;

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {

+    Count++;

+  }

+

+  if (Count == 0) {

+    return EFI_SUCCESS;

+  }

+

+  NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE));

+  if (NeighborCacheTmp == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  *NeighborCount = Count;

+  Count          = 0;

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {

+    Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);

+

+    EfiNeighborCache = NeighborCacheTmp + Count;

+

+   EfiNeighborCache->State = Neighbor->State;

+    IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor);

+    IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress);

+

+    Count++;

+  }

+

+  ASSERT (*NeighborCount == Count);

+  *NeighborCache = NeighborCacheTmp;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number

+  of prefix entries is also returned.

+

+  @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.

+  @param[out] PrefixCount       The number of returned prefix entries.

+  @param[out] PrefixTable       The pointer to the array of PrefixTable.

+

+  @retval EFI_SUCCESS           The prefix table successfully built.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the prefix table.

+

+**/

+EFI_STATUS

+Ip6BuildPrefixTable (

+  IN IP6_PROTOCOL           *IpInstance,

+  OUT UINT32                *PrefixCount,

+  OUT EFI_IP6_ADDRESS_INFO  **PrefixTable

+  )

+{

+  LIST_ENTRY                *Entry;

+  IP6_SERVICE               *IpSb;

+  UINT32                    Count;

+  IP6_PREFIX_LIST_ENTRY     *PrefixList;

+  EFI_IP6_ADDRESS_INFO      *EfiPrefix;

+  EFI_IP6_ADDRESS_INFO      *PrefixTableTmp;

+

+  NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);

+  ASSERT (PrefixCount != NULL && PrefixTable != NULL);

+

+  IpSb  = IpInstance->Service;

+  Count = 0;

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {

+    Count++;

+  }

+

+  if (Count == 0) {

+    return EFI_SUCCESS;

+  }

+

+  PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO));

+  if (PrefixTableTmp == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  *PrefixCount = Count;

+  Count        = 0;

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {

+    PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);

+    EfiPrefix  = PrefixTableTmp + Count;

+    IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix);

+    EfiPrefix->PrefixLength = PrefixList->PrefixLength;

+

+    Count++;

+  }

+

+  ASSERT (*PrefixCount == Count);

+  *PrefixTable = PrefixTableTmp;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Allocate and initialize a IP6 prefix list entry.

+

+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.

+  @param[in]  OnLinkOrAuto      If TRUE, the entry is created for the on link prefix list.

+                                Otherwise, it is created for the autoconfiguration prefix list.

+  @param[in]  ValidLifetime     The length of time in seconds that the prefix

+                                is valid for the purpose of on-link determination.

+  @param[in]  PreferredLifetime The length of time in seconds that addresses

+                                generated from the prefix via stateless address

+                                autoconfiguration remain preferred.

+  @param[in]  PrefixLength      The prefix length of the Prefix.

+  @param[in]  Prefix            The prefix address.

+

+  @return NULL if it failed to allocate memory for the prefix node. Otherwise, point

+          to the created or existing prefix list entry.

+

+**/

+IP6_PREFIX_LIST_ENTRY *

+Ip6CreatePrefixListEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN BOOLEAN                OnLinkOrAuto,

+  IN UINT32                 ValidLifetime,

+  IN UINT32                 PreferredLifetime,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *Prefix

+  )

+{

+  IP6_PREFIX_LIST_ENTRY     *PrefixEntry;

+  IP6_ROUTE_ENTRY           *RtEntry;

+  LIST_ENTRY                *ListHead;

+  LIST_ENTRY                *Entry;

+  IP6_PREFIX_LIST_ENTRY     *TmpPrefixEntry;

+

+  if (Prefix == NULL || PreferredLifetime > ValidLifetime || PrefixLength >= IP6_PREFIX_NUM) {

+    return NULL;

+  }

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  PrefixEntry = Ip6FindPrefixListEntry (

+                  IpSb,

+                  OnLinkOrAuto,

+                  PrefixLength,

+                  Prefix

+                  );

+  if (PrefixEntry != NULL) {

+    PrefixEntry->RefCnt ++;

+    return PrefixEntry;

+  }

+

+  PrefixEntry = AllocatePool (sizeof (IP6_PREFIX_LIST_ENTRY));

+  if (PrefixEntry == NULL) {

+    return NULL;

+  }

+

+  PrefixEntry->RefCnt            = 1;

+  PrefixEntry->ValidLifetime     = ValidLifetime;

+  PrefixEntry->PreferredLifetime = PreferredLifetime;

+  PrefixEntry->PrefixLength      = PrefixLength;

+  IP6_COPY_ADDRESS (&PrefixEntry->Prefix, Prefix);

+

+  ListHead = OnLinkOrAuto ? &IpSb->OnlinkPrefix : &IpSb->AutonomousPrefix;

+

+  //

+  // Create a direct route entry for on-link prefix and insert to route area.

+  //

+  if (OnLinkOrAuto) {

+    RtEntry = Ip6CreateRouteEntry (Prefix, PrefixLength, NULL);

+    if (RtEntry == NULL) {

+      FreePool (PrefixEntry);

+      return NULL;

+    }

+

+    RtEntry->Flag = IP6_DIRECT_ROUTE;

+    InsertHeadList (&IpSb->RouteTable->RouteArea[PrefixLength], &RtEntry->Link);

+    IpSb->RouteTable->TotalNum++;

+  }

+

+  //

+  // Insert the prefix entry in the order that a prefix with longer prefix length

+  // is put ahead in the list.

+  //

+  NET_LIST_FOR_EACH (Entry, ListHead) {

+    TmpPrefixEntry = NET_LIST_USER_STRUCT(Entry, IP6_PREFIX_LIST_ENTRY, Link);

+

+    if (TmpPrefixEntry->PrefixLength < PrefixEntry->PrefixLength) {

+      break;

+    }

+  }

+

+  NetListInsertBefore (Entry, &PrefixEntry->Link);

+

+  return PrefixEntry;

+}

+

+/**

+  Destory a IP6 prefix list entry.

+

+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.

+  @param[in]  PrefixEntry       The to be destroyed prefix list entry.

+  @param[in]  OnLinkOrAuto      If TRUE, the entry is removed from on link prefix list.

+                                Otherwise remove from autoconfiguration prefix list.

+  @param[in]  ImmediateDelete   If TRUE, remove the entry directly.

+                                Otherwise, check the reference count to see whether

+                                it should be removed.

+

+**/

+VOID

+Ip6DestroyPrefixListEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_PREFIX_LIST_ENTRY  *PrefixEntry,

+  IN BOOLEAN                OnLinkOrAuto,

+  IN BOOLEAN                ImmediateDelete

+  )

+{

+  LIST_ENTRY      *Entry;

+  IP6_INTERFACE   *IpIf;

+  EFI_STATUS      Status;

+

+  if ((!ImmediateDelete) && (PrefixEntry->RefCnt > 0) && ((--PrefixEntry->RefCnt) > 0)) {

+    return ;

+  }

+

+  if (OnLinkOrAuto) {

+      //

+      // Remove the direct route for onlink prefix from route table.

+      //

+      do {

+        Status = Ip6DelRoute (

+                   IpSb->RouteTable,

+                   &PrefixEntry->Prefix,

+                   PrefixEntry->PrefixLength,

+                   NULL

+                   );

+      } while (Status != EFI_NOT_FOUND);

+  } else {

+    //

+    // Remove the corresponding addresses generated from this autonomous prefix.

+    //

+    NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+      IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);

+

+      Ip6RemoveAddr (IpSb, &IpIf->AddressList, &IpIf->AddressCount, &PrefixEntry->Prefix, PrefixEntry->PrefixLength);

+    }

+  }

+

+  RemoveEntryList (&PrefixEntry->Link);

+  FreePool (PrefixEntry);

+}

+

+/**

+  Search the list array to find an IP6 prefix list entry.

+

+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.

+  @param[in]  OnLinkOrAuto      If TRUE, the search the link prefix list,

+                                Otherwise search the autoconfiguration prefix list.

+  @param[in]  PrefixLength      The prefix length of the Prefix

+  @param[in]  Prefix            The prefix address.

+

+  @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the

+          pointer to the IP6 prefix list entry.

+

+**/

+IP6_PREFIX_LIST_ENTRY *

+Ip6FindPrefixListEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN BOOLEAN                OnLinkOrAuto,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *Prefix

+  )

+{

+  IP6_PREFIX_LIST_ENTRY     *PrefixList;

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *ListHead;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (Prefix != NULL);

+

+  if (OnLinkOrAuto) {

+    ListHead = &IpSb->OnlinkPrefix;

+  } else {

+    ListHead = &IpSb->AutonomousPrefix;

+  }

+

+  NET_LIST_FOR_EACH (Entry, ListHead) {

+    PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);

+    if (PrefixLength != 255) {

+      //

+      // Perform exactly prefix match.

+      //

+      if (PrefixList->PrefixLength == PrefixLength &&

+        NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixLength)) {

+        return PrefixList;

+      }

+    } else {

+      //

+      // Perform the longest prefix match. The list is already sorted with

+      // the longest length prefix put at the head of the list.

+      //

+      if (NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixList->PrefixLength)) {

+        return PrefixList;

+      }

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  Release the resource in the prefix list table, and destroy the list entry and

+  corresponding addresses or route entries.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  ListHead          The list entry head of the prefix list table.

+

+**/

+VOID

+Ip6CleanPrefixListTable (

+  IN IP6_SERVICE            *IpSb,

+  IN LIST_ENTRY             *ListHead

+  )

+{

+  IP6_PREFIX_LIST_ENTRY     *PrefixList;

+  BOOLEAN                   OnLink;

+

+  OnLink = (BOOLEAN) (ListHead == &IpSb->OnlinkPrefix);

+

+  while (!IsListEmpty (ListHead)) {

+    PrefixList = NET_LIST_HEAD (ListHead, IP6_PREFIX_LIST_ENTRY, Link);

+    Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);

+  }

+}

+

+/**

+  Callback function when address resolution is finished. It will cancel

+  all the queued frames if the address resolution failed, or transmit them

+  if the request succeeded.

+

+  @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.

+

+**/

+VOID

+Ip6OnArpResolved (

+  IN VOID                   *Context

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_NEIGHBOR_ENTRY        *ArpQue;

+  IP6_SERVICE               *IpSb;

+  IP6_LINK_TX_TOKEN         *Token;

+  EFI_STATUS                Status;

+  BOOLEAN                   Sent;

+

+  ArpQue = (IP6_NEIGHBOR_ENTRY *) Context;

+  if ((ArpQue == NULL) || (ArpQue->Interface == NULL)) {

+    return ;

+  }

+

+  IpSb   = ArpQue->Interface->Service;

+  if ((IpSb == NULL) || (IpSb->Signature != IP6_SERVICE_SIGNATURE)) {

+    return ;

+  }

+

+  //

+  // ARP resolve failed for some reason. Release all the frame

+  // and ARP queue itself. Ip6FreeArpQue will call the frame's

+  // owner back.

+  //

+  if (NET_MAC_EQUAL (&ArpQue->LinkAddress, &mZeroMacAddress, IpSb->SnpMode.HwAddressSize)) {

+    Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, TRUE, EFI_NO_MAPPING, NULL, NULL);

+    return ;

+  }

+

+  //

+  // ARP resolve succeeded, Transmit all the frame.

+  //

+  Sent = FALSE;

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {

+    RemoveEntryList (Entry);

+

+    Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);

+    IP6_COPY_LINK_ADDRESS (&Token->DstMac, &ArpQue->LinkAddress);

+

+    //

+    // Insert the tx token before transmitting it via MNP as the FrameSentDpc

+    // may be called before Mnp->Transmit returns which will remove this tx

+    // token from the SentFrames list. Remove it from the list if the returned

+    // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the

+    // FrameSentDpc won't be queued.

+    //

+    InsertTailList (&ArpQue->Interface->SentFrames, &Token->Link);

+

+    Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);

+    if (EFI_ERROR (Status)) {

+      RemoveEntryList (&Token->Link);

+      Token->CallBack (Token->Packet, Status, 0, Token->Context);

+

+      Ip6FreeLinkTxToken (Token);

+      continue;

+    } else {

+      Sent = TRUE;

+    }

+  }

+

+  //

+  // Free the ArpQue only but not the whole neighbor entry.

+  //

+  Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, FALSE, EFI_SUCCESS, NULL, NULL);

+

+  if (Sent && (ArpQue->State == EfiNeighborStale)) {

+    ArpQue->State = EfiNeighborDelay;

+    ArpQue->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);

+  }

+}

+

+/**

+  Allocate and initialize an IP6 neighbor cache entry.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  CallBack          The callback function to be called when

+                                address resolution is finished.

+  @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.

+  @param[in]  LinkAddress       Points to the MAC address of the neighbor.

+                                Ignored if NULL.

+

+  @return NULL if failed to allocate memory for the neighbor cache entry.

+          Otherwise, point to the created neighbor cache entry.

+

+**/

+IP6_NEIGHBOR_ENTRY *

+Ip6CreateNeighborEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_ARP_CALLBACK       CallBack,

+  IN EFI_IPv6_ADDRESS       *Ip6Address,

+  IN EFI_MAC_ADDRESS        *LinkAddress OPTIONAL

+  )

+{

+  IP6_NEIGHBOR_ENTRY        *Entry;

+  IP6_DEFAULT_ROUTER        *DefaultRouter;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (Ip6Address!= NULL);

+

+  Entry = AllocateZeroPool (sizeof (IP6_NEIGHBOR_ENTRY));

+  if (Entry == NULL) {

+    return NULL;

+  }

+

+  Entry->RefCnt    = 1;

+  Entry->IsRouter  = FALSE;

+  Entry->ArpFree   = FALSE;

+  Entry->Dynamic   = FALSE;

+  Entry->State     = EfiNeighborInComplete;

+  Entry->Transmit  = IP6_MAX_MULTICAST_SOLICIT + 1;

+  Entry->CallBack  = CallBack;

+  Entry->Interface = NULL;

+

+  InitializeListHead (&Entry->Frames);

+

+  IP6_COPY_ADDRESS (&Entry->Neighbor, Ip6Address);

+

+  if (LinkAddress != NULL) {

+    IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, LinkAddress);

+  } else {

+    IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, &mZeroMacAddress);

+  }

+

+  InsertHeadList (&IpSb->NeighborTable, &Entry->Link);

+

+  //

+  // If corresponding default router entry exists, establish the relationship.

+  //

+  DefaultRouter = Ip6FindDefaultRouter (IpSb, Ip6Address);

+  if (DefaultRouter != NULL) {

+    DefaultRouter->NeighborCache = Entry;

+  }

+

+  return Entry;

+}

+

+/**

+  Search a IP6 neighbor cache entry.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.

+

+  @return NULL if it failed to find the matching neighbor cache entry.

+          Otherwise, point to the found neighbor cache entry.

+

+**/

+IP6_NEIGHBOR_ENTRY *

+Ip6FindNeighborEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Ip6Address

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_NEIGHBOR_ENTRY        *Neighbor;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (Ip6Address != NULL);

+

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {

+    Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);

+    if (EFI_IP6_EQUAL (Ip6Address, &Neighbor->Neighbor)) {

+      RemoveEntryList (Entry);

+      InsertHeadList (&IpSb->NeighborTable, Entry);

+

+      return Neighbor;

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  Free a IP6 neighbor cache entry and remove all the frames on the address

+  resolution queue that pass the FrameToCancel. That is, either FrameToCancel

+  is NULL, or it returns true for the frame.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  NeighborCache     The to be free neighbor cache entry.

+  @param[in]  SendIcmpError     If TRUE, send out ICMP error.

+  @param[in]  FullFree          If TRUE, remove the neighbor cache entry.

+                                Otherwise remove the pending frames.

+  @param[in]  IoStatus          The status returned to the cancelled frames'

+                                callback function.

+  @param[in]  FrameToCancel     Function to select which frame to cancel.

+                                This is an optional parameter that may be NULL.

+  @param[in]  Context           Opaque parameter to the FrameToCancel.

+                                Ignored if FrameToCancel is NULL.

+

+  @retval EFI_INVALID_PARAMETER The input parameter is invalid.

+  @retval EFI_SUCCESS           The operation finished successfully.

+

+**/

+EFI_STATUS

+Ip6FreeNeighborEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_NEIGHBOR_ENTRY     *NeighborCache,

+  IN BOOLEAN                SendIcmpError,

+  IN BOOLEAN                FullFree,

+  IN EFI_STATUS             IoStatus,

+  IN IP6_FRAME_TO_CANCEL    FrameToCancel OPTIONAL,

+  IN VOID                   *Context      OPTIONAL

+  )

+{

+  IP6_LINK_TX_TOKEN         *TxToken;

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_DEFAULT_ROUTER        *DefaultRouter;

+

+  //

+  // If FrameToCancel fails, the token will not be released.

+  // To avoid the memory leak, stop this usage model.

+  //

+  if (FullFree && FrameToCancel != NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &NeighborCache->Frames) {

+    TxToken = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);

+

+    if (SendIcmpError && !IP6_IS_MULTICAST (&TxToken->Packet->Ip.Ip6->DestinationAddress)) {

+      Ip6SendIcmpError (

+        IpSb,

+        TxToken->Packet,

+        NULL,

+        &TxToken->Packet->Ip.Ip6->SourceAddress,

+        ICMP_V6_DEST_UNREACHABLE,

+        ICMP_V6_ADDR_UNREACHABLE,

+        NULL

+        );

+    }

+

+    if ((FrameToCancel == NULL) || FrameToCancel (TxToken, Context)) {

+      RemoveEntryList (Entry);

+      TxToken->CallBack (TxToken->Packet, IoStatus, 0, TxToken->Context);

+      Ip6FreeLinkTxToken (TxToken);

+    }

+  }

+

+  if (NeighborCache->ArpFree && IsListEmpty (&NeighborCache->Frames)) {

+    RemoveEntryList (&NeighborCache->ArpList);

+    NeighborCache->ArpFree = FALSE;

+  }

+

+  if (FullFree) {

+    if (NeighborCache->IsRouter) {

+      DefaultRouter = Ip6FindDefaultRouter (IpSb, &NeighborCache->Neighbor);

+      if (DefaultRouter != NULL) {

+        Ip6DestroyDefaultRouter (IpSb, DefaultRouter);

+      }

+    }

+

+    RemoveEntryList (&NeighborCache->Link);

+    FreePool (NeighborCache);

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Allocate and initialize an IP6 default router entry.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  Ip6Address        The IPv6 address of the default router.

+  @param[in]  RouterLifetime    The lifetime associated with the default

+                                router, in units of seconds.

+

+  @return NULL if it failed to allocate memory for the default router node.

+          Otherwise, point to the created default router node.

+

+**/

+IP6_DEFAULT_ROUTER *

+Ip6CreateDefaultRouter (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Ip6Address,

+  IN UINT16                 RouterLifetime

+  )

+{

+  IP6_DEFAULT_ROUTER        *Entry;

+  IP6_ROUTE_ENTRY           *RtEntry;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (Ip6Address != NULL);

+

+  Entry = AllocatePool (sizeof (IP6_DEFAULT_ROUTER));

+  if (Entry == NULL) {

+    return NULL;

+  }

+

+  Entry->RefCnt        = 1;

+  Entry->Lifetime      = RouterLifetime;

+  Entry->NeighborCache = Ip6FindNeighborEntry (IpSb, Ip6Address);

+  IP6_COPY_ADDRESS (&Entry->Router, Ip6Address);

+

+  //

+  // Add a default route into route table with both Destination and PrefixLength set to zero.

+  //

+  RtEntry = Ip6CreateRouteEntry (NULL, 0, Ip6Address);

+  if (RtEntry == NULL) {

+    FreePool (Entry);

+    return NULL;

+  }

+

+  InsertHeadList (&IpSb->RouteTable->RouteArea[0], &RtEntry->Link);

+  IpSb->RouteTable->TotalNum++;

+

+  InsertTailList (&IpSb->DefaultRouterList, &Entry->Link);

+

+  return Entry;

+}

+

+/**

+  Destroy an IP6 default router entry.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  DefaultRouter     The to be destroyed IP6_DEFAULT_ROUTER.

+

+**/

+VOID

+Ip6DestroyDefaultRouter (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_DEFAULT_ROUTER     *DefaultRouter

+  )

+{

+  EFI_STATUS                Status;

+

+  RemoveEntryList (&DefaultRouter->Link);

+

+  //

+  // Update the Destination Cache - all entries using the time-out router as next-hop

+  // should perform next-hop determination again.

+  //

+  do {

+    Status = Ip6DelRoute (IpSb->RouteTable, NULL, 0, &DefaultRouter->Router);

+  } while (Status != EFI_NOT_FOUND);

+

+  FreePool (DefaultRouter);

+}

+

+/**

+  Clean an IP6 default router list.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  DefaultRouter     The to be destroyed IP6_DEFAULT_ROUTER.

+

+**/

+VOID

+Ip6CleanDefaultRouterList (

+  IN IP6_SERVICE            *IpSb

+  )

+{

+  IP6_DEFAULT_ROUTER        *DefaultRouter;

+

+  while (!IsListEmpty (&IpSb->DefaultRouterList)) {

+    DefaultRouter = NET_LIST_HEAD (&IpSb->DefaultRouterList, IP6_DEFAULT_ROUTER, Link);

+    Ip6DestroyDefaultRouter (IpSb, DefaultRouter);

+  }

+}

+

+/**

+  Search a default router node from an IP6 default router list.

+

+  @param[in]  IpSb          The pointer to the IP6_SERVICE instance.

+  @param[in]  Ip6Address    The IPv6 address of the to be searched default router node.

+

+  @return NULL if it failed to find the matching default router node.

+          Otherwise, point to the found default router node.

+

+**/

+IP6_DEFAULT_ROUTER *

+Ip6FindDefaultRouter (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Ip6Address

+  )

+{

+  LIST_ENTRY                *Entry;

+  IP6_DEFAULT_ROUTER        *DefaultRouter;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (Ip6Address != NULL);

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->DefaultRouterList) {

+    DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);

+    if (EFI_IP6_EQUAL (Ip6Address, &DefaultRouter->Router)) {

+      return DefaultRouter;

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  The function to be called after DAD (Duplicate Address Detection) is performed.

+

+  @param[in]  IsDadPassed   If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.

+  @param[in]  IpIf          Points to the IP6_INTERFACE.

+  @param[in]  DadEntry      The DAD entry which already performed DAD.

+

+**/

+VOID

+Ip6OnDADFinished (

+  IN BOOLEAN        IsDadPassed,

+  IN IP6_INTERFACE  *IpIf,

+  IN IP6_DAD_ENTRY  *DadEntry

+  )

+{

+  IP6_SERVICE               *IpSb;

+  IP6_ADDRESS_INFO          *AddrInfo;

+  EFI_DHCP6_PROTOCOL        *Dhcp6;

+  UINT16                    OptBuf[4];

+  EFI_DHCP6_PACKET_OPTION   *Oro;

+  EFI_DHCP6_RETRANSMISSION  InfoReqReXmit;

+

+  IpSb     = IpIf->Service;

+  AddrInfo = DadEntry->AddressInfo;

+

+  if (IsDadPassed) {

+    //

+    // DAD succeed.

+    //

+    if (NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {

+      ASSERT (!IpSb->LinkLocalOk);

+

+      IP6_COPY_ADDRESS (&IpSb->LinkLocalAddr, &AddrInfo->Address);

+      IpSb->LinkLocalOk = TRUE;

+      IpIf->Configured  = TRUE;

+

+      //

+      // Check whether DHCP6 need to be started.

+      //

+      Dhcp6 = IpSb->Ip6ConfigInstance.Dhcp6;

+

+      if (IpSb->Dhcp6NeedStart) {

+        Dhcp6->Start (Dhcp6);

+        IpSb->Dhcp6NeedStart = FALSE;

+      }

+

+      if (IpSb->Dhcp6NeedInfoRequest) {

+        //

+        // Set the exta options to send. Here we only want the option request option

+        // with DNS SERVERS.

+        //

+        Oro         = (EFI_DHCP6_PACKET_OPTION *) OptBuf;

+        Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO);

+        Oro->OpLen  = HTONS (2);

+        *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);

+

+        InfoReqReXmit.Irt = 4;

+        InfoReqReXmit.Mrc = 64;

+        InfoReqReXmit.Mrt = 60;

+        InfoReqReXmit.Mrd = 0;

+

+        Dhcp6->InfoRequest (

+                 Dhcp6,

+                 TRUE,

+                 Oro,

+                 0,

+                 NULL,

+                 &InfoReqReXmit,

+                 IpSb->Ip6ConfigInstance.Dhcp6Event,

+                 Ip6ConfigOnDhcp6Reply,

+                 &IpSb->Ip6ConfigInstance

+                 );

+      }

+

+      //

+      // Add an on-link prefix for link-local address.

+      //

+      Ip6CreatePrefixListEntry (

+        IpSb,

+        TRUE,

+        (UINT32) IP6_INFINIT_LIFETIME,

+        (UINT32) IP6_INFINIT_LIFETIME,

+        IP6_LINK_LOCAL_PREFIX_LENGTH,

+        &IpSb->LinkLocalAddr

+        );

+

+    } else {

+      //

+      // Global scope unicast address.

+      //

+      Ip6AddAddr (IpIf, AddrInfo);

+

+      //

+      // Add an on-link prefix for this address.

+      //

+      Ip6CreatePrefixListEntry (

+        IpSb,

+        TRUE,

+        AddrInfo->ValidLifetime,

+        AddrInfo->PreferredLifetime,

+        AddrInfo->PrefixLength,

+        &AddrInfo->Address

+        );

+

+      IpIf->Configured = TRUE;

+    }

+  } else {

+    //

+    // Leave the group we joined before.

+    //

+    Ip6LeaveGroup (IpSb, &DadEntry->Destination);

+  }

+

+  if (DadEntry->Callback != NULL) {

+    DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context);

+  }

+

+  if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {

+    FreePool (AddrInfo);

+    RemoveEntryList (&DadEntry->Link);

+    FreePool (DadEntry);

+    //

+    // Disable IP operation since link-local address is a duplicate address.

+    //

+    IpSb->LinkLocalDadFail = TRUE;

+    IpSb->Mnp->Configure (IpSb->Mnp, NULL);

+    gBS->SetTimer (IpSb->Timer, TimerCancel, 0);

+    gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);

+    return ;

+  }

+

+  if (!IsDadPassed || NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {

+    //

+    // Free the AddressInfo we hold if DAD fails or it is a link-local address.

+    //

+    FreePool (AddrInfo);

+  }

+

+  RemoveEntryList (&DadEntry->Link);

+  FreePool (DadEntry);

+}

+

+/**

+  Create a DAD (Duplicate Address Detection) entry and queue it to be performed.

+

+  @param[in]  IpIf          Points to the IP6_INTERFACE.

+  @param[in]  AddressInfo   The address information which needs DAD performed.

+  @param[in]  Callback      The callback routine that will be called after DAD

+                            is performed. This is an optional parameter that

+                            may be NULL.

+  @param[in]  Context       The opaque parameter for a DAD callback routine.

+                            This is an optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS           The DAD entry was created and queued.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory to complete the

+                                operation.

+

+

+**/

+EFI_STATUS

+Ip6InitDADProcess (

+  IN IP6_INTERFACE          *IpIf,

+  IN IP6_ADDRESS_INFO       *AddressInfo,

+  IN IP6_DAD_CALLBACK       Callback  OPTIONAL,

+  IN VOID                   *Context  OPTIONAL

+  )

+{

+  IP6_DAD_ENTRY                             *Entry;

+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  *DadXmits;

+  IP6_SERVICE                               *IpSb;

+  EFI_STATUS                                Status;

+  UINT32                                    MaxDelayTick;

+

+  NET_CHECK_SIGNATURE (IpIf, IP6_INTERFACE_SIGNATURE);

+  ASSERT (AddressInfo != NULL);

+

+  Status   = EFI_SUCCESS;

+  IpSb     = IpIf->Service;

+  DadXmits = &IpSb->Ip6ConfigInstance.DadXmits;

+

+  //

+  // Allocate the resources and insert info

+  //

+  Entry = AllocatePool (sizeof (IP6_DAD_ENTRY));

+  if (Entry == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Map the incoming unicast address to solicited-node multicast address

+  //

+  Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination);

+

+  //

+  // Join in the solicited-node multicast address.

+  //

+  Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination);

+  if (EFI_ERROR (Status)) {

+    FreePool (Entry);

+    return Status;

+  }

+

+  Entry->Signature    = IP6_DAD_ENTRY_SIGNATURE;

+  Entry->MaxTransmit  = DadXmits->DupAddrDetectTransmits;

+  Entry->Transmit     = 0;

+  Entry->Receive      = 0;

+  MaxDelayTick        = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS;

+  Entry->RetransTick  = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5;

+  Entry->AddressInfo  = AddressInfo;

+  Entry->Callback     = Callback;

+  Entry->Context      = Context;

+  InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link);

+

+  if (Entry->MaxTransmit == 0) {

+    //

+    // DAD is disabled on this interface, immediately mark this DAD successful.

+    //

+    Ip6OnDADFinished (TRUE, IpIf, Entry);

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Search IP6_DAD_ENTRY from the Duplicate Address Detection List.

+

+  @param[in]  IpSb          The pointer to the IP6_SERVICE instance.

+  @param[in]  Target        The address information which needs DAD performed .

+  @param[out] Interface     If not NULL, output the IP6 interface that configures

+                            the tentative address.

+

+  @return NULL if failed to find the matching DAD entry.

+          Otherwise, point to the found DAD entry.

+

+**/

+IP6_DAD_ENTRY *

+Ip6FindDADEntry (

+  IN  IP6_SERVICE      *IpSb,

+  IN  EFI_IPv6_ADDRESS *Target,

+  OUT IP6_INTERFACE    **Interface OPTIONAL

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Entry2;

+  IP6_INTERFACE             *IpIf;

+  IP6_DAD_ENTRY             *DupAddrDetect;

+  IP6_ADDRESS_INFO          *AddrInfo;

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);

+

+    NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) {

+      DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);

+      AddrInfo      = DupAddrDetect->AddressInfo;

+      if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) {

+        if (Interface != NULL) {

+          *Interface = IpIf;

+        }

+        return DupAddrDetect;

+      }

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  Generate router solicit message and send it out to Destination Address or

+  All Router Link Local scope multicast address.

+

+  @param[in]  IpSb               The IP service to send the packet.

+  @param[in]  Interface          If not NULL, points to the IP6 interface to send

+                                 the packet.

+  @param[in]  SourceAddress      If not NULL, the source address of the message.

+  @param[in]  DestinationAddress If not NULL, the destination address of the message.

+  @param[in]  SourceLinkAddress  If not NULL, the MAC address of the source.

+                                 A source link-layer address option will be appended

+                                 to the message.

+

+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the

+                                 operation.

+  @retval EFI_SUCCESS            The router solicit message was successfully sent.

+

+**/

+EFI_STATUS

+Ip6SendRouterSolicit (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_INTERFACE          *Interface          OPTIONAL,

+  IN EFI_IPv6_ADDRESS       *SourceAddress      OPTIONAL,

+  IN EFI_IPv6_ADDRESS       *DestinationAddress OPTIONAL,

+  IN EFI_MAC_ADDRESS        *SourceLinkAddress  OPTIONAL

+  )

+{

+  NET_BUF                   *Packet;

+  EFI_IP6_HEADER            Head;

+  IP6_ICMP_INFORMATION_HEAD *IcmpHead;

+  IP6_ETHER_ADDR_OPTION     *LinkLayerOption;

+  UINT16                    PayloadLen;

+  IP6_INTERFACE             *IpIf;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  IpIf = Interface;

+  if (IpIf == NULL && IpSb->DefaultInterface != NULL) {

+    IpIf = IpSb->DefaultInterface;

+  }

+

+  //

+  // Generate the packet to be sent

+  //

+

+  PayloadLen = (UINT16) sizeof (IP6_ICMP_INFORMATION_HEAD);

+  if (SourceLinkAddress != NULL) {

+    PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION);

+  }

+

+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Create the basic IPv6 header.

+  //

+  Head.FlowLabelL     = 0;

+  Head.FlowLabelH     = 0;

+  Head.PayloadLength  = HTONS (PayloadLen);

+  Head.NextHeader     = IP6_ICMP;

+  Head.HopLimit       = IP6_HOP_LIMIT;

+

+  if (SourceAddress != NULL) {

+    IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);

+  } else {

+    ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));

+  }

+

+

+  if (DestinationAddress != NULL) {

+    IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);

+  } else {

+    Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress);

+  }

+

+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));

+

+  //

+  // Fill in the ICMP header, and Source link-layer address if contained.

+  //

+

+  IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);

+  ASSERT (IcmpHead != NULL);

+  ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));

+  IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT;

+  IcmpHead->Head.Code = 0;

+

+  LinkLayerOption = NULL;

+  if (SourceLinkAddress != NULL) {

+    LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (

+                                                  Packet,

+                                                  sizeof (IP6_ETHER_ADDR_OPTION),

+                                                  FALSE

+                                                  );

+    ASSERT (LinkLayerOption != NULL);

+    LinkLayerOption->Type   = Ip6OptionEtherSource;

+    LinkLayerOption->Length = (UINT8) sizeof (IP6_ETHER_ADDR_OPTION);

+    CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);

+  }

+

+  //

+  // Transmit the packet

+  //

+  return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);

+}

+

+/**

+  Generate a Neighbor Advertisement message and send it out to Destination Address.

+

+  @param[in]  IpSb               The IP service to send the packet.

+  @param[in]  SourceAddress      The source address of the message.

+  @param[in]  DestinationAddress The destination address of the message.

+  @param[in]  TargetIp6Address   The target address field in the Neighbor Solicitation

+                                 message that prompted this advertisement.

+  @param[in]  TargetLinkAddress  The MAC address for the target, i.e. the sender

+                                 of the advertisement.

+  @param[in]  IsRouter           If TRUE, indicates the sender is a router.

+  @param[in]  Override           If TRUE, indicates the advertisement should override

+                                 an existing cache entry and update the MAC address.

+  @param[in]  Solicited          If TRUE, indicates the advertisement was sent

+                                 in response to a Neighbor Solicitation from

+                                 the Destination address.

+

+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the

+                                 operation.

+  @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.

+

+**/

+EFI_STATUS

+Ip6SendNeighborAdvertise (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *SourceAddress,

+  IN EFI_IPv6_ADDRESS       *DestinationAddress,

+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,

+  IN EFI_MAC_ADDRESS        *TargetLinkAddress,

+  IN BOOLEAN                IsRouter,

+  IN BOOLEAN                Override,

+  IN BOOLEAN                Solicited

+  )

+{

+  NET_BUF                   *Packet;

+  EFI_IP6_HEADER            Head;

+  IP6_ICMP_INFORMATION_HEAD *IcmpHead;

+  IP6_ETHER_ADDR_OPTION     *LinkLayerOption;

+  EFI_IPv6_ADDRESS          *Target;

+  UINT16                    PayloadLen;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  //

+  // The Neighbor Advertisement message must include a Target link-layer address option

+  // when responding to multicast solicitation and should include such option when

+  // responding to unicast solicitation. It also must include such option as unsolicited

+  // advertisement.

+  //

+  ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL);

+

+  PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION));

+

+  //

+  // Generate the packet to be sent

+  //

+

+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Create the basic IPv6 header.

+  //

+  Head.FlowLabelL     = 0;

+  Head.FlowLabelH     = 0;

+  Head.PayloadLength  = HTONS (PayloadLen);

+  Head.NextHeader     = IP6_ICMP;

+  Head.HopLimit       = IP6_HOP_LIMIT;

+

+  IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);

+  IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);

+

+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));

+

+  //

+  // Fill in the ICMP header, Target address, and Target link-layer address.

+  // Set the Router flag, Solicited flag and Override flag.

+  //

+

+  IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);

+  ASSERT (IcmpHead != NULL);

+  ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));

+  IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE;

+  IcmpHead->Head.Code = 0;

+

+  if (IsRouter) {

+    IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG;

+  }

+

+  if (Solicited) {

+    IcmpHead->Fourth |= IP6_SOLICITED_FLAG;

+  }

+

+  if (Override) {

+    IcmpHead->Fourth |= IP6_OVERRIDE_FLAG;

+  }

+

+  Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);

+  ASSERT (Target != NULL);

+  IP6_COPY_ADDRESS (Target, TargetIp6Address);

+

+  LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (

+                                                Packet,

+                                                sizeof (IP6_ETHER_ADDR_OPTION),

+                                                FALSE

+                                                );

+  ASSERT (LinkLayerOption != NULL);

+  LinkLayerOption->Type   = Ip6OptionEtherTarget;

+  LinkLayerOption->Length = 1;

+  CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6);

+

+  //

+  // Transmit the packet

+  //

+  return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);

+}

+

+/**

+  Generate the Neighbor Solicitation message and send it to the Destination Address.

+

+  @param[in]  IpSb               The IP service to send the packet

+  @param[in]  SourceAddress      The source address of the message.

+  @param[in]  DestinationAddress The destination address of the message.

+  @param[in]  TargetIp6Address   The IP address of the target of the solicitation.

+                                 It must not be a multicast address.

+  @param[in]  SourceLinkAddress  The MAC address for the sender. If not NULL,

+                                 a source link-layer address option will be appended

+                                 to the message.

+

+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the

+                                 operation.

+  @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.

+

+**/

+EFI_STATUS

+Ip6SendNeighborSolicit (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *SourceAddress,

+  IN EFI_IPv6_ADDRESS       *DestinationAddress,

+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,

+  IN EFI_MAC_ADDRESS        *SourceLinkAddress OPTIONAL

+  )

+{

+  NET_BUF                   *Packet;

+  EFI_IP6_HEADER            Head;

+  IP6_ICMP_INFORMATION_HEAD *IcmpHead;

+  IP6_ETHER_ADDR_OPTION     *LinkLayerOption;

+  EFI_IPv6_ADDRESS          *Target;

+  BOOLEAN                   IsDAD;

+  UINT16                    PayloadLen;

+  IP6_NEIGHBOR_ENTRY        *Neighbor;

+

+  //

+  // Check input parameters

+  //

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  if (DestinationAddress == NULL || TargetIp6Address == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  IsDAD = FALSE;

+

+  if (SourceAddress == NULL || (SourceAddress != NULL && NetIp6IsUnspecifiedAddr (SourceAddress))) {

+    IsDAD = TRUE;

+  }

+

+  //

+  // The Neighbor Solicitation message should include a source link-layer address option

+  // if the solicitation is not sent by performing DAD - Duplicate Address Detection.

+  // Otherwise must not include it.

+  //

+  PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS));

+

+  if (!IsDAD) {

+    if (SourceLinkAddress == NULL) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    PayloadLen = (UINT16) (PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION));

+  }

+

+  //

+  // Generate the packet to be sent

+  //

+

+  Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Create the basic IPv6 header

+  //

+  Head.FlowLabelL     = 0;

+  Head.FlowLabelH     = 0;

+  Head.PayloadLength  = HTONS (PayloadLen);

+  Head.NextHeader     = IP6_ICMP;

+  Head.HopLimit       = IP6_HOP_LIMIT;

+

+  if (SourceAddress != NULL) {

+    IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);

+  } else {

+    ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));

+  }

+

+  IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);

+

+  NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));

+

+  //

+  // Fill in the ICMP header, Target address, and Source link-layer address.

+  //

+  IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);

+  ASSERT (IcmpHead != NULL);

+  ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));

+  IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT;

+  IcmpHead->Head.Code = 0;

+

+  Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);

+  ASSERT (Target != NULL);

+  IP6_COPY_ADDRESS (Target, TargetIp6Address);

+

+  LinkLayerOption = NULL;

+  if (!IsDAD) {

+

+    //

+    // Fill in the source link-layer address option

+    //

+    LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (

+                                                  Packet,

+                                                  sizeof (IP6_ETHER_ADDR_OPTION),

+                                                  FALSE

+                                                  );

+    ASSERT (LinkLayerOption != NULL);

+    LinkLayerOption->Type   = Ip6OptionEtherSource;

+    LinkLayerOption->Length = 1;

+    CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);

+  }

+

+  //

+  // Create a Neighbor Cache entry in the INCOMPLETE state when performing

+  // address resolution.

+  //

+  if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) {

+    Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);

+    if (Neighbor == NULL) {

+      Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL);

+      ASSERT (Neighbor != NULL);

+    }

+  }

+

+  //

+  // Transmit the packet

+  //

+  return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);

+}

+

+/**

+  Process the Neighbor Solicitation message. The message may be sent for Duplicate

+  Address Detection or Address Resolution.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the message.

+  @param[in]  Packet             The content of the message with IP head removed.

+

+  @retval EFI_SUCCESS            The packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.

+  @retval Others                 Failed to process the packet.

+

+**/

+EFI_STATUS

+Ip6ProcessNeighborSolicit (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_INFORMATION_HEAD Icmp;

+  EFI_IPv6_ADDRESS          Target;

+  IP6_ETHER_ADDR_OPTION     LinkLayerOption;

+  BOOLEAN                   IsDAD;

+  BOOLEAN                   IsUnicast;

+  BOOLEAN                   IsMaintained;

+  IP6_DAD_ENTRY             *DupAddrDetect;

+  IP6_INTERFACE             *IpIf;

+  IP6_NEIGHBOR_ENTRY        *Neighbor;

+  BOOLEAN                   Solicited;

+  BOOLEAN                   UpdateCache;

+  EFI_IPv6_ADDRESS          Dest;

+  UINT16                    OptionLen;

+  UINT8                     *Option;

+  BOOLEAN                   Provided;

+  EFI_STATUS                Status;

+  VOID                      *MacAddress;

+

+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

+  NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);

+

+  //

+  // Perform Message Validation:

+  // The IP Hop Limit field has a value of 255, i.e., the packet

+  // could not possibly have been forwarded by a router.

+  // ICMP Code is 0.

+  // Target Address is not a multicast address.

+  //

+  Status = EFI_INVALID_PARAMETER;

+

+  if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {

+    goto Exit;

+  }

+

+  //

+  // ICMP length is 24 or more octets.

+  //

+  OptionLen = 0;

+  if (Head->PayloadLength < IP6_ND_LENGTH) {

+    goto Exit;

+  } else {

+    OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);

+    Option    = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);

+

+    //

+    // All included options should have a length that is greater than zero.

+    //

+    if (!Ip6IsNDOptionValid (Option, OptionLen)) {

+      goto Exit;

+    }

+  }

+

+  IsDAD        = NetIp6IsUnspecifiedAddr (&Head->SourceAddress);

+  IsUnicast    = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress);

+  IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL);

+

+  Provided = FALSE;

+  if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {

+    NetbufCopy (

+      Packet,

+      IP6_ND_LENGTH,

+      sizeof (IP6_ETHER_ADDR_OPTION),

+      (UINT8 *) &LinkLayerOption

+      );

+    //

+    // The solicitation for neighbor discovery should include a source link-layer

+    // address option. If the option is not recognized, silently ignore it.

+    //

+    if (LinkLayerOption.Type == Ip6OptionEtherSource) {

+      if (IsDAD) {

+        //

+        // If the IP source address is the unspecified address, the source

+        // link-layer address option must not be included in the message.

+        //

+        goto Exit;

+      }

+

+      Provided = TRUE;

+    }

+  }

+

+  //

+  // If the IP source address is the unspecified address, the IP

+  // destination address is a solicited-node multicast address.

+  //

+  if (IsDAD && IsUnicast) {

+    goto Exit;

+  }

+

+  //

+  // If the target address is tentative, and the source address is a unicast address,

+  // the solicitation's sender is performing address resolution on the target;

+  //  the solicitation should be silently ignored.

+  //

+  if (!IsDAD && !IsMaintained) {

+    goto Exit;

+  }

+

+  //

+  // If received unicast neighbor solicitation but destination is not this node,

+  // drop the packet.

+  //

+  if (IsUnicast && !IsMaintained) {

+    goto Exit;

+  }

+

+  //

+  // In DAD, when target address is a tentative address,

+  // process the received neighbor solicitation message but not send out response.

+  //

+  if (IsDAD && !IsMaintained) {

+    DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);

+    if (DupAddrDetect != NULL) {

+      if (DupAddrDetect->Transmit == 0) {

+        //

+        // The NS is from another node to performing DAD on the same address since

+        // we haven't send out any NS yet. Fail DAD for the tentative address.

+        //

+        Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);

+        Status = EFI_ICMP_ERROR;

+        goto Exit;

+      }

+

+      //

+      // Check the MAC address of the incoming packet.

+      //

+      if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) {

+        goto Exit;

+      }

+

+      MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress;

+      if (MacAddress != NULL) {

+        if (CompareMem (

+              MacAddress,

+              &IpSb->SnpMode.CurrentAddress,

+              IpSb->SnpMode.HwAddressSize

+              ) != 0) {

+          //

+          // The NS is from another node to performing DAD on the same address.

+          // Fail DAD for the tentative address.

+          //

+          Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);

+          Status = EFI_ICMP_ERROR;

+        } else {

+          //

+          // The below layer loopback the NS we sent. Record it and wait for more.

+          //

+          DupAddrDetect->Receive++;

+          Status = EFI_SUCCESS;

+        }

+      }

+    }

+    goto Exit;

+  }

+

+  //

+  // If the solicitation does not contain a link-layer address, DO NOT create or

+  // update the neighbor cache entries.

+  //

+  if (Provided) {

+    Neighbor    = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);

+    UpdateCache = FALSE;

+

+    if (Neighbor == NULL) {

+      Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL);

+      if (Neighbor == NULL) {

+        Status = EFI_OUT_OF_RESOURCES;

+        goto Exit;

+      }

+      UpdateCache = TRUE;

+    } else {

+      if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) {

+        UpdateCache = TRUE;

+      }

+    }

+

+    if (UpdateCache) {

+      Neighbor->State = EfiNeighborStale;

+      Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;

+      CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);

+      //

+      // Send queued packets if exist.

+      //

+      Neighbor->CallBack ((VOID *) Neighbor);

+    }

+  }

+

+  //

+  // Sends a Neighbor Advertisement as response.

+  // Set the Router flag to zero since the node is a host.

+  // If the source address of the solicitation is unspeicifed, and target address

+  // is one of the maintained address, reply a unsolicited multicast advertisement.

+  //

+  if (IsDAD && IsMaintained) {

+    Solicited = FALSE;

+    Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest);

+  } else {

+    Solicited = TRUE;

+    IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress);

+  }

+

+  Status = Ip6SendNeighborAdvertise (

+             IpSb,

+             &Target,

+             &Dest,

+             &Target,

+             &IpSb->SnpMode.CurrentAddress,

+             FALSE,

+             TRUE,

+             Solicited

+             );

+Exit:

+  NetbufFree (Packet);

+  return Status;

+}

+

+/**

+  Process the Neighbor Advertisement message.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the message.

+  @param[in]  Packet             The content of the message with IP head removed.

+

+  @retval EFI_SUCCESS            The packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.

+  @retval Others                 Failed to process the packet.

+

+**/

+EFI_STATUS

+Ip6ProcessNeighborAdvertise (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_INFORMATION_HEAD Icmp;

+  EFI_IPv6_ADDRESS          Target;

+  IP6_ETHER_ADDR_OPTION     LinkLayerOption;

+  BOOLEAN                   Provided;

+  INTN                      Compare;

+  IP6_NEIGHBOR_ENTRY        *Neighbor;

+  IP6_DEFAULT_ROUTER        *DefaultRouter;

+  BOOLEAN                   Solicited;

+  BOOLEAN                   IsRouter;

+  BOOLEAN                   Override;

+  IP6_DAD_ENTRY             *DupAddrDetect;

+  IP6_INTERFACE             *IpIf;

+  UINT16                    OptionLen;

+  UINT8                     *Option;

+  EFI_STATUS                Status;

+

+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

+  NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);

+

+  //

+  // Validate the incoming Neighbor Advertisement

+  //

+  Status = EFI_INVALID_PARAMETER;

+  //

+  // The IP Hop Limit field has a value of 255, i.e., the packet

+  // could not possibly have been forwarded by a router.

+  // ICMP Code is 0.

+  // Target Address is not a multicast address.

+  //

+  if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {

+    goto Exit;

+  }

+

+  //

+  // ICMP length is 24 or more octets.

+  //

+  Provided  = FALSE;

+  OptionLen = 0;

+  if (Head->PayloadLength < IP6_ND_LENGTH) {

+    goto Exit;

+  } else {

+    OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);

+    Option    = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);

+

+    //

+    // All included options should have a length that is greater than zero.

+    //

+    if (!Ip6IsNDOptionValid (Option, OptionLen)) {

+      goto Exit;

+    }

+  }

+

+  //

+  // If the IP destination address is a multicast address, Solicited Flag is ZERO.

+  //

+  Solicited = FALSE;

+  if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) {

+    Solicited = TRUE;

+  }

+  if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) {

+    goto Exit;

+  }

+

+  //

+  // DAD - Check whether the Target is one of our tentative address.

+  //

+  DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);

+  if (DupAddrDetect != NULL) {

+    //

+    // DAD fails, some other node is using this address.

+    //

+    NetbufFree (Packet);

+    Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);

+    return EFI_ICMP_ERROR;

+  }

+

+  //

+  // Search the Neighbor Cache for the target's entry. If no entry exists,

+  // the advertisement should be silently discarded.

+  //

+  Neighbor = Ip6FindNeighborEntry (IpSb, &Target);

+  if (Neighbor == NULL) {

+    goto Exit;

+  }

+

+  //

+  // Get IsRouter Flag and Override Flag

+  //

+  IsRouter = FALSE;

+  Override = FALSE;

+  if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) {

+    IsRouter = TRUE;

+  }

+  if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) {

+    Override = TRUE;

+  }

+

+  //

+  // Check whether link layer option is included.

+  //

+  if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {

+    NetbufCopy (

+      Packet,

+      IP6_ND_LENGTH,

+      sizeof (IP6_ETHER_ADDR_OPTION),

+      (UINT8 *) &LinkLayerOption

+      );

+

+    if (LinkLayerOption.Type == Ip6OptionEtherTarget) {

+      Provided = TRUE;

+    }

+  }

+

+  Compare = 0;

+  if (Provided) {

+    Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);

+  }

+

+  if (!Neighbor->IsRouter && IsRouter) {

+    DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);

+    if (DefaultRouter != NULL) {

+      DefaultRouter->NeighborCache = Neighbor;

+    }

+  }

+

+  if (Neighbor->State == EfiNeighborInComplete) {

+    //

+    // If the target's Neighbor Cache entry is in INCOMPLETE state and no

+    // Target Link-Layer address option is included while link layer has

+    // address, the message should be silently discarded.

+    //

+    if (!Provided) {

+      goto Exit;

+    }

+    //

+    // Update the Neighbor Cache

+    //

+    CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);

+    if (Solicited) {

+      Neighbor->State = EfiNeighborReachable;

+      Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);

+    } else {

+      Neighbor->State = EfiNeighborStale;

+      Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;

+      //

+      // Send any packets queued for the neighbor awaiting address resolution.

+      //

+      Neighbor->CallBack ((VOID *) Neighbor);

+    }

+

+    Neighbor->IsRouter = IsRouter;

+

+  } else {

+    if (!Override && Compare != 0) {

+      //

+      // When the Override Flag is clear and supplied link-layer address differs from

+      // that in the cache, if the state of the entry is not REACHABLE, ignore the

+      // message. Otherwise set it to STALE but do not update the entry in any

+      // other way.

+      //

+      if (Neighbor->State == EfiNeighborReachable) {

+        Neighbor->State = EfiNeighborStale;

+        Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;

+      }

+    } else {

+      if (Compare != 0) {

+        CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);

+      }

+      //

+      // Update the entry's state

+      //

+      if (Solicited) {

+        Neighbor->State = EfiNeighborReachable;

+        Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);

+      } else {

+        if (Compare != 0) {

+          Neighbor->State = EfiNeighborStale;

+          Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;

+        }

+      }

+

+      //

+      // When IsRouter is changed from TRUE to FALSE, remove the router from the

+      // Default Router List and remove the Destination Cache entries for all destinations

+      // using the neighbor as a router.

+      //

+      if (Neighbor->IsRouter && !IsRouter) {

+        DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);

+        if (DefaultRouter != NULL) {

+          Ip6DestroyDefaultRouter (IpSb, DefaultRouter);

+        }

+      }

+

+      Neighbor->IsRouter = IsRouter;

+    }

+  }

+

+  if (Neighbor->State == EfiNeighborReachable) {

+    Neighbor->CallBack ((VOID *) Neighbor);

+  }

+

+  Status = EFI_SUCCESS;

+

+Exit:

+  NetbufFree (Packet);

+  return Status;

+}

+

+/**

+  Process the Router Advertisement message according to RFC4861.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the message.

+  @param[in]  Packet             The content of the message with the IP head removed.

+

+  @retval EFI_SUCCESS            The packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the

+                                 operation.

+  @retval Others                 Failed to process the packet.

+

+**/

+EFI_STATUS

+Ip6ProcessRouterAdvertise (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_INFORMATION_HEAD Icmp;

+  UINT32                    ReachableTime;

+  UINT32                    RetransTimer;

+  UINT16                    RouterLifetime;

+  UINT16                    Offset;

+  UINT8                     Type;

+  UINT8                     Length;

+  IP6_ETHER_ADDR_OPTION     LinkLayerOption;

+  UINT32                    Fourth;

+  UINT8                     CurHopLimit;

+  BOOLEAN                   Mflag;

+  BOOLEAN                   Oflag;

+  IP6_DEFAULT_ROUTER        *DefaultRouter;

+  IP6_NEIGHBOR_ENTRY        *NeighborCache;

+  EFI_MAC_ADDRESS           LinkLayerAddress;

+  IP6_MTU_OPTION            MTUOption;

+  IP6_PREFIX_INFO_OPTION    PrefixOption;

+  IP6_PREFIX_LIST_ENTRY     *PrefixList;

+  BOOLEAN                   OnLink;

+  BOOLEAN                   Autonomous;

+  EFI_IPv6_ADDRESS          StatelessAddress;

+  EFI_STATUS                Status;

+  UINT16                    OptionLen;

+  UINT8                     *Option;

+  INTN                      Result;

+

+  Status = EFI_INVALID_PARAMETER;

+

+  if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) {

+    //

+    // Skip the process below as it's not required under the current policy.

+    //

+    goto Exit;

+  }

+

+  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

+

+  //

+  // Validate the incoming Router Advertisement

+  //

+

+  //

+  // The IP source address must be a link-local address

+  //

+  if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {

+    goto Exit;

+  }

+  //

+  // The IP Hop Limit field has a value of 255, i.e. the packet

+  // could not possibly have been forwarded by a router.

+  // ICMP Code is 0.

+  // ICMP length (derived from the IP length) is 16 or more octets.

+  //

+  if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 ||

+      Head->PayloadLength < IP6_RA_LENGTH) {

+    goto Exit;

+  }

+

+  //

+  // All included options have a length that is greater than zero.

+  //

+  OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH);

+  Option    = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL);

+

+  if (!Ip6IsNDOptionValid (Option, OptionLen)) {

+    goto Exit;

+  }

+

+  //

+  // Process Fourth field.

+  // In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag,

+  // and Router Lifetime (16 bit).

+  //

+

+  Fourth = NTOHL (Icmp.Fourth);

+  CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16));

+

+  //

+  // If the source address already in the default router list, update it.

+  // Otherwise create a new entry.

+  // A Lifetime of zero indicates that the router is not a default router.

+  //

+  DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress);

+  if (DefaultRouter == NULL) {

+    if (RouterLifetime != 0) {

+      DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime);

+      if (DefaultRouter == NULL) {

+        Status = EFI_OUT_OF_RESOURCES;

+        goto Exit;

+      }

+    }

+  } else {

+    if (RouterLifetime != 0) {

+      DefaultRouter->Lifetime = RouterLifetime;

+      //

+      // Check the corresponding neighbor cache entry here.

+      //

+      if (DefaultRouter->NeighborCache == NULL) {

+        DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);

+      }

+    } else {

+      //

+      // If the address is in the host's default router list and the router lifetime is zero,

+      // immediately time-out the entry.

+      //

+      Ip6DestroyDefaultRouter (IpSb, DefaultRouter);

+    }

+  }

+

+  CurHopLimit = *((UINT8 *) &Fourth + 3);

+  if (CurHopLimit != 0) {

+    IpSb->CurHopLimit = CurHopLimit;

+  }

+

+  Mflag = FALSE;

+  Oflag = FALSE;

+  if ((*((UINT8 *) &Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) {

+    Mflag = TRUE;

+  } else {

+    if ((*((UINT8 *) &Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) {

+      Oflag = TRUE;

+    }

+  }

+

+  if (Mflag || Oflag) {

+    //

+    // Use Ip6Config to get available addresses or other configuration from DHCP.

+    //

+    Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag);

+  }

+

+  //

+  // Process Reachable Time and Retrans Timer fields.

+  //

+  NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *) &ReachableTime);

+  NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *) &RetransTimer);

+  ReachableTime = NTOHL (ReachableTime);

+  RetransTimer  = NTOHL (RetransTimer);

+

+  if (ReachableTime != 0 && ReachableTime != IpSb->BaseReachableTime) {

+    //

+    // If new value is not unspecified and differs from the previous one, record it

+    // in BaseReachableTime and recompute a ReachableTime.

+    //

+    IpSb->BaseReachableTime = ReachableTime;

+    Ip6UpdateReachableTime (IpSb);

+  }

+

+  if (RetransTimer != 0) {

+    IpSb->RetransTimer = RetransTimer;

+  }

+

+  //

+  // IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists.

+  //

+  NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);

+  if (NeighborCache != NULL) {

+    NeighborCache->IsRouter = TRUE;

+  }

+

+  //

+  // If an valid router advertisment is received, stops router solicitation.

+  //

+  IpSb->RouterAdvertiseReceived = TRUE;

+

+  //

+  // The only defined options that may appear are the Source

+  // Link-Layer Address, Prefix information and MTU options.

+  // All included options have a length that is greater than zero.

+  //

+  Offset = 16;

+  while (Offset < Head->PayloadLength) {

+    NetbufCopy (Packet, Offset, sizeof (UINT8), &Type);

+    switch (Type) {

+    case Ip6OptionEtherSource:

+      //

+      // Update the neighbor cache

+      //

+      NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption);

+      if (LinkLayerOption.Length <= 0) {

+        goto Exit;

+      }

+

+      ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS));

+      CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6);

+

+      if (NeighborCache == NULL) {

+        NeighborCache = Ip6CreateNeighborEntry (

+                          IpSb,

+                          Ip6OnArpResolved,

+                          &Head->SourceAddress,

+                          &LinkLayerAddress

+                          );

+        if (NeighborCache == NULL) {

+          Status = EFI_OUT_OF_RESOURCES;

+          goto Exit;

+        }

+        NeighborCache->IsRouter = TRUE;

+        NeighborCache->State    = EfiNeighborStale;

+        NeighborCache->Ticks    = (UINT32) IP6_INFINIT_LIFETIME;

+      } else {

+        Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6);

+

+        //

+        // If the link-local address is the same as that already in the cache,

+        // the cache entry's state remains unchanged. Otherwise update the

+        // reachability state to STALE.

+        //

+        if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {

+          CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6);

+

+          NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;

+

+          if (NeighborCache->State == EfiNeighborInComplete) {

+            //

+            // Send queued packets if exist.

+            //

+            NeighborCache->State = EfiNeighborStale;

+            NeighborCache->CallBack ((VOID *) NeighborCache);

+          } else {

+            NeighborCache->State = EfiNeighborStale;

+          }

+        }

+      }

+

+      Offset = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8);

+      break;

+    case Ip6OptionPrefixInfo:

+      NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption);

+      if (PrefixOption.Length != 4) {

+        goto Exit;

+      }

+      PrefixOption.ValidLifetime     = NTOHL (PrefixOption.ValidLifetime);

+      PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime);

+

+      //

+      // Get L and A flag, recorded in the lower 2 bits of Reserved1

+      //

+      OnLink = FALSE;

+      if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) {

+        OnLink = TRUE;

+      }

+      Autonomous = FALSE;

+      if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) {

+        Autonomous = TRUE;

+      }

+

+      //

+      // If the prefix is the link-local prefix, silently ignore the prefix option.

+      //

+      if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH &&

+          NetIp6IsLinkLocalAddr (&PrefixOption.Prefix)

+          ) {

+        Offset += sizeof (IP6_PREFIX_INFO_OPTION);

+        break;

+      }

+      //

+      // Do following if on-link flag is set according to RFC4861.

+      //

+      if (OnLink) {

+        PrefixList = Ip6FindPrefixListEntry (

+                       IpSb,

+                       TRUE,

+                       PrefixOption.PrefixLength,

+                       &PrefixOption.Prefix

+                       );

+        //

+        // Create a new entry for the prefix, if the ValidLifetime is zero,

+        // silently ignore the prefix option.

+        //

+        if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) {

+          PrefixList = Ip6CreatePrefixListEntry (

+                         IpSb,

+                         TRUE,

+                         PrefixOption.ValidLifetime,

+                         PrefixOption.PreferredLifetime,

+                         PrefixOption.PrefixLength,

+                         &PrefixOption.Prefix

+                         );

+          if (PrefixList == NULL) {

+            Status = EFI_OUT_OF_RESOURCES;

+            goto Exit;

+          }

+        } else if (PrefixList != NULL) {

+          if (PrefixOption.ValidLifetime != 0) {

+            PrefixList->ValidLifetime = PrefixOption.ValidLifetime;

+          } else {

+            //

+            // If the prefix exists and incoming ValidLifetime is zero, immediately

+            // remove the prefix.

+            Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);

+          }

+        }

+      }

+

+      //

+      // Do following if Autonomous flag is set according to RFC4862.

+      //

+      if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) {

+        PrefixList = Ip6FindPrefixListEntry (

+                       IpSb,

+                       FALSE,

+                       PrefixOption.PrefixLength,

+                       &PrefixOption.Prefix

+                       );

+        //

+        // Create a new entry for the prefix, and form an address by prefix + interface id

+        // If the sum of the prefix length and interface identifier length

+        // does not equal 128 bits, the Prefix Information option MUST be ignored.

+        //

+        if (PrefixList == NULL &&

+            PrefixOption.ValidLifetime != 0 &&

+            PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128

+            ) {

+          //

+          // Form the address in network order.

+          //

+          CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64));

+          CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64));

+

+          //

+          // If the address is not yet in the assigned address list, adds it into.

+          //

+          if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) {

+            //

+            // And also not in the DAD process, check its uniqeness firstly.

+            //

+            if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) {

+              Status = Ip6SetAddress (

+                         IpSb->DefaultInterface,

+                         &StatelessAddress,

+                         FALSE,

+                         PrefixOption.PrefixLength,

+                         PrefixOption.ValidLifetime,

+                         PrefixOption.PreferredLifetime,

+                         NULL,

+                         NULL

+                         );

+              if (EFI_ERROR (Status)) {

+                goto Exit;

+              }

+            }

+          }

+

+          //

+          // Adds the prefix option to stateless prefix option list.

+          //

+          PrefixList = Ip6CreatePrefixListEntry (

+                         IpSb,

+                         FALSE,

+                         PrefixOption.ValidLifetime,

+                         PrefixOption.PreferredLifetime,

+                         PrefixOption.PrefixLength,

+                         &PrefixOption.Prefix

+                         );

+          if (PrefixList == NULL) {

+            Status = EFI_OUT_OF_RESOURCES;

+            goto Exit;

+          }

+        } else if (PrefixList != NULL) {

+

+          //

+          // Reset the preferred lifetime of the address if the advertised prefix exists.

+          // Perform specific action to valid lifetime together.

+          //

+          PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime;

+          if ((PrefixOption.ValidLifetime > 7200) ||

+              (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) {

+            //

+            // If the received Valid Lifetime is greater than 2 hours or

+            // greater than RemainingLifetime, set the valid lifetime of the

+            // corresponding address to the advertised Valid Lifetime.

+            //

+            PrefixList->ValidLifetime = PrefixOption.ValidLifetime;

+

+          } else if (PrefixList->ValidLifetime <= 7200) {

+            //

+            // If RemainingLifetime is less than or equls to 2 hours, ignore the

+            // Prefix Information option with regards to the valid lifetime.

+            // TODO: If this option has been authenticated, set the valid lifetime.

+            //

+          } else {

+            //

+            // Otherwise, reset the valid lifetime of the corresponding

+            // address to 2 hours.

+            //

+            PrefixList->ValidLifetime = 7200;

+          }

+        }

+      }

+

+      Offset += sizeof (IP6_PREFIX_INFO_OPTION);

+      break;

+    case Ip6OptionMtu:

+      NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption);

+      if (MTUOption.Length != 1) {

+        goto Exit;

+      }

+

+      //

+      // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order

+      // to omit implementation of Path MTU Discovery. Thus ignore the MTU option

+      // in Router Advertisement.

+      //

+

+      Offset += sizeof (IP6_MTU_OPTION);

+      break;

+    default:

+      //

+      // Silently ignore unrecognized options

+      //

+      NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length);

+      if (Length <= 0) {

+        goto Exit;

+      }

+

+      Offset = (UINT16) (Offset + (UINT16) Length * 8);

+      break;

+    }

+  }

+

+  Status = EFI_SUCCESS;

+

+Exit:

+  NetbufFree (Packet);

+  return Status;

+}

+

+/**

+  Process the ICMPv6 redirect message. Find the instance, then update

+  its route cache.

+

+  @param[in]  IpSb               The IP6 service binding instance that received

+                                 the packet.

+  @param[in]  Head               The IP head of the received ICMPv6 packet.

+  @param[in]  Packet             The content of the ICMPv6 redirect packet with

+                                 the IP head removed.

+

+  @retval EFI_INVALID_PARAMETER  The parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES   Insuffcient resources to complete the

+                                 operation.

+  @retval EFI_SUCCESS            Successfully updated the route caches.

+

+**/

+EFI_STATUS

+Ip6ProcessRedirect (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  )

+{

+  IP6_ICMP_INFORMATION_HEAD *Icmp;

+  EFI_IPv6_ADDRESS          *Target;

+  EFI_IPv6_ADDRESS          *IcmpDest;

+  UINT8                     *Option;

+  UINT16                    OptionLen;

+  IP6_ROUTE_ENTRY           *RouteEntry;

+  IP6_ROUTE_CACHE_ENTRY     *RouteCache;

+  IP6_NEIGHBOR_ENTRY        *NeighborCache;

+  INT32                     Length;

+  UINT8                     OptLen;

+  IP6_ETHER_ADDR_OPTION     *LinkLayerOption;

+  EFI_MAC_ADDRESS           Mac;

+  UINT32                    Index;

+  BOOLEAN                   IsRouter;

+  EFI_STATUS                Status;

+  INTN                      Result;

+

+  Status = EFI_INVALID_PARAMETER;

+

+  Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL);

+  if (Icmp == NULL) {

+    goto Exit;

+  }

+

+  //

+  // Validate the incoming Redirect message

+  //

+

+  //

+  // The IP Hop Limit field has a value of 255, i.e. the packet

+  // could not possibly have been forwarded by a router.

+  // ICMP Code is 0.

+  // ICMP length (derived from the IP length) is 40 or more octets.

+  //

+  if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 ||

+      Head->PayloadLength < IP6_REDITECT_LENGTH) {

+    goto Exit;

+  }

+

+  //

+  // The IP source address must be a link-local address

+  //

+  if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {

+    goto Exit;

+  }

+

+  //

+  // The dest of this ICMP redirect message is not us.

+  //

+  if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {

+    goto Exit;

+  }

+

+  //

+  // All included options have a length that is greater than zero.

+  //

+  OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH);

+  Option    = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL);

+

+  if (!Ip6IsNDOptionValid (Option, OptionLen)) {

+    goto Exit;

+  }

+

+  Target   = (EFI_IPv6_ADDRESS *) (Icmp + 1);

+  IcmpDest = Target + 1;

+

+  //

+  // The ICMP Destination Address field in the redirect message does not contain

+  // a multicast address.

+  //

+  if (IP6_IS_MULTICAST (IcmpDest)) {

+    goto Exit;

+  }

+

+  //

+  // The ICMP Target Address is either a link-local address (when redirected to

+  // a router) or the same as the ICMP Destination Address (when redirected to

+  // the on-link destination).

+  //

+  IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest);

+  if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) {

+    goto Exit;

+  }

+

+  //

+  // Check the options. The only interested option here is the target-link layer

+  // address option.

+  //

+  Length          = Packet->TotalSize - 40;

+  Option          = (UINT8 *) (IcmpDest + 1);

+  LinkLayerOption = NULL;

+  while (Length > 0) {

+    switch (*Option) {

+    case Ip6OptionEtherTarget:

+

+      LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) Option;

+      OptLen          = LinkLayerOption->Length;

+      if (OptLen != 1) {

+        //

+        // For ethernet, the length must be 1.

+        //

+        goto Exit;

+      }

+      break;

+

+    default:

+

+      OptLen = *(Option + 1);

+      if (OptLen == 0) {

+        //

+        // A length of 0 is invalid.

+        //

+        goto Exit;

+      }

+      break;

+    }

+

+    Length -= 8 * OptLen;

+    Option += 8 * OptLen;

+  }

+

+  if (Length != 0) {

+    goto Exit;

+  }

+

+  //

+  // The IP source address of the Redirect is the same as the current

+  // first-hop router for the specified ICMP Destination Address.

+  //

+  RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress);

+  if (RouteCache != NULL) {

+    if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) {

+      //

+      // The source of this Redirect message must match the NextHop of the

+      // corresponding route cache entry.

+      //

+      goto Exit;

+    }

+

+    //

+    // Update the NextHop.

+    //

+    IP6_COPY_ADDRESS (&RouteCache->NextHop, Target);

+

+    if (!IsRouter) {

+      RouteEntry = (IP6_ROUTE_ENTRY *) RouteCache->Tag;

+      RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE;

+    }

+

+  } else {

+    //

+    // Get the Route Entry.

+    //

+    RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL);

+    if (RouteEntry == NULL) {

+      RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL);

+      if (RouteEntry == NULL) {

+        Status = EFI_OUT_OF_RESOURCES;

+        goto Exit;

+      }

+    }

+

+    if (!IsRouter) {

+      RouteEntry->Flag = IP6_DIRECT_ROUTE;

+    }

+

+    //

+    // Create a route cache for this.

+    //

+    RouteCache = Ip6CreateRouteCacheEntry (

+                   IcmpDest,

+                   &Head->DestinationAddress,

+                   Target,

+                   (UINTN) RouteEntry

+                   );

+    if (RouteCache == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto Exit;

+    }

+

+    //

+    // Insert the newly created route cache entry.

+    //

+    Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress);

+    InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link);

+  }

+

+  //

+  // Try to locate the neighbor cache for the Target.

+  //

+  NeighborCache = Ip6FindNeighborEntry (IpSb, Target);

+

+  if (LinkLayerOption != NULL) {

+    if (NeighborCache == NULL) {

+      //

+      // Create a neighbor cache for the Target.

+      //

+      ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS));

+      CopyMem (&Mac, LinkLayerOption->EtherAddr, 6);

+      NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac);

+      if (NeighborCache == NULL) {

+        //

+        // Just report a success here. The neighbor cache can be created in

+        // some other place.

+        //

+        Status = EFI_SUCCESS;

+        goto Exit;

+      }

+

+      NeighborCache->State = EfiNeighborStale;

+      NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;

+    } else {

+      Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6);

+

+      //

+      // If the link-local address is the same as that already in the cache,

+      // the cache entry's state remains unchanged. Otherwise update the

+      // reachability state to STALE.

+      //

+      if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {

+        CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6);

+

+        NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;

+

+        if (NeighborCache->State == EfiNeighborInComplete) {

+          //

+          // Send queued packets if exist.

+          //

+          NeighborCache->State = EfiNeighborStale;

+          NeighborCache->CallBack ((VOID *) NeighborCache);

+        } else {

+          NeighborCache->State = EfiNeighborStale;

+        }

+      }

+    }

+  }

+

+  if (NeighborCache != NULL && IsRouter) {

+    //

+    // The Target is a router, set IsRouter to TRUE.

+    //

+    NeighborCache->IsRouter = TRUE;

+  }

+

+  Status = EFI_SUCCESS;

+

+Exit:

+  NetbufFree (Packet);

+  return Status;

+}

+

+/**

+  Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().

+

+  @param[in]  IpSb               The IP6 service binding instance.

+  @param[in]  TargetIp6Address   Pointer to Target IPv6 address.

+  @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.

+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor

+                                 cache. It will be deleted after Timeout. A value of zero means that

+                                 the entry is permanent. A non-zero value means that the entry is

+                                 dynamic.

+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will

+                                 be overridden and updated; if FALSE, and if a

+                                 corresponding cache entry already existed, EFI_ACCESS_DENIED

+                                 will be returned.

+

+  @retval  EFI_SUCCESS           The neighbor cache entry has been added.

+  @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache

+                                 due to insufficient resources.

+  @retval  EFI_NOT_FOUND         TargetLinkAddress is NULL.

+  @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,

+                                 and that entry is tagged as un-overridden (when DeleteFlag

+                                 is FALSE).

+

+**/

+EFI_STATUS

+Ip6AddNeighbor (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,

+  IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,

+  IN UINT32                 Timeout,

+  IN BOOLEAN                Override

+  )

+{

+  IP6_NEIGHBOR_ENTRY        *Neighbor;

+

+  Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);

+  if (Neighbor != NULL) {

+    if (!Override) {

+      return EFI_ACCESS_DENIED;

+    } else {

+      if (TargetLinkAddress != NULL) {

+        IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress);

+      }

+    }

+  } else {

+    if (TargetLinkAddress == NULL) {

+      return EFI_NOT_FOUND;

+    }

+

+    Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress);

+    if (Neighbor == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  }

+

+  Neighbor->State = EfiNeighborReachable;

+

+  if (Timeout != 0) {

+    Neighbor->Ticks   = IP6_GET_TICKS (Timeout / TICKS_PER_MS);

+    Neighbor->Dynamic = TRUE;

+  } else {

+    Neighbor->Ticks   = (UINT32) IP6_INFINIT_LIFETIME;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().

+

+  @param[in]  IpSb               The IP6 service binding instance.

+  @param[in]  TargetIp6Address   Pointer to Target IPv6 address.

+  @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.

+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor

+                                 cache. It will be deleted after Timeout. A value of zero means that

+                                 the entry is permanent. A non-zero value means that the entry is

+                                 dynamic.

+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will

+                                 be overridden and updated; if FALSE, and if a

+                                 corresponding cache entry already existed, EFI_ACCESS_DENIED

+                                 will be returned.

+

+  @retval  EFI_SUCCESS           The neighbor cache entry has been updated or deleted.

+  @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache.

+

+**/

+EFI_STATUS

+Ip6DelNeighbor (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,

+  IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,

+  IN UINT32                 Timeout,

+  IN BOOLEAN                Override

+  )

+{

+  IP6_NEIGHBOR_ENTRY        *Neighbor;

+

+  Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);

+  if (Neighbor == NULL) {

+    return EFI_NOT_FOUND;

+  }

+

+  RemoveEntryList (&Neighbor->Link);

+  FreePool (Neighbor);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.

+  This time routine handles DAD module and neighbor state transition.

+  It is also responsible for sending out router solicitations.

+

+  @param[in]  Event                 The IP6 service instance's heartbeat timer.

+  @param[in]  Context               The IP6 service instance.

+

+**/

+VOID

+EFIAPI

+Ip6NdFasterTimerTicking (

+  IN EFI_EVENT              Event,

+  IN VOID                   *Context

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  LIST_ENTRY                *Entry2;

+  IP6_INTERFACE             *IpIf;

+  IP6_DELAY_JOIN_LIST       *DelayNode;

+  EFI_IPv6_ADDRESS          Source;

+  IP6_DAD_ENTRY             *DupAddrDetect;

+  EFI_STATUS                Status;

+  IP6_NEIGHBOR_ENTRY        *NeighborCache;

+  EFI_IPv6_ADDRESS          Destination;

+  IP6_SERVICE               *IpSb;

+  BOOLEAN                   Flag;

+

+  IpSb = (IP6_SERVICE *) Context;

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS));

+

+  //

+  // A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router

+  // Solicitation messages, each separated by at least

+  // RTR_SOLICITATION_INTERVAL (4) seconds.

+  //

+  if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) &&

+      !IpSb->RouterAdvertiseReceived &&

+      IpSb->SolicitTimer > 0

+      ) {

+    if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) {

+      Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL);

+      if (!EFI_ERROR (Status)) {

+        IpSb->SolicitTimer--;

+        IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL);

+      }

+    }

+  }

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);

+

+    //

+    // Process the delay list to join the solicited-node multicast address.

+    //

+    NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) {

+      DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link);

+      if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) {

+        //

+        // The timer expires, init the duplicate address detection.

+        //

+        Ip6InitDADProcess (

+          DelayNode->Interface,

+          DelayNode->AddressInfo,

+          DelayNode->DadCallback,

+          DelayNode->Context

+          );

+

+        //

+        // Remove the delay node

+        //

+        RemoveEntryList (&DelayNode->Link);

+        FreePool (DelayNode);

+      }

+    }

+

+    //

+    // Process the duplicate address detection list.

+    //

+    NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {

+      DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link);

+

+      if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) {

+        //

+        // The timer expires, check the remaining transmit counts.

+        //

+        if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) {

+          //

+          // Send the Neighbor Solicitation message with

+          // Source - unspecified address, destination - solicited-node multicast address

+          // Target - the address to be validated

+          //

+          Status = Ip6SendNeighborSolicit (

+                     IpSb,

+                     NULL,

+                     &DupAddrDetect->Destination,

+                     &DupAddrDetect->AddressInfo->Address,

+                     NULL

+                     );

+          if (EFI_ERROR (Status)) {

+            return;

+          }

+

+          DupAddrDetect->Transmit++;

+          DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer);

+        } else {

+          //

+          // All required solicitation has been sent out, and the RetransTime after the last

+          // Neighbor Solicit is elapsed, finish the DAD process.

+          //

+          Flag = FALSE;

+          if ((DupAddrDetect->Receive == 0) ||

+              (DupAddrDetect->Transmit == DupAddrDetect->Receive)) {

+            Flag = TRUE;

+          }

+

+          Ip6OnDADFinished (Flag, IpIf, DupAddrDetect);

+        }

+      }

+    }

+  }

+

+  //

+  // Polling the state of Neighbor cache

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {

+    NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);

+

+    switch (NeighborCache->State) {

+    case EfiNeighborInComplete:

+      if (NeighborCache->Ticks > 0) {

+        --NeighborCache->Ticks;

+      }

+

+      //

+      // Retransmit Neighbor Solicitation messages approximately every

+      // RetransTimer milliseconds while awaiting a response.

+      //

+      if (NeighborCache->Ticks == 0) {

+        if (NeighborCache->Transmit > 1) {

+          //

+          // Send out multicast neighbor solicitation for address resolution.

+          // After last neighbor solicitation message has been sent out, wait

+          // for RetransTimer and then remove entry if no response is received.

+          //

+          Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);

+          Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);

+          if (EFI_ERROR (Status)) {

+            return;

+          }

+

+          Status = Ip6SendNeighborSolicit (

+                     IpSb,

+                     &Source,

+                     &Destination,

+                     &NeighborCache->Neighbor,

+                     &IpSb->SnpMode.CurrentAddress

+                     );

+          if (EFI_ERROR (Status)) {

+            return;

+          }

+        }

+

+        //

+        // Update the retransmit times.

+        //

+        if (NeighborCache->Transmit > 0) {

+          --NeighborCache->Transmit;

+          NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);

+        }

+      }

+

+      if (NeighborCache->Transmit == 0) {

+        //

+        // Timeout, send ICMP destination unreachable packet and then remove entry

+        //

+        Status = Ip6FreeNeighborEntry (

+                   IpSb,

+                   NeighborCache,

+                   TRUE,

+                   TRUE,

+                   EFI_ICMP_ERROR,

+                   NULL,

+                   NULL

+                   );

+        if (EFI_ERROR (Status)) {

+          return;

+        }

+      }

+

+      break;

+

+    case EfiNeighborReachable:

+      //

+      // This entry is inserted by EfiIp6Neighbors() as static entry

+      // and will not timeout.

+      //

+      if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) {

+        break;

+      }

+

+      if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {

+        if (NeighborCache->Dynamic) {

+          //

+          // This entry is inserted by EfiIp6Neighbors() as dynamic entry

+          // and will be deleted after timeout.

+          //

+          Status = Ip6FreeNeighborEntry (

+                     IpSb,

+                     NeighborCache,

+                     FALSE,

+                     TRUE,

+                     EFI_TIMEOUT,

+                     NULL,

+                     NULL

+                     );

+          if (EFI_ERROR (Status)) {

+            return;

+          }

+        } else {

+          NeighborCache->State = EfiNeighborStale;

+          NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;

+        }

+      }

+

+      break;

+

+    case EfiNeighborDelay:

+      if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {

+

+        NeighborCache->State    = EfiNeighborProbe;

+        NeighborCache->Ticks    = IP6_GET_TICKS (IpSb->RetransTimer);

+        NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1;

+        //

+        // Send out unicast neighbor solicitation for Neighbor Unreachability Detection

+        //

+        Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);

+        if (EFI_ERROR (Status)) {

+          return;

+        }

+

+        Status = Ip6SendNeighborSolicit (

+                   IpSb,

+                   &Source,

+                   &NeighborCache->Neighbor,

+                   &NeighborCache->Neighbor,

+                   &IpSb->SnpMode.CurrentAddress

+                   );

+        if (EFI_ERROR (Status)) {

+          return;

+        }

+

+        NeighborCache->Transmit--;

+      }

+

+      break;

+

+    case EfiNeighborProbe:

+      if (NeighborCache->Ticks > 0) {

+        --NeighborCache->Ticks;

+      }

+

+      //

+      // Retransmit Neighbor Solicitation messages approximately every

+      // RetransTimer milliseconds while awaiting a response.

+      //

+      if (NeighborCache->Ticks == 0) {

+        if (NeighborCache->Transmit > 1) {

+          //

+          // Send out unicast neighbor solicitation for Neighbor Unreachability

+          // Detection. After last neighbor solicitation message has been sent out,

+          // wait for RetransTimer and then remove entry if no response is received.

+          //

+          Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);

+          if (EFI_ERROR (Status)) {

+            return;

+          }

+

+          Status = Ip6SendNeighborSolicit (

+                     IpSb,

+                     &Source,

+                     &NeighborCache->Neighbor,

+                     &NeighborCache->Neighbor,

+                     &IpSb->SnpMode.CurrentAddress

+                     );

+          if (EFI_ERROR (Status)) {

+            return;

+          }

+        }

+

+        //

+        // Update the retransmit times.

+        //

+        if (NeighborCache->Transmit > 0) {

+          --NeighborCache->Transmit;

+          NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);

+        }

+      }

+

+      if (NeighborCache->Transmit == 0) {

+        //

+        // Delete the neighbor entry.

+        //

+        Status = Ip6FreeNeighborEntry (

+                   IpSb,

+                   NeighborCache,

+                   FALSE,

+                   TRUE,

+                   EFI_TIMEOUT,

+                   NULL,

+                   NULL

+                   );

+        if (EFI_ERROR (Status)) {

+          return;

+        }

+      }

+

+      break;

+

+    default:

+      break;

+    }

+  }

+}

+

+/**

+  The heartbeat timer of ND module in 1 second. This time routine handles following

+  things: 1) maitain default router list; 2) maintain prefix options;

+  3) maintain route caches.

+

+  @param[in]  IpSb              The IP6 service binding instance.

+

+**/

+VOID

+Ip6NdTimerTicking (

+  IN IP6_SERVICE            *IpSb

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_DEFAULT_ROUTER        *DefaultRouter;

+  IP6_PREFIX_LIST_ENTRY     *PrefixOption;

+  UINT8                     Index;

+  IP6_ROUTE_CACHE_ENTRY     *RouteCache;

+

+  //

+  // Decrease the lifetime of default router, if expires remove it from default router list.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) {

+    DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);

+    if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) {

+      if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) {

+        Ip6DestroyDefaultRouter (IpSb, DefaultRouter);

+      }

+    }

+  }

+

+  //

+  // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) {

+    PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);

+    if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {

+      if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) {

+        if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) &&

+            (PrefixOption->PreferredLifetime > 0)

+            ) {

+          --PrefixOption->PreferredLifetime;

+        }

+      } else {

+        Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE);

+      }

+    }

+  }

+

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) {

+    PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);

+    if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {

+      if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) {

+        Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE);

+      }

+    }

+  }

+

+  //

+  // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries.

+  // Remove the entries at the tail of the bucket. These entries

+  // are likely to be used least.

+  // Reclaim frequency is set to 1 second.

+  //

+  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {

+    while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) {

+      Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]);

+      if (Entry == NULL) {

+        break;

+      }

+

+      RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);

+      Ip6FreeRouteCacheEntry (RouteCache);

+      ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0);

+      IpSb->RouteTable->Cache.CacheNum[Index]--;

+    }

+  }

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Nd.h b/NetworkPkg/Ip6Dxe/Ip6Nd.h
new file mode 100644
index 0000000..57fb8fe
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Nd.h
@@ -0,0 +1,750 @@
+/** @file

+  Definition of Neighbor Discovery support routines.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_ND_H__

+#define __EFI_IP6_ND_H__

+

+#define IP6_GET_TICKS(Ms)  (((Ms) + IP6_TIMER_INTERVAL_IN_MS - 1) / IP6_TIMER_INTERVAL_IN_MS)

+

+enum {

+  IP6_INF_ROUTER_LIFETIME        = 0xFFFF,

+

+  IP6_MAX_RTR_SOLICITATION_DELAY = 1000, ///< 1000 milliseconds

+  IP6_MAX_RTR_SOLICITATIONS      = 3,

+  IP6_RTR_SOLICITATION_INTERVAL  = 4000,

+

+  IP6_MIN_RANDOM_FACTOR_SCALED   = 1,

+  IP6_MAX_RANDOM_FACTOR_SCALED   = 3,

+  IP6_RANDOM_FACTOR_SCALE        = 2,

+

+  IP6_MAX_MULTICAST_SOLICIT      = 3,

+  IP6_MAX_UNICAST_SOLICIT        = 3,

+  IP6_MAX_ANYCAST_DELAY_TIME     = 1,

+  IP6_MAX_NEIGHBOR_ADV           = 3,

+  IP6_REACHABLE_TIME             = 30000,

+  IP6_RETRANS_TIMER              = 1000,

+  IP6_DELAY_FIRST_PROBE_TIME     = 5000,

+

+  IP6_MIN_LINK_MTU               = 1280,

+  IP6_MAX_LINK_MTU               = 1500,

+

+  IP6_IS_ROUTER_FLAG             = 0x80,

+  IP6_SOLICITED_FLAG             = 0x40,

+  IP6_OVERRIDE_FLAG              = 0x20,

+

+  IP6_M_ADDR_CONFIG_FLAG         = 0x80,

+  IP6_O_CONFIG_FLAG              = 0x40,

+

+  IP6_ON_LINK_FLAG               = 0x80,

+  IP6_AUTO_CONFIG_FLAG           = 0x40,

+

+  IP6_ND_LENGTH                  = 24,

+  IP6_RA_LENGTH                  = 16,

+  IP6_REDITECT_LENGTH            = 40,

+  IP6_DAD_ENTRY_SIGNATURE        = SIGNATURE_32 ('I', 'P', 'D', 'E')

+};

+

+typedef

+VOID

+(*IP6_ARP_CALLBACK) (

+  VOID                      *Context

+  );

+

+typedef struct _IP6_ETHE_ADDR_OPTION {

+  UINT8                     Type;

+  UINT8                     Length;

+  UINT8                     EtherAddr[6];

+} IP6_ETHER_ADDR_OPTION;

+

+typedef struct _IP6_MTU_OPTION {

+  UINT8                     Type;

+  UINT8                     Length;

+  UINT16                    Reserved;

+  UINT32                    Mtu;

+} IP6_MTU_OPTION;

+

+typedef struct _IP6_PREFIX_INFO_OPTION {

+  UINT8                     Type;

+  UINT8                     Length;

+  UINT8                     PrefixLength;

+  UINT8                     Reserved1;

+  UINT32                    ValidLifetime;

+  UINT32                    PreferredLifetime;

+  UINT32                    Reserved2;

+  EFI_IPv6_ADDRESS          Prefix;

+} IP6_PREFIX_INFO_OPTION;

+

+typedef

+VOID

+(*IP6_DAD_CALLBACK) (

+  IN BOOLEAN           IsDadPassed,

+  IN EFI_IPv6_ADDRESS  *TargetAddress,

+  IN VOID              *Context

+  );

+

+typedef struct _IP6_DAD_ENTRY {

+  UINT32                    Signature;

+  LIST_ENTRY                Link;

+  UINT32                    MaxTransmit;

+  UINT32                    Transmit;

+  UINT32                    Receive;

+  UINT32                    RetransTick;

+  IP6_ADDRESS_INFO          *AddressInfo;

+  EFI_IPv6_ADDRESS          Destination;

+  IP6_DAD_CALLBACK          Callback;

+  VOID                      *Context;

+} IP6_DAD_ENTRY;

+

+typedef struct _IP6_DELAY_JOIN_LIST {

+  LIST_ENTRY                Link;

+  UINT32                    DelayTime; ///< in tick per 50 milliseconds

+  IP6_INTERFACE             *Interface;

+  IP6_ADDRESS_INFO          *AddressInfo;

+  IP6_DAD_CALLBACK          DadCallback;

+  VOID                      *Context;

+} IP6_DELAY_JOIN_LIST;

+

+typedef struct _IP6_NEIGHBOR_ENTRY {

+  LIST_ENTRY                Link;

+  LIST_ENTRY                ArpList;

+  INTN                      RefCnt;

+  BOOLEAN                   IsRouter;

+  BOOLEAN                   ArpFree;

+  BOOLEAN                   Dynamic;

+  EFI_IPv6_ADDRESS          Neighbor;

+  EFI_MAC_ADDRESS           LinkAddress;

+  EFI_IP6_NEIGHBOR_STATE    State;

+  UINT32                    Transmit;

+  UINT32                    Ticks;

+

+  LIST_ENTRY                Frames;

+  IP6_INTERFACE             *Interface;

+  IP6_ARP_CALLBACK          CallBack;

+} IP6_NEIGHBOR_ENTRY;

+

+typedef struct _IP6_DEFAULT_ROUTER {

+  LIST_ENTRY                Link;

+  INTN                      RefCnt;

+  UINT16                    Lifetime;

+  EFI_IPv6_ADDRESS          Router;

+  IP6_NEIGHBOR_ENTRY        *NeighborCache;

+} IP6_DEFAULT_ROUTER;

+

+typedef struct _IP6_PREFIX_LIST_ENTRY {

+  LIST_ENTRY                Link;

+  INTN                      RefCnt;

+  UINT32                    ValidLifetime;

+  UINT32                    PreferredLifetime;

+  UINT8                     PrefixLength;

+  EFI_IPv6_ADDRESS          Prefix;

+} IP6_PREFIX_LIST_ENTRY;

+

+/**

+  Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number

+  of EFI_IP6_NEIGHBOR_CACHE is also returned.

+

+  @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.

+  @param[out] NeighborCount     The number of returned neighbor cache entries.

+  @param[out] NeighborCache     The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.

+

+  @retval EFI_SUCCESS           The EFI_IP6_NEIGHBOR_CACHE successfully built.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.

+

+**/

+EFI_STATUS

+Ip6BuildEfiNeighborCache (

+  IN IP6_PROTOCOL            *IpInstance,

+  OUT UINT32                 *NeighborCount,

+  OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache

+  );

+

+/**

+  Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number

+  of prefix entries is also returned.

+

+  @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.

+  @param[out] PrefixCount       The number of returned prefix entries.

+  @param[out] PrefixTable       The pointer to the array of PrefixTable.

+

+  @retval EFI_SUCCESS           The prefix table successfully built.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the prefix table.

+

+**/

+EFI_STATUS

+Ip6BuildPrefixTable (

+  IN IP6_PROTOCOL           *IpInstance,

+  OUT UINT32                *PrefixCount,

+  OUT EFI_IP6_ADDRESS_INFO  **PrefixTable

+  );

+

+/**

+  Allocate and initialize an IP6 default router entry.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  Ip6Address        The IPv6 address of the default router.

+  @param[in]  RouterLifetime    The lifetime associated with the default

+                                router, in units of seconds.

+

+  @return NULL if it failed to allocate memory for the default router node.

+          Otherwise, point to the created default router node.

+

+**/

+IP6_DEFAULT_ROUTER *

+Ip6CreateDefaultRouter (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Ip6Address,

+  IN UINT16                 RouterLifetime

+  );

+

+/**

+  Destroy an IP6 default router entry.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  DefaultRouter     The to be destroyed IP6_DEFAULT_ROUTER.

+

+**/

+VOID

+Ip6DestroyDefaultRouter (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_DEFAULT_ROUTER     *DefaultRouter

+  );

+

+/**

+  Clean an IP6 default router list.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  DefaultRouter     The to be destroyed IP6_DEFAULT_ROUTER.

+

+**/

+VOID

+Ip6CleanDefaultRouterList (

+  IN IP6_SERVICE            *IpSb

+  );

+

+/**

+  Search a default router node from an IP6 default router list.

+

+  @param[in]  IpSb          The pointer to the IP6_SERVICE instance.

+  @param[in]  Ip6Address    The IPv6 address of the to be searched default router node.

+

+  @return NULL if it failed to find the matching default router node.

+          Otherwise, point to the found default router node.

+

+**/

+IP6_DEFAULT_ROUTER *

+Ip6FindDefaultRouter (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Ip6Address

+  );

+

+/**

+  The function to be called after DAD (Duplicate Address Detection) is performed.

+

+  @param[in]  IsDadPassed   If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.

+  @param[in]  IpIf          Points to the IP6_INTERFACE.

+  @param[in]  DadEntry      The DAD entry which already performed DAD.

+

+**/

+VOID

+Ip6OnDADFinished (

+  IN BOOLEAN        IsDadPassed,

+  IN IP6_INTERFACE  *IpIf,

+  IN IP6_DAD_ENTRY  *DadEntry

+  );

+

+/**

+  Create a DAD (Duplicate Address Detection) entry and queue it to be performed.

+

+  @param[in]  IpIf          Points to the IP6_INTERFACE.

+  @param[in]  AddressInfo   The address information which needs DAD performed.

+  @param[in]  Callback      The callback routine that will be called after DAD

+                            is performed. This is an optional parameter that

+                            may be NULL.

+  @param[in]  Context       The opaque parameter for a DAD callback routine.

+                            This is an optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS           The DAD entry was created and queued.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory to complete the

+                                operation.

+

+

+**/

+EFI_STATUS

+Ip6InitDADProcess (

+  IN IP6_INTERFACE          *IpIf,

+  IN IP6_ADDRESS_INFO       *AddressInfo,

+  IN IP6_DAD_CALLBACK       Callback  OPTIONAL,

+  IN VOID                   *Context  OPTIONAL

+  );

+

+/**

+  Search IP6_DAD_ENTRY from the Duplicate Address Detection List.

+

+  @param[in]  IpSb          The pointer to the IP6_SERVICE instance.

+  @param[in]  Target        The address information which needs DAD performed .

+  @param[out] Interface     If not NULL, output the IP6 interface that configures

+                            the tentative address.

+

+  @return NULL if failed to find the matching DAD entry.

+          Otherwise, point to the found DAD entry.

+

+**/

+IP6_DAD_ENTRY *

+Ip6FindDADEntry (

+  IN  IP6_SERVICE      *IpSb,

+  IN  EFI_IPv6_ADDRESS *Target,

+  OUT IP6_INTERFACE    **Interface OPTIONAL

+  );

+

+/**

+  Allocate and initialize a IP6 prefix list entry.

+

+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.

+  @param[in]  OnLinkOrAuto      If TRUE, the entry is created for the on link prefix list.

+                                Otherwise, it is created for the autoconfiguration prefix list.

+  @param[in]  ValidLifetime     The length of time in seconds that the prefix

+                                is valid for the purpose of on-link determination.

+  @param[in]  PreferredLifetime The length of time in seconds that addresses

+                                generated from the prefix via stateless address

+                                autoconfiguration remain preferred.

+  @param[in]  PrefixLength      The prefix length of the Prefix.

+  @param[in]  Prefix            The prefix address.

+

+  @return NULL if it failed to allocate memory for the prefix node. Otherwise, point

+          to the created or existing prefix list entry.

+

+**/

+IP6_PREFIX_LIST_ENTRY *

+Ip6CreatePrefixListEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN BOOLEAN                OnLinkOrAuto,

+  IN UINT32                 ValidLifetime,

+  IN UINT32                 PreferredLifetime,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *Prefix

+  );

+

+/**

+  Destory a IP6 prefix list entry.

+

+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.

+  @param[in]  PrefixEntry       The to be destroyed prefix list entry.

+  @param[in]  OnLinkOrAuto      If TRUE, the entry is removed from on link prefix list.

+                                Otherwise remove from autoconfiguration prefix list.

+  @param[in]  ImmediateDelete   If TRUE, remove the entry directly.

+                                Otherwise, check the reference count to see whether

+                                it should be removed.

+

+**/

+VOID

+Ip6DestroyPrefixListEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_PREFIX_LIST_ENTRY  *PrefixEntry,

+  IN BOOLEAN                OnLinkOrAuto,

+  IN BOOLEAN                ImmediateDelete

+  );

+

+/**

+  Search the list array to find an IP6 prefix list entry.

+

+  @param[in]  IpSb              The pointer to IP6_SERVICE instance.

+  @param[in]  OnLinkOrAuto      If TRUE, the search the link prefix list,

+                                Otherwise search the autoconfiguration prefix list.

+  @param[in]  PrefixLength      The prefix length of the Prefix

+  @param[in]  Prefix            The prefix address.

+

+  @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the

+          pointer to the IP6 prefix list entry.

+

+**/

+IP6_PREFIX_LIST_ENTRY *

+Ip6FindPrefixListEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN BOOLEAN                OnLinkOrAuto,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *Prefix

+  );

+

+/**

+  Release the resource in prefix list table, and destroy the list entry and

+  corresponding addresses or route entries.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  ListHead          The list entry head of the prefix list table.

+

+**/

+VOID

+Ip6CleanPrefixListTable (

+  IN IP6_SERVICE            *IpSb,

+  IN LIST_ENTRY             *ListHead

+  );

+

+/**

+  Allocate and initialize an IP6 neighbor cache entry.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  CallBack          The callback function to be called when

+                                address resolution is finished.

+  @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.

+  @param[in]  LinkAddress       Points to the MAC address of the neighbor.

+                                Ignored if NULL.

+

+  @return NULL if failed to allocate memory for the neighbor cache entry.

+          Otherwise, point to the created neighbor cache entry.

+

+**/

+IP6_NEIGHBOR_ENTRY *

+Ip6CreateNeighborEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_ARP_CALLBACK       CallBack,

+  IN EFI_IPv6_ADDRESS       *Ip6Address,

+  IN EFI_MAC_ADDRESS        *LinkAddress OPTIONAL

+  );

+

+/**

+  Search a IP6 neighbor cache entry.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.

+

+  @return NULL if it failed to find the matching neighbor cache entry.

+          Otherwise, point to the found neighbor cache entry.

+

+**/

+IP6_NEIGHBOR_ENTRY *

+Ip6FindNeighborEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Ip6Address

+  );

+

+/**

+  Free a IP6 neighbor cache entry and remove all the frames on the address

+  resolution queue that pass the FrameToCancel. That is, either FrameToCancel

+  is NULL, or it returns true for the frame.

+

+  @param[in]  IpSb              The pointer to the IP6_SERVICE instance.

+  @param[in]  NeighborCache     The to be free neighbor cache entry.

+  @param[in]  SendIcmpError     If TRUE, send out ICMP error.

+  @param[in]  FullFree          If TRUE, remove the neighbor cache entry.

+                                Otherwise remove the pending frames.

+  @param[in]  IoStatus          The status returned to the cancelled frames'

+                                callback function.

+  @param[in]  FrameToCancel     Function to select which frame to cancel.

+                                This is an optional parameter that may be NULL.

+  @param[in]  Context           Opaque parameter to the FrameToCancel.

+                                Ignored if FrameToCancel is NULL.

+

+  @retval EFI_INVALID_PARAMETER The input parameter is invalid.

+  @retval EFI_SUCCESS           The operation finished successfully.

+

+**/

+EFI_STATUS

+Ip6FreeNeighborEntry (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_NEIGHBOR_ENTRY     *NeighborCache,

+  IN BOOLEAN                SendIcmpError,

+  IN BOOLEAN                FullFree,

+  IN EFI_STATUS             IoStatus,

+  IN IP6_FRAME_TO_CANCEL    FrameToCancel OPTIONAL,

+  IN VOID                   *Context      OPTIONAL

+  );

+

+/**

+  Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().

+

+  @param[in]  IpSb               The IP6 service binding instance.

+  @param[in]  TargetIp6Address   Pointer to Target IPv6 address.

+  @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.

+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor

+                                 cache. It will be deleted after Timeout. A value of zero means that

+                                 the entry is permanent. A non-zero value means that the entry is

+                                 dynamic.

+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will

+                                 be overridden and updated; if FALSE, and if a

+                                 corresponding cache entry already existed, EFI_ACCESS_DENIED

+                                 will be returned.

+

+  @retval  EFI_SUCCESS           The neighbor cache entry has been added.

+  @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache

+                                 due to insufficient resources.

+  @retval  EFI_NOT_FOUND         TargetLinkAddress is NULL.

+  @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,

+                                 and that entry is tagged as un-overridden (when DeleteFlag

+                                 is FALSE).

+

+**/

+EFI_STATUS

+Ip6AddNeighbor (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,

+  IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,

+  IN UINT32                 Timeout,

+  IN BOOLEAN                Override

+  );

+

+/**

+  Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().

+

+  @param[in]  IpSb               The IP6 service binding instance.

+  @param[in]  TargetIp6Address   Pointer to Target IPv6 address.

+  @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.

+  @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor

+                                 cache. It will be deleted after Timeout. A value of zero means that

+                                 the entry is permanent. A non-zero value means that the entry is

+                                 dynamic.

+  @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will

+                                 be overridden and updated; if FALSE, and if a

+                                 corresponding cache entry already existed, EFI_ACCESS_DENIED

+                                 will be returned.

+

+  @retval  EFI_SUCCESS           The neighbor cache entry has been updated or deleted.

+  @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache.

+

+**/

+EFI_STATUS

+Ip6DelNeighbor (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,

+  IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,

+  IN UINT32                 Timeout,

+  IN BOOLEAN                Override

+  );

+

+/**

+  Process the Neighbor Solicitation message. The message may be sent for Duplicate

+  Address Detection or Address Resolution.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the message.

+  @param[in]  Packet             The content of the message with IP head removed.

+

+  @retval EFI_SUCCESS            The packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.

+  @retval Others                 Failed to process the packet.

+

+**/

+EFI_STATUS

+Ip6ProcessNeighborSolicit (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  );

+

+/**

+  Process the Neighbor Advertisement message.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the message.

+  @param[in]  Packet             The content of the message with IP head removed.

+

+  @retval EFI_SUCCESS            The packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.

+  @retval Others                 Failed to process the packet.

+

+**/

+EFI_STATUS

+Ip6ProcessNeighborAdvertise (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  );

+

+/**

+  Process the Router Advertisement message according to RFC4861.

+

+  @param[in]  IpSb               The IP service that received the packet.

+  @param[in]  Head               The IP head of the message.

+  @param[in]  Packet             The content of the message with the IP head removed.

+

+  @retval EFI_SUCCESS            The packet processed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is invalid.

+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the operation.

+  @retval Others                 Failed to process the packet.

+

+**/

+EFI_STATUS

+Ip6ProcessRouterAdvertise (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  );

+

+/**

+  Process the ICMPv6 redirect message. Find the instance, then update

+  its route cache.

+

+  @param[in]  IpSb               The IP6 service binding instance that received

+                                 the packet.

+  @param[in]  Head               The IP head of the received ICMPv6 packet.

+  @param[in]  Packet             The content of the ICMPv6 redirect packet with

+                                 the IP head removed.

+

+  @retval EFI_INVALID_PARAMETER  The parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES   Insuffcient resources to complete the

+                                 operation.

+  @retval EFI_SUCCESS            Successfully updated the route caches.

+

+**/

+EFI_STATUS

+Ip6ProcessRedirect (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IP6_HEADER         *Head,

+  IN NET_BUF                *Packet

+  );

+

+/**

+  Generate router solicit message and send it out to Destination Address or

+  All Router Link Local scope multicast address.

+

+  @param[in]  IpSb               The IP service to send the packet.

+  @param[in]  Interface          If not NULL, points to the IP6 interface to send

+                                 the packet.

+  @param[in]  SourceAddress      If not NULL, the source address of the message.

+  @param[in]  DestinationAddress If not NULL, the destination address of the message.

+  @param[in]  SourceLinkAddress  If not NULL, the MAC address of the source.

+                                 A source link-layer address option will be appended

+                                 to the message.

+

+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the operation.

+  @retval EFI_SUCCESS            The router solicit message was successfully sent.

+

+**/

+EFI_STATUS

+Ip6SendRouterSolicit (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_INTERFACE          *Interface          OPTIONAL,

+  IN EFI_IPv6_ADDRESS       *SourceAddress      OPTIONAL,

+  IN EFI_IPv6_ADDRESS       *DestinationAddress OPTIONAL,

+  IN EFI_MAC_ADDRESS        *SourceLinkAddress  OPTIONAL

+  );

+

+/**

+  Generate the Neighbor Solicitation message and send it to the Destination Address.

+

+  @param[in]  IpSb               The IP service to send the packet

+  @param[in]  SourceAddress      The source address of the message.

+  @param[in]  DestinationAddress The destination address of the message.

+  @param[in]  TargetIp6Address   The IP address of the target of the solicitation.

+                                 It must not be a multicast address.

+  @param[in]  SourceLinkAddress  The MAC address for the sender. If not NULL,

+                                 a source link-layer address option will be appended

+                                 to the message.

+

+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the

+                                 operation.

+  @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.

+

+**/

+EFI_STATUS

+Ip6SendNeighborSolicit (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *SourceAddress,

+  IN EFI_IPv6_ADDRESS       *DestinationAddress,

+  IN EFI_IPv6_ADDRESS       *TargetIp6Address,

+  IN EFI_MAC_ADDRESS        *SourceLinkAddress OPTIONAL

+  );

+

+/**

+  Set the interface's address. This will trigger the DAD process for the

+  address to set. To set an already set address, the lifetimes wil be

+  updated to the new value passed in.

+

+  @param[in]  Interface             The interface to set the address.

+  @param[in]  Ip6Addr               The interface's to be assigned IPv6 address.

+  @param[in]  IsAnycast             If TRUE, the unicast IPv6 address is anycast.

+                                    Otherwise, it is not anycast.

+  @param[in]  PrefixLength          The prefix length of the Ip6Addr.

+  @param[in]  ValidLifetime         The valid lifetime for this address.

+  @param[in]  PreferredLifetime     The preferred lifetime for this address.

+  @param[in]  DadCallback           The caller's callback to trigger when DAD finishes.

+                                    This is an optional parameter that may be NULL.

+  @param[in]  Context               The context that will be passed to DadCallback.

+                                    This is an optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS               The interface is scheduled to be configured with

+                                    the specified address.

+  @retval EFI_OUT_OF_RESOURCES      Failed to set the interface's address due to

+                                    lack of resources.

+

+**/

+EFI_STATUS

+Ip6SetAddress (

+  IN IP6_INTERFACE          *Interface,

+  IN EFI_IPv6_ADDRESS       *Ip6Addr,

+  IN BOOLEAN                IsAnycast,

+  IN UINT8                  PrefixLength,

+  IN UINT32                 ValidLifetime,

+  IN UINT32                 PreferredLifetime,

+  IN IP6_DAD_CALLBACK       DadCallback  OPTIONAL,

+  IN VOID                   *Context     OPTIONAL

+  );

+

+/**

+  The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.

+  This time routine handles DAD module and neighbor state transition.

+  It is also responsible for sending out router solicitations.

+

+  @param[in]  Event                 The IP6 service instance's heartbeat timer.

+  @param[in]  Context               The IP6 service instance.

+

+**/

+VOID

+EFIAPI

+Ip6NdFasterTimerTicking (

+  IN EFI_EVENT              Event,

+  IN VOID                   *Context

+  );

+

+/**

+  The heartbeat timer of ND module in 1 second. This time routine handles following

+  things: 1) maitain default router list; 2) maintain prefix options;

+  3) maintain route caches.

+

+  @param[in]  IpSb              The IP6 service binding instance.

+

+**/

+VOID

+Ip6NdTimerTicking (

+  IN IP6_SERVICE            *IpSb

+  );

+

+/**

+  Callback function when address resolution is finished. It will cancel

+  all the queued frames if the address resolution failed, or transmit them

+  if the request succeeded.

+

+  @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.

+

+**/

+VOID

+Ip6OnArpResolved (

+  IN VOID                   *Context

+  );

+

+/**

+  Update the ReachableTime in IP6 service binding instance data, in milliseconds.

+

+  @param[in, out] IpSb     Points to the IP6_SERVICE.

+

+**/

+VOID

+Ip6UpdateReachableTime (

+  IN OUT IP6_SERVICE  *IpSb

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6NvData.h b/NetworkPkg/Ip6Dxe/Ip6NvData.h
new file mode 100644
index 0000000..715473f
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6NvData.h
@@ -0,0 +1,70 @@
+/** @file

+  NVData structure used by the IP6 configuration component.

+

+  Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _IP6_NV_DATA_H_

+#define _IP6_NV_DATA_H_

+

+#define IP6_CONFIG_NVDATA_GUID \

+  { \

+    0x2eea107, 0x98db, 0x400e, { 0x98, 0x30, 0x46, 0xa, 0x15, 0x42, 0xd7, 0x99 } \

+  }

+

+#define FORMID_MAIN_FORM          1

+#define FORMID_MANUAL_CONFIG_FORM 2

+

+#define IP6_POLICY_AUTO           0

+#define IP6_POLICY_MANUAL         1

+#define DAD_MAX_TRANSMIT_COUNT    10

+

+#define KEY_INTERFACE_ID          0x101

+#define KEY_MANUAL_ADDRESS        0x102

+#define KEY_GATEWAY_ADDRESS       0x103

+#define KEY_DNS_ADDRESS           0x104

+#define KEY_SAVE_CHANGES          0x105

+#define KEY_SAVE_CONFIG_CHANGES   0x106

+#define KEY_IGNORE_CONFIG_CHANGES 0x107

+

+#define HOST_ADDRESS_LABEL        0x9000

+#define ROUTE_TABLE_LABEL         0xa000

+#define GATEWAY_ADDRESS_LABEL     0xb000

+#define DNS_ADDRESS_LABEL         0xc000

+#define LABEL_END                 0xffff

+

+#define INTERFACE_ID_STR_MIN_SIZE 1

+#define INTERFACE_ID_STR_MAX_SIZE 23

+#define INTERFACE_ID_STR_STORAGE  24

+#define IP6_STR_MAX_SIZE          40

+#define ADDRESS_STR_MIN_SIZE      2

+#define ADDRESS_STR_MAX_SIZE      255

+

+///

+/// IP6_CONFIG_IFR_NVDATA contains the IP6 configure

+/// parameters for that NIC.

+///

+#pragma pack(1)

+typedef struct {

+  UINT8           IfType;                                 ///< interface type

+  UINT8           Padding[3];

+  UINT32          Policy;                                 ///< manual or automatic

+  UINT32          DadTransmitCount;                       ///< dad transmits count

+  CHAR16          InterfaceId[INTERFACE_ID_STR_STORAGE];  ///< alternative interface id

+  CHAR16          ManualAddress[ADDRESS_STR_MAX_SIZE];    ///< IP addresses

+  CHAR16          GatewayAddress[ADDRESS_STR_MAX_SIZE];   ///< Gateway address

+  CHAR16          DnsAddress[ADDRESS_STR_MAX_SIZE];       ///< DNS server address

+} IP6_CONFIG_IFR_NVDATA;

+#pragma pack()

+

+#endif

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Option.c b/NetworkPkg/Ip6Dxe/Ip6Option.c
new file mode 100644
index 0000000..9a91fd7
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Option.c
@@ -0,0 +1,758 @@
+/** @file

+  IP6 option support functions and routines.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+/**

+  Validate the IP6 option format for both the packets we received

+  and that we will transmit. It will compute the ICMPv6 error message fields

+  if the option is malformated.

+

+  @param[in]  IpSb              The IP6 service data.

+  @param[in]  Packet            The to be validated packet.

+  @param[in]  Option            The first byte of the option.

+  @param[in]  OptionLen         The length of the whole option.

+  @param[in]  Pointer           Identifies the octet offset within

+                                the invoking packet where the error was detected.

+

+

+  @retval TRUE     The option is properly formatted.

+  @retval FALSE    The option is malformated.

+

+**/

+BOOLEAN

+Ip6IsOptionValid (

+  IN IP6_SERVICE            *IpSb,

+  IN NET_BUF                *Packet,

+  IN UINT8                  *Option,

+  IN UINT8                  OptionLen,

+  IN UINT32                 Pointer

+  )

+{

+  UINT8                      Offset;

+  UINT8                      OptionType;

+

+  Offset = 0;

+

+  while (Offset < OptionLen) {

+    OptionType = *(Option + Offset);

+

+    switch (OptionType) {

+    case Ip6OptionPad1:

+      //

+      // It is a Pad1 option

+      //

+      Offset++;

+      break;

+    case Ip6OptionPadN:

+      //

+      // It is a PadN option

+      //

+      Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2);

+      break;

+    case Ip6OptionRouterAlert:

+      //

+      // It is a Router Alert Option

+      //

+      Offset += 4;

+      break;

+    default:

+      //

+      // The highest-order two bits specify the action must be taken if

+      // the processing IPv6 node does not recognize the option type.

+      //

+      switch (OptionType & Ip6OptionMask) {

+      case Ip6OptionSkip:

+        Offset = (UINT8) (Offset + *(Option + Offset + 1));

+        break;

+      case Ip6OptionDiscard:

+        return FALSE;

+      case Ip6OptionParameterProblem:

+        Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);

+        Ip6SendIcmpError (

+          IpSb,

+          Packet,

+          NULL,

+          &Packet->Ip.Ip6->SourceAddress,

+          ICMP_V6_PARAMETER_PROBLEM,

+          2,

+          &Pointer

+          );

+        return FALSE;

+      case Ip6OptionMask:

+        if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {

+          Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);

+          Ip6SendIcmpError (

+            IpSb,

+            Packet,

+            NULL,

+            &Packet->Ip.Ip6->SourceAddress,

+            ICMP_V6_PARAMETER_PROBLEM,

+            2,

+            &Pointer

+            );

+        }

+

+        return FALSE;

+        break;

+      }

+

+      break;

+    }

+

+  }

+

+  return TRUE;

+}

+

+/**

+  Validate the IP6 option format for both the packets we received

+  and that we will transmit. It supports the defined options in Neighbor

+  Discovery messages.

+

+  @param[in]  Option            The first byte of the option.

+  @param[in]  OptionLen         The length of the whole option.

+

+  @retval TRUE     The option is properly formatted.

+  @retval FALSE    The option is malformated.

+

+**/

+BOOLEAN

+Ip6IsNDOptionValid (

+  IN UINT8                  *Option,

+  IN UINT16                 OptionLen

+  )

+{

+  UINT16                    Offset;

+  UINT8                     OptionType;

+  UINT16                    Length;

+

+  Offset = 0;

+

+  while (Offset < OptionLen) {

+    OptionType = *(Option + Offset);

+     Length    = (UINT16) (*(Option + Offset + 1) * 8);

+

+    switch (OptionType) {

+    case Ip6OptionPrefixInfo:

+      if (Length != 32) {

+        return FALSE;

+      }

+

+      break;

+

+    case Ip6OptionMtu:

+      if (Length != 8) {

+        return FALSE;

+      }

+

+      break;

+

+    default:

+      //

+      // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and

+      // Ip6OptionRedirected here. For unrecognized options, silently ignore

+      // and continue processsing the message.

+      //

+      if (Length == 0) {

+        return FALSE;

+      }

+

+      break;

+    }

+

+    Offset = (UINT16) (Offset + Length);

+  }

+

+  return TRUE;

+}

+

+

+/**

+  Validate whether the NextHeader is a known valid protocol or one of the user configured

+  protocols from the upper layer.

+

+  @param[in]  IpSb          The IP6 service instance.

+  @param[in]  NextHeader    The next header field.

+

+  @retval TRUE              The NextHeader is a known valid protocol or user configured.

+  @retval FALSE             The NextHeader is not a known valid protocol.

+

+**/

+BOOLEAN

+Ip6IsValidProtocol (

+  IN IP6_SERVICE            *IpSb,

+  IN UINT8                  NextHeader

+  )

+{

+  LIST_ENTRY                *Entry;

+  IP6_PROTOCOL              *IpInstance;

+

+  if (NextHeader == EFI_IP_PROTO_TCP ||

+      NextHeader == EFI_IP_PROTO_UDP ||

+      NextHeader == IP6_ICMP ||

+      NextHeader == IP6_ESP

+      ) {

+    return TRUE;

+  }

+

+  if (IpSb == NULL) {

+    return FALSE;

+  }

+

+  if (IpSb->Signature != IP6_SERVICE_SIGNATURE) {

+    return FALSE;

+  }

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->Children) {

+    IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);

+    if (IpInstance->State == IP6_STATE_CONFIGED) {

+      if (IpInstance->ConfigData.DefaultProtocol == NextHeader) {

+        return TRUE;

+      }

+    }

+  }

+

+  return FALSE;

+}

+

+/**

+  Validate the IP6 extension header format for both the packets we received

+  and that we will transmit. It will compute the ICMPv6 error message fields

+  if the option is mal-formated.

+

+  @param[in]  IpSb          The IP6 service instance. This is an optional parameter.

+  @param[in]  Packet        The data of the packet. Ignored if NULL.

+  @param[in]  NextHeader    The next header field in IPv6 basic header.

+  @param[in]  ExtHdrs       The first byte of the option.

+  @param[in]  ExtHdrsLen    The length of the whole option.

+  @param[in]  Rcvd          The option is from the packet we received if TRUE,

+                            otherwise, the option we want to transmit.

+  @param[out] FormerHeader  The offset of NextHeader which points to Fragment

+                            Header when we received, of the ExtHdrs.

+                            Ignored if we transmit.

+  @param[out] LastHeader    The pointer of NextHeader of the last extension

+                            header processed by IP6.

+  @param[out] RealExtsLen   The length of extension headers processed by IP6 layer.

+                            This is an optional parameter that may be NULL.

+  @param[out] UnFragmentLen The length of unfragmented length of extension headers.

+                            This is an optional parameter that may be NULL.

+  @param[out] Fragmented    Indicate whether the packet is fragmented.

+                            This is an optional parameter that may be NULL.

+

+  @retval     TRUE          The option is properly formated.

+  @retval     FALSE         The option is malformated.

+

+**/

+BOOLEAN

+Ip6IsExtsValid (

+  IN IP6_SERVICE            *IpSb           OPTIONAL,

+  IN NET_BUF                *Packet         OPTIONAL,

+  IN UINT8                  *NextHeader,

+  IN UINT8                  *ExtHdrs,

+  IN UINT32                 ExtHdrsLen,

+  IN BOOLEAN                Rcvd,

+  OUT UINT32                *FormerHeader   OPTIONAL,

+  OUT UINT8                 **LastHeader,

+  OUT UINT32                *RealExtsLen    OPTIONAL,

+  OUT UINT32                *UnFragmentLen  OPTIONAL,

+  OUT BOOLEAN               *Fragmented     OPTIONAL

+  )

+{

+  UINT32                     Pointer;

+  UINT32                     Offset;

+  UINT8                      *Option;

+  UINT8                      OptionLen;

+  BOOLEAN                    Flag;

+  UINT8                      CountD;

+  UINT8                      CountA;

+  IP6_FRAGMENT_HEADER        *FragmentHead;

+  UINT16                     FragmentOffset;

+  IP6_ROUTING_HEADER         *RoutingHead;

+

+  if (RealExtsLen != NULL) {

+    *RealExtsLen = 0;

+  }

+

+  if (UnFragmentLen != NULL) {

+    *UnFragmentLen = 0;

+  }

+

+  if (Fragmented != NULL) {

+    *Fragmented = FALSE;

+  }

+

+  *LastHeader = NextHeader;

+

+  if (ExtHdrs == NULL && ExtHdrsLen == 0) {

+    return TRUE;

+  }

+

+  if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {

+    return FALSE;

+  }

+

+  Pointer = 0;

+  Offset  = 0;

+  Flag    = FALSE;

+  CountD  = 0;

+  CountA  = 0;

+

+  while (Offset <= ExtHdrsLen) {

+

+    switch (*NextHeader) {

+    case IP6_HOP_BY_HOP:

+      if (Offset != 0) {

+        if (!Rcvd) {

+          return FALSE;

+        }

+        //

+        // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only.

+        // If not, generate a ICMP parameter problem message with code value of 1.

+        //

+        if (Pointer == 0) {

+          Pointer = sizeof (EFI_IP6_HEADER);

+        } else {

+          Pointer = Offset + sizeof (EFI_IP6_HEADER);

+        }

+

+        if ((IpSb != NULL) && (Packet != NULL) &&

+            !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {

+          Ip6SendIcmpError (

+            IpSb,

+            Packet,

+            NULL,

+            &Packet->Ip.Ip6->SourceAddress,

+            ICMP_V6_PARAMETER_PROBLEM,

+            1,

+            &Pointer

+            );

+        }

+        return FALSE;

+      }

+

+      Flag = TRUE;

+

+    //

+    // Fall through

+    //

+    case IP6_DESTINATION:

+      if (*NextHeader == IP6_DESTINATION) {

+        CountD++;

+      }

+

+      if (CountD > 2) {

+        return FALSE;

+      }

+

+      NextHeader = ExtHdrs + Offset;

+      Pointer    = Offset;

+

+      Offset++;

+      Option     = ExtHdrs + Offset;

+      OptionLen  = (UINT8) ((*Option + 1) * 8 - 2);

+      Option++;

+      Offset++;

+

+      if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) {

+        return FALSE;

+      }

+

+      Offset = Offset + OptionLen;

+

+      if (Flag) {

+        if (UnFragmentLen != NULL) {

+          *UnFragmentLen = Offset;

+        }

+

+        Flag = FALSE;

+      }

+

+      break;

+

+    case IP6_ROUTING:

+      NextHeader = ExtHdrs + Offset;

+      RoutingHead = (IP6_ROUTING_HEADER *) NextHeader;

+

+      //

+      // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095.

+      // Thus all routing types are processed as unrecognized.

+      //

+      if (RoutingHead->SegmentsLeft == 0) {

+        //

+        // Ignore the routing header and proceed to process the next header.

+        //

+        Offset = Offset + (RoutingHead->HeaderLen + 1) * 8;

+

+        if (UnFragmentLen != NULL) {

+          *UnFragmentLen = Offset;

+        }

+

+      } else {

+        //

+        // Discard the packet and send an ICMP Parameter Problem, Code 0, message

+        // to the packet's source address, pointing to the unrecognized routing

+        // type.

+        //

+        Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER);

+        if ((IpSb != NULL) && (Packet != NULL) &&

+            !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {

+          Ip6SendIcmpError (

+            IpSb,

+            Packet,

+            NULL,

+            &Packet->Ip.Ip6->SourceAddress,

+            ICMP_V6_PARAMETER_PROBLEM,

+            0,

+            &Pointer

+            );

+        }

+

+        return FALSE;

+      }

+

+      break;

+

+    case IP6_FRAGMENT:

+

+      //

+      // RFC2402, AH header should after fragment header.

+      //

+      if (CountA > 1) {

+        return FALSE;

+      }

+

+      //

+      // RFC2460, ICMP Parameter Problem message with code 0 should be sent

+      // if the length of a fragment is not a multiple of 8 octects and the M

+      // flag of that fragment is 1, pointing to the Payload length field of the

+      // fragment packet.

+      //

+      if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) {

+        //

+        // Check whether it is the last fragment.

+        //

+        FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset);

+        if (FragmentHead == NULL) {

+          return FALSE;

+        }

+

+        FragmentOffset = NTOHS (FragmentHead->FragmentOffset);

+

+        if (((FragmentOffset & 0x1) == 0x1) &&

+            !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {

+          Pointer = sizeof (UINT32);

+          Ip6SendIcmpError (

+            IpSb,

+            Packet,

+            NULL,

+            &Packet->Ip.Ip6->SourceAddress,

+            ICMP_V6_PARAMETER_PROBLEM,

+            0,

+            &Pointer

+            );

+          return FALSE;

+        }

+      }

+

+      if (Fragmented != NULL) {

+        *Fragmented = TRUE;

+      }

+

+      if (Rcvd && FormerHeader != NULL) {

+        *FormerHeader = (UINT32) (NextHeader - ExtHdrs);

+      }

+

+      NextHeader = ExtHdrs + Offset;

+      Offset     = Offset + 8;

+      break;

+

+    case IP6_AH:

+      if (++CountA > 1) {

+        return FALSE;

+      }

+

+      Option     = ExtHdrs + Offset;

+      NextHeader = Option;

+      Option++;

+      //

+      // RFC2402, Payload length is specified in 32-bit words, minus "2".

+      //

+      OptionLen  = (UINT8) ((*Option + 2) * 4);

+      Offset     = Offset + OptionLen;

+      break;

+

+    case IP6_NO_NEXT_HEADER:

+      *LastHeader = NextHeader;

+      return FALSE;

+      break;

+

+    default:

+      if (Ip6IsValidProtocol (IpSb, *NextHeader)) {

+

+        *LastHeader = NextHeader;

+

+        if (RealExtsLen != NULL) {

+          *RealExtsLen = Offset;

+        }

+

+        return TRUE;

+      }

+

+      //

+      // The Next Header value is unrecognized by the node, discard the packet and

+      // send an ICMP parameter problem message with code value of 1.

+      //

+      if (Offset == 0) {

+        //

+        // The Next Header directly follows IPv6 basic header.

+        //

+        Pointer = 6;

+      } else {

+        if (Pointer == 0) {

+          Pointer = sizeof (EFI_IP6_HEADER);

+        } else {

+          Pointer = Offset + sizeof (EFI_IP6_HEADER);

+        }

+      }

+

+      if ((IpSb != NULL) && (Packet != NULL) &&

+          !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {

+        Ip6SendIcmpError (

+          IpSb,

+          Packet,

+          NULL,

+          &Packet->Ip.Ip6->SourceAddress,

+          ICMP_V6_PARAMETER_PROBLEM,

+          1,

+          &Pointer

+          );

+      }

+      return FALSE;

+    }

+  }

+

+  *LastHeader = NextHeader;

+

+  if (RealExtsLen != NULL) {

+    *RealExtsLen = Offset;

+  }

+

+  return TRUE;

+}

+

+/**

+  Generate an IPv6 router alert option in network order and output it through Buffer.

+

+  @param[out]     Buffer         Points to a buffer to record the generated option.

+  @param[in, out] BufferLen      The length of Buffer, in bytes.

+  @param[in]      NextHeader     The 8-bit selector indicates the type of header

+                                 immediately following the Hop-by-Hop Options header.

+

+  @retval EFI_BUFFER_TOO_SMALL   The Buffer is too small to contain the generated

+                                 option. BufferLen is updated for the required size.

+

+  @retval EFI_SUCCESS            The option is generated and filled in to Buffer.

+

+**/

+EFI_STATUS

+Ip6FillHopByHop (

+  OUT UINT8                  *Buffer,

+  IN OUT UINTN               *BufferLen,

+  IN UINT8                   NextHeader

+  )

+{

+  UINT8                      BufferArray[8];

+

+  if (*BufferLen < 8) {

+    *BufferLen = 8;

+    return EFI_BUFFER_TOO_SMALL;

+  }

+

+  //

+  // Form the Hop-By-Hop option in network order.

+  // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN

+  // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets.

+  //

+  ZeroMem (BufferArray, sizeof (BufferArray));

+  BufferArray[0] = NextHeader;

+  BufferArray[2] = 0x5;

+  BufferArray[3] = 0x2;

+  BufferArray[6] = 1;

+

+  CopyMem (Buffer, BufferArray, sizeof (BufferArray));

+  return EFI_SUCCESS;

+}

+

+/**

+  Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.

+

+  @param[in]  IpSb             The IP6 service instance to transmit the packet.

+  @param[in]  NextHeader       The extension header type of first extension header.

+  @param[in]  LastHeader       The extension header type of last extension header.

+  @param[in]  ExtHdrs          The length of the original extension header.

+  @param[in]  ExtHdrsLen       The length of the extension headers.

+  @param[in]  FragmentOffset   The fragment offset of the data following the header.

+  @param[out] UpdatedExtHdrs   The updated ExtHdrs with Fragment header inserted.

+                               It's caller's responsiblity to free this buffer.

+

+  @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of

+                               resource.

+  @retval EFI_UNSUPPORTED      The extension header specified in ExtHdrs is not

+                               supported currently.

+  @retval EFI_SUCCESS          The operation performed successfully.

+

+**/

+EFI_STATUS

+Ip6FillFragmentHeader (

+  IN  IP6_SERVICE           *IpSb,

+  IN  UINT8                 NextHeader,

+  IN  UINT8                 LastHeader,

+  IN  UINT8                 *ExtHdrs,

+  IN  UINT32                ExtHdrsLen,

+  IN  UINT16                FragmentOffset,

+  OUT UINT8                 **UpdatedExtHdrs

+  )

+{

+  UINT32                    Length;

+  UINT8                     *Buffer;

+  UINT32                    FormerHeader;

+  UINT32                    Offset;

+  UINT32                    Part1Len;

+  UINT32                    HeaderLen;

+  UINT8                     Current;

+  IP6_FRAGMENT_HEADER       FragmentHead;

+

+  if (UpdatedExtHdrs == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);

+  Buffer = AllocatePool (Length);

+  if (Buffer == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Offset         = 0;

+  Part1Len       = 0;

+  FormerHeader   = 0;

+  Current        = NextHeader;

+

+  while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) {

+    switch (NextHeader) {

+    case IP6_ROUTING:

+    case IP6_HOP_BY_HOP:

+    case IP6_DESTINATION:

+      Current      = NextHeader;

+      NextHeader   = *(ExtHdrs + Offset);

+

+      if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) {

+        //

+        // Destination Options header should occur at most twice, once before

+        // a Routing header and once before the upper-layer header. Here we

+        // find the one before the upper-layer header. Insert the Fragment

+        // Header before it.

+        //

+        CopyMem (Buffer, ExtHdrs, Part1Len);

+        *(Buffer + FormerHeader) = IP6_FRAGMENT;

+        //

+        // Exit the loop.

+        //

+        Offset = ExtHdrsLen + 1;

+        break;

+      }

+

+

+      FormerHeader = Offset;

+      HeaderLen    = (*(ExtHdrs + Offset + 1) + 1) * 8;

+      Part1Len     = Part1Len + HeaderLen;

+      Offset       = Offset + HeaderLen;

+      break;

+

+    case IP6_FRAGMENT:

+      Current    = NextHeader;

+

+      if (Part1Len != 0) {

+        CopyMem (Buffer, ExtHdrs, Part1Len);

+      }

+

+      *(Buffer + FormerHeader) = IP6_FRAGMENT;

+

+      //

+      // Exit the loop.

+      //

+      Offset = ExtHdrsLen + 1;

+      break;

+

+    case IP6_AH:

+      Current    = NextHeader;

+      NextHeader = *(ExtHdrs + Offset);

+      //

+      // RFC2402, Payload length is specified in 32-bit words, minus "2".

+      //

+      HeaderLen  = (*(ExtHdrs + Offset + 1) + 2) * 4;

+      Part1Len   = Part1Len + HeaderLen;

+      Offset     = Offset + HeaderLen;

+      break;

+

+    default:

+      if (Ip6IsValidProtocol (IpSb, NextHeader)) {

+        Current = NextHeader;

+        CopyMem (Buffer, ExtHdrs, Part1Len);

+        *(Buffer + FormerHeader) = IP6_FRAGMENT;

+        //

+        // Exit the loop.

+        //

+        Offset = ExtHdrsLen + 1;

+        break;

+      }

+

+      FreePool (Buffer);

+      return EFI_UNSUPPORTED;

+    }

+  }

+

+  //

+  // Append the Fragment header. If the fragment offset indicates the fragment

+  // is the first fragment.

+  //

+  if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {

+    FragmentHead.NextHeader = Current;

+  } else {

+    FragmentHead.NextHeader = LastHeader;

+  }

+

+  FragmentHead.Reserved       = 0;

+  FragmentHead.FragmentOffset = HTONS (FragmentOffset);

+  FragmentHead.Identification = mIp6Id;

+

+  CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER));

+

+  if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) {

+    //

+    // Append the part2 (fragmentable part) of Extension headers

+    //

+    CopyMem (

+      Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER),

+      ExtHdrs + Part1Len,

+      ExtHdrsLen - Part1Len

+      );

+  }

+

+  *UpdatedExtHdrs = Buffer;

+

+  return EFI_SUCCESS;

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Option.h b/NetworkPkg/Ip6Dxe/Ip6Option.h
new file mode 100644
index 0000000..b62a042
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Option.h
@@ -0,0 +1,191 @@
+/** @file

+  Definition of IP6 option process routines.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_OPTION_H__

+#define __EFI_IP6_OPTION_H__

+

+#define IP6_FRAGMENT_OFFSET_MASK (~0x3)

+

+typedef struct _IP6_FRAGMENT_HEADER {

+  UINT8                     NextHeader;

+  UINT8                     Reserved;

+  UINT16                    FragmentOffset;

+  UINT32                    Identification;

+} IP6_FRAGMENT_HEADER;

+

+typedef struct _IP6_ROUTING_HEADER {

+  UINT8                     NextHeader;

+  UINT8                     HeaderLen;

+  UINT8                     RoutingType;

+  UINT8                     SegmentsLeft;

+} IP6_ROUTING_HEADER;

+

+typedef enum {

+  Ip6OptionPad1             = 0,

+  Ip6OptionPadN             = 1,

+  Ip6OptionRouterAlert      = 5,

+  Ip6OptionSkip             = 0,

+  Ip6OptionDiscard          = 0x40,

+  Ip6OptionParameterProblem = 0x80,

+  Ip6OptionMask             = 0xc0,

+

+  Ip6OptionEtherSource      = 1,

+  Ip6OptionEtherTarget      = 2,

+  Ip6OptionPrefixInfo       = 3,

+  Ip6OptionRedirected       = 4,

+  Ip6OptionMtu              = 5

+} IP6_OPTION_TYPE;

+

+/**

+  Validate the IP6 extension header format for both the packets we received

+  and that we will transmit. It will compute the ICMPv6 error message fields

+  if the option is mal-formated.

+

+  @param[in]  IpSb          The IP6 service instance. This is an optional parameter.

+  @param[in]  Packet        The data of the packet. Ignored if NULL.

+  @param[in]  NextHeader    The next header field in IPv6 basic header.

+  @param[in]  ExtHdrs       The first byte of the option.

+  @param[in]  ExtHdrsLen    The length of the whole option.

+  @param[in]  Rcvd          The option is from the packet we received if TRUE,

+                            otherwise, the option we want to transmit.

+  @param[out] FormerHeader  The offset of NextHeader which points to Fragment

+                            Header when we received, of the ExtHdrs.

+                            Ignored if we transmit.

+  @param[out] LastHeader    The pointer of NextHeader of the last extension

+                            header processed by IP6.

+  @param[out] RealExtsLen   The length of extension headers processed by IP6 layer.

+                            This is an optional parameter that may be NULL.

+  @param[out] UnFragmentLen The length of unfragmented length of extension headers.

+                            This is an optional parameter that may be NULL.

+  @param[out] Fragmented    Indicate whether the packet is fragmented.

+                            This is an optional parameter that may be NULL.

+

+  @retval     TRUE          The option is properly formated.

+  @retval     FALSE         The option is malformated.

+

+**/

+BOOLEAN

+Ip6IsExtsValid (

+  IN IP6_SERVICE            *IpSb           OPTIONAL,

+  IN NET_BUF                *Packet         OPTIONAL,

+  IN UINT8                  *NextHeader,

+  IN UINT8                  *ExtHdrs,

+  IN UINT32                 ExtHdrsLen,

+  IN BOOLEAN                Rcvd,

+  OUT UINT32                *FormerHeader   OPTIONAL,

+  OUT UINT8                 **LastHeader,

+  OUT UINT32                *RealExtsLen    OPTIONAL,

+  OUT UINT32                *UnFragmentLen  OPTIONAL,

+  OUT BOOLEAN               *Fragmented     OPTIONAL

+  );

+

+/**

+  Generate an IPv6 router alert option in network order and output it through Buffer.

+

+  @param[out]     Buffer         Points to a buffer to record the generated option.

+  @param[in, out] BufferLen      The length of Buffer, in bytes.

+  @param[in]      NextHeader     The 8-bit selector indicates the type of header

+                                 immediately following the Hop-by-Hop Options header.

+

+  @retval EFI_BUFFER_TOO_SMALL   The Buffer is too small to contain the generated

+                                 option. BufferLen is updated for the required size.

+

+  @retval EFI_SUCCESS            The option is generated and filled in to Buffer.

+

+**/

+EFI_STATUS

+Ip6FillHopByHop (

+  OUT UINT8                  *Buffer,

+  IN OUT UINTN               *BufferLen,

+  IN UINT8                   NextHeader

+  );

+

+/**

+  Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.

+

+  @param[in]  IpSb             The IP6 service instance to transmit the packet.

+  @param[in]  NextHeader       The extension header type of first extension header.

+  @param[in]  LastHeader       The extension header type of last extension header.

+  @param[in]  ExtHdrs          The length of the original extension header.

+  @param[in]  ExtHdrsLen       The length of the extension headers.

+  @param[in]  FragmentOffset   The fragment offset of the data following the header.

+  @param[out] UpdatedExtHdrs   The updated ExtHdrs with Fragment header inserted.

+                               It's caller's responsiblity to free this buffer.

+

+  @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of

+                               resource.

+  @retval EFI_UNSUPPORTED      The extension header specified in ExtHdrs is not

+                               supported currently.

+  @retval EFI_SUCCESS          The operation performed successfully.

+

+**/

+EFI_STATUS

+Ip6FillFragmentHeader (

+  IN  IP6_SERVICE           *IpSb,

+  IN  UINT8                 NextHeader,

+  IN  UINT8                 LastHeader,

+  IN  UINT8                 *ExtHdrs,

+  IN  UINT32                ExtHdrsLen,

+  IN  UINT16                FragmentOffset,

+  OUT UINT8                 **UpdatedExtHdrs

+  );

+

+/**

+  Copy the extension headers from the original to buffer. A Fragment header is

+  appended to the end.

+

+  @param[in]       NextHeader       The 8-bit selector indicates the type of

+                                    the fragment header's next header.

+  @param[in]       ExtHdrs          The length of the original extension header.

+  @param[in]       LastHeader       The pointer of next header of last extension header.

+  @param[in]       FragmentOffset   The fragment offset of the data following the header.

+  @param[in]       UnFragmentHdrLen The length of unfragmented length of extension headers.

+  @param[in, out]  Buf              The buffer to copy options to.

+  @param[in, out]  BufLen           The length of the buffer.

+

+  @retval EFI_SUCCESS           The options are copied over.

+  @retval EFI_BUFFER_TOO_SMALL  The buffer caller provided is too small.

+

+**/

+EFI_STATUS

+Ip6CopyExts (

+  IN UINT8                  NextHeader,

+  IN UINT8                  *ExtHdrs,

+  IN UINT8                  *LastHeader,

+  IN UINT16                 FragmentOffset,

+  IN UINT32                 UnFragmentHdrLen,

+  IN OUT UINT8              *Buf,

+  IN OUT UINT32             *BufLen

+  );

+

+/**

+  Validate the IP6 option format for both the packets we received

+  and that we will transmit. It supports the defined options in Neighbor

+  Discovery messages.

+

+  @param[in]  Option            The first byte of the option.

+  @param[in]  OptionLen         The length of the whole option.

+

+  @retval TRUE     The option is properly formatted.

+  @retval FALSE    The option is malformated.

+

+**/

+BOOLEAN

+Ip6IsNDOptionValid (

+  IN UINT8                  *Option,

+  IN UINT16                 OptionLen

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6Output.c b/NetworkPkg/Ip6Dxe/Ip6Output.c
new file mode 100644
index 0000000..baa4904
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Output.c
@@ -0,0 +1,1077 @@
+/** @file

+  The internal functions and routines to transmit the IP6 packet.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+UINT32 mIp6Id;

+

+/**

+  Output all the available source addresses to a list entry head SourceList. The

+  number of source addresses are also returned.

+

+  @param[in]       IpSb             Points to an IP6 service binding instance.

+  @param[out]      SourceList       The list entry head of all source addresses.

+                                    It is the caller's responsiblity to free the

+                                    resources.

+  @param[out]      SourceCount      The number of source addresses.

+

+  @retval EFI_SUCCESS           The source addresses were copied to a list entry head

+                                SourceList.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to complete the operation.

+

+**/

+EFI_STATUS

+Ip6CandidateSource (

+  IN IP6_SERVICE            *IpSb,

+  OUT LIST_ENTRY            *SourceList,

+  OUT UINT32                *SourceCount

+  )

+{

+  IP6_INTERFACE             *IpIf;

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Entry2;

+  IP6_ADDRESS_INFO          *AddrInfo;

+  IP6_ADDRESS_INFO          *Copy;

+

+  *SourceCount = 0;

+

+  if (IpSb->LinkLocalOk) {

+    Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO));

+    if (Copy == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    Copy->Signature         = IP6_ADDR_INFO_SIGNATURE;

+    IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr);

+    Copy->IsAnycast         = FALSE;

+    Copy->PrefixLength      = IP6_LINK_LOCAL_PREFIX_LENGTH;

+    Copy->ValidLifetime     = (UINT32) IP6_INFINIT_LIFETIME;

+    Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME;

+

+    InsertTailList (SourceList, &Copy->Link);

+    (*SourceCount)++;

+  }

+

+  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {

+    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);

+

+    NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {

+      AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);

+

+      if (AddrInfo->IsAnycast) {

+        //

+        // Never use an anycast address.

+        //

+        continue;

+      }

+

+      Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo);

+      if (Copy == NULL) {

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      InsertTailList (SourceList, &Copy->Link);

+      (*SourceCount)++;

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Caculate how many bits are the same between two IPv6 addresses.

+

+  @param[in]       AddressA         Points to an IPv6 address.

+  @param[in]       AddressB         Points to another IPv6 address.

+

+  @return The common bits of the AddressA and AddressB.

+

+**/

+UINT8

+Ip6CommonPrefixLen (

+  IN EFI_IPv6_ADDRESS       *AddressA,

+  IN EFI_IPv6_ADDRESS       *AddressB

+  )

+{

+  UINT8                     Count;

+  UINT8                     Index;

+  UINT8                     ByteA;

+  UINT8                     ByteB;

+  UINT8                     NumBits;

+

+  Count = 0;

+  Index = 0;

+

+  while (Index < 16) {

+    ByteA = AddressA->Addr[Index];

+    ByteB = AddressB->Addr[Index];

+

+    if (ByteA == ByteB) {

+      Count += 8;

+      Index++;

+      continue;

+    }

+

+    //

+    // Check how many bits are common between the two bytes.

+    //

+    NumBits = 8;

+    ByteA   = (UINT8) (ByteA ^ ByteB);

+

+    while (ByteA != 0) {

+      NumBits--;

+      ByteA = (UINT8) (ByteA >> 1);

+    }

+

+    return (UINT8) (Count + NumBits);

+  }

+

+  return Count;

+}

+

+/**

+  Output all the available source addresses to a list entry head SourceList. The

+  number of source addresses are also returned.

+

+  @param[in]       IpSb             Points to a IP6 service binding instance.

+  @param[in]       Destination      The IPv6 destination address.

+  @param[out]      Source           The selected IPv6 source address according to

+                                    the Destination.

+

+  @retval EFI_SUCCESS           The source addresses were copied to a list entry

+                                head SourceList.

+  @retval EFI_NO_MAPPING        The IPv6 stack is not auto configured.

+

+**/

+EFI_STATUS

+Ip6SelectSourceAddress (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Destination,

+  OUT EFI_IPv6_ADDRESS      *Source

+  )

+{

+  EFI_STATUS                Status;

+  LIST_ENTRY                SourceList;

+  UINT32                    SourceCount;

+  UINT8                     ScopeD;

+  LIST_ENTRY                *Entry;

+  IP6_ADDRESS_INFO          *AddrInfo;

+  IP6_PREFIX_LIST_ENTRY     *Prefix;

+  UINT8                     LastCommonLength;

+  UINT8                     CurrentCommonLength;

+  EFI_IPv6_ADDRESS          *TmpAddress;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  Status = EFI_SUCCESS;

+  InitializeListHead (&SourceList);

+

+  if (!IpSb->LinkLocalOk) {

+    return EFI_NO_MAPPING;

+  }

+

+  //

+  // Rule 1: Prefer same address.

+  //

+  if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) {

+    IP6_COPY_ADDRESS (Source, Destination);

+    goto Exit;

+  }

+

+  //

+  // Rule 2: Prefer appropriate scope.

+  //

+  if (IP6_IS_MULTICAST (Destination)) {

+    ScopeD = (UINT8) (Destination->Addr[1] >> 4);

+  } else if (NetIp6IsLinkLocalAddr (Destination)) {

+    ScopeD = 0x2;

+  } else {

+    ScopeD = 0xE;

+  }

+

+  if (ScopeD <= 0x2) {

+    //

+    // Return the link-local address if it exists

+    // One IP6_SERVICE only has one link-local address.

+    //

+    IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);

+    goto Exit;

+  }

+

+  //

+  // All candidate source addresses are global unicast address.

+  //

+  Ip6CandidateSource (IpSb, &SourceList, &SourceCount);

+

+  if (SourceCount == 0) {

+    Status = EFI_NO_MAPPING;

+    goto Exit;

+  }

+

+  IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);

+

+  if (SourceCount == 1) {

+    goto Exit;

+  }

+

+  //

+  // Rule 3: Avoid deprecated addresses.

+  // TODO: check the "deprecated" state of the stateful configured address

+  //

+  NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {

+    Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);

+    if (Prefix->PreferredLifetime == 0) {

+      Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength);

+

+      if (SourceCount == 1) {

+        goto Exit;

+      }

+    }

+  }

+

+  //

+  // TODO: Rule 4: Prefer home addresses.

+  // TODO: Rule 5: Prefer outgoing interface.

+  // TODO: Rule 6: Prefer matching label.

+  // TODO: Rule 7: Prefer public addresses.

+  //

+

+  //

+  // Rule 8: Use longest matching prefix.

+  //

+  LastCommonLength = Ip6CommonPrefixLen (Source, Destination);

+  TmpAddress       = NULL;

+

+  for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) {

+    AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);

+

+    CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination);

+    if (CurrentCommonLength > LastCommonLength) {

+      LastCommonLength = CurrentCommonLength;

+      TmpAddress       = &AddrInfo->Address;

+    }

+  }

+

+  if (TmpAddress != NULL) {

+    IP6_COPY_ADDRESS (Source, TmpAddress);

+  }

+

+Exit:

+

+  Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0);

+

+  return Status;

+}

+

+/**

+  Select an interface to send the packet generated in the IP6 driver

+  itself: that is, not by the requests of the IP6 child's consumer. Such

+  packets include the ICMPv6 echo replies and other ICMPv6 error packets.

+

+  @param[in]  IpSb                 The IP4 service that wants to send the packets.

+  @param[in]  Destination          The destination of the packet.

+  @param[in, out]  Source          The source of the packet.

+

+  @return NULL if no proper interface is found, otherwise, the interface that

+          can be used to send the system packet from.

+

+**/

+IP6_INTERFACE *

+Ip6SelectInterface (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Destination,

+  IN OUT EFI_IPv6_ADDRESS   *Source

+  )

+{

+  EFI_STATUS                Status;

+  EFI_IPv6_ADDRESS          SelectedSource;

+  IP6_INTERFACE             *IpIf;

+  BOOLEAN                   Exist;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+  ASSERT (Destination != NULL && Source != NULL);

+

+  if (NetIp6IsUnspecifiedAddr (Destination)) {

+    return NULL;

+  }

+

+  if (!NetIp6IsUnspecifiedAddr (Source)) {

+    Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL);

+    ASSERT (Exist);

+

+    return IpIf;

+  }

+

+  //

+  // If source is unspecified, select a source according to the destination.

+  //

+  Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource);

+  if (EFI_ERROR (Status)) {

+    return IpSb->DefaultInterface;

+  }

+

+  Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL);

+  IP6_COPY_ADDRESS (Source, &SelectedSource);

+

+  return IpIf;

+}

+

+/**

+  The default callback function for the system generated packet.

+  It will free the packet.

+

+  @param[in]  Packet        The packet that transmitted.

+  @param[in]  IoStatus      The result of the transmission, succeeded or failed.

+  @param[in]  LinkFlag      Not used when transmitted. Check IP6_FRAME_CALLBACK

+                            for reference.

+  @param[in]  Context       The context provided by us.

+

+**/

+VOID

+Ip6SysPacketSent (

+  NET_BUF                   *Packet,

+  EFI_STATUS                IoStatus,

+  UINT32                    LinkFlag,

+  VOID                      *Context

+  )

+{

+  NetbufFree (Packet);

+  Packet = NULL;

+}

+

+/**

+  Prefix an IP6 basic head and unfragmentable extension headers and a fragment header

+  to the Packet. Used for IP6 fragmentation.

+

+  @param[in]  IpSb             The IP6 service instance to transmit the packet.

+  @param[in]  Packet           The packet to prefix the IP6 header to.

+  @param[in]  Head             The caller supplied header.

+  @param[in]  FragmentOffset   The fragment offset of the data following the header.

+  @param[in]  ExtHdrs          The length of the original extension header.

+  @param[in]  ExtHdrsLen       The length of the extension headers.

+  @param[in]  LastHeader       The pointer of next header of last extension header.

+  @param[in]  HeadLen          The length of the unfragmented part of the IP6 header.

+

+  @retval EFI_BAD_BUFFER_SIZE  There is no enought room in the head space of

+                               Packet.

+  @retval EFI_SUCCESS          The operation performed successfully.

+

+**/

+EFI_STATUS

+Ip6PrependHead (

+  IN IP6_SERVICE            *IpSb,

+  IN NET_BUF                *Packet,

+  IN EFI_IP6_HEADER         *Head,

+  IN UINT16                 FragmentOffset,

+  IN UINT8                  *ExtHdrs,

+  IN UINT32                 ExtHdrsLen,

+  IN UINT8                  LastHeader,

+  IN UINT32                 HeadLen

+  )

+{

+  UINT32                    Len;

+  UINT32                    UnFragExtHdrsLen;

+  EFI_IP6_HEADER            *PacketHead;

+  UINT8                     *UpdatedExtHdrs;

+  EFI_STATUS                Status;

+  UINT8                     NextHeader;

+

+  //

+  // HeadLen is the length of the fixed part of the sequences of fragments, i.e.

+  // the unfragment part.

+  //

+  PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);

+  if (PacketHead == NULL) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  //

+  // Set the head up, convert the host byte order to network byte order

+  //

+  CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));

+  PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER)));

+  Packet->Ip.Ip6            = PacketHead;

+

+  Len              = HeadLen - sizeof (EFI_IP6_HEADER);

+  UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER);

+

+  if (UnFragExtHdrsLen == 0) {

+    PacketHead->NextHeader = IP6_FRAGMENT;

+  }

+

+  //

+  // Append the extension headers: firstly copy the unfragmentable headers, then append

+  // fragmentation header.

+  //

+  if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {

+    NextHeader = Head->NextHeader;

+  } else {

+    NextHeader = PacketHead->NextHeader;

+  }

+

+  Status = Ip6FillFragmentHeader (

+             IpSb,

+             NextHeader,

+             LastHeader,

+             ExtHdrs,

+             ExtHdrsLen,

+             FragmentOffset,

+             &UpdatedExtHdrs

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  CopyMem (

+    (UINT8 *) (PacketHead + 1),

+    UpdatedExtHdrs,

+    UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER)

+    );

+

+  FreePool (UpdatedExtHdrs);

+  return EFI_SUCCESS;

+}

+

+/**

+  Transmit an IP6 packet. The packet comes either from the IP6

+  child's consumer (IpInstance != NULL) or the IP6 driver itself

+  (IpInstance == NULL). It will route the packet, fragment it,

+  then transmit all the fragments through an interface.

+

+  @param[in]  IpSb             The IP6 service instance to transmit the packet.

+  @param[in]  Interface        The IP6 interface to transmit the packet. Ignored

+                               if NULL.

+  @param[in]  IpInstance       The IP6 child that issues the transmission.  It is

+                               NULL if the packet is from the system.

+  @param[in]  Packet           The user data to send, excluding the IP header.

+  @param[in]  Head             The caller supplied header. The caller should set

+                               the  following header fields: NextHeader, HopLimit,

+                               Src, Dest, FlowLabel, PayloadLength. This function

+                               will fill in the Ver, TrafficClass.

+  @param[in]  ExtHdrs          The extension headers to append to the IPv6 basic

+                               header.

+  @param[in]  ExtHdrsLen       The length of the extension headers.

+  @param[in]  Callback         The callback function to issue when transmission

+                               completed.

+  @param[in]  Context          The opaque context for the callback.

+

+  @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.

+  @retval EFI_NO_MAPPING        There is no interface to the destination.

+  @retval EFI_NOT_FOUND         There is no route to the destination.

+  @retval EFI_SUCCESS           The packet successfully transmitted.

+  @retval EFI_OUT_OF_RESOURCES  Failed to finish the operation due to lack of

+                                resources.

+  @retval Others                Failed to transmit the packet.

+

+**/

+EFI_STATUS

+Ip6Output (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_INTERFACE          *Interface   OPTIONAL,

+  IN IP6_PROTOCOL           *IpInstance  OPTIONAL,

+  IN NET_BUF                *Packet,

+  IN EFI_IP6_HEADER         *Head,

+  IN UINT8                  *ExtHdrs,

+  IN UINT32                 ExtHdrsLen,

+  IN IP6_FRAME_CALLBACK     Callback,

+  IN VOID                   *Context

+  )

+{

+  IP6_INTERFACE             *IpIf;

+  EFI_IPv6_ADDRESS          NextHop;

+  IP6_NEIGHBOR_ENTRY        *NeighborCache;

+  IP6_ROUTE_CACHE_ENTRY     *RouteCache;

+  EFI_STATUS                Status;

+  UINT32                    Mtu;

+  UINT32                    HeadLen;

+  UINT16                    FragmentOffset;

+  UINT8                     *LastHeader;

+  UINT32                    UnFragmentLen;

+  UINT32                    UnFragmentHdrsLen;

+  UINT32                    FragmentHdrsLen;

+  UINT16                    *Checksum;

+  UINT16                    PacketChecksum;

+  UINT16                    PseudoChecksum;

+  UINT32                    Index;

+  UINT32                    PacketLen;

+  UINT32                    RealExtLen;

+  UINT32                    Offset;

+  NET_BUF                   *TmpPacket;

+  NET_BUF                   *Fragment;

+  UINT32                    Num;

+  UINT8                     *Buf;

+  EFI_IP6_HEADER            *PacketHead;

+  IP6_ICMP_HEAD             *IcmpHead;

+  IP6_TXTOKEN_WRAP          *Wrap;

+  IP6_ROUTE_ENTRY           *RouteEntry;

+  UINT8                     *UpdatedExtHdrs;

+  UINT8                     NextHeader;

+  UINT8                     LastHeaderBackup;

+  BOOLEAN                   FragmentHeadInserted;

+  UINT8                     *ExtHdrsBackup;

+  UINT8                     NextHeaderBackup;

+  EFI_IPv6_ADDRESS          Source;

+  EFI_IPv6_ADDRESS          Destination;

+

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  //

+  // RFC2460: Each extension header is an integer multiple of 8 octets long,

+  // in order to retain 8-octet alignment for subsequent headers.

+  //

+  if ((ExtHdrsLen & 0x7) != 0) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  LastHeader = NULL;

+

+  Ip6IsExtsValid (

+    NULL,

+    NULL,

+    &Head->NextHeader,

+    ExtHdrs,

+    ExtHdrsLen,

+    FALSE,

+    NULL,

+    &LastHeader,

+    NULL,

+    NULL,

+    NULL

+    );

+

+  //

+  // Select an interface/source for system packet, application

+  // should select them itself.

+  //

+  IpIf = Interface;

+  if (IpIf == NULL) {

+    //

+    // IpInstance->Interface is NULL when IpInstance is configured with both stationaddress

+    // and destinationaddress is unspecified.

+    //

+    if (IpInstance == NULL || IpInstance->Interface == NULL) {

+      IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress);

+      if (IpInstance != NULL) {

+        IpInstance->Interface = IpIf;

+      }

+    } else {

+      IpIf = IpInstance->Interface;

+    }

+  }

+

+  if (IpIf == NULL) {

+    return EFI_NO_MAPPING;

+  }

+

+  //

+  // Update the common field in Head here.

+  //

+  Head->Version       = 6;

+  Head->TrafficClassL = 0;

+  Head->TrafficClassH = 0;

+

+  Checksum            = NULL;

+  NextHeader          = *LastHeader;

+

+  switch (NextHeader) {

+  case EFI_IP_PROTO_UDP:

+    Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);

+    ASSERT (Packet->Udp != NULL);

+    if (Packet->Udp->Checksum == 0) {

+      Checksum = &Packet->Udp->Checksum;

+    }

+    break;

+

+  case EFI_IP_PROTO_TCP:

+    Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL);

+    ASSERT (Packet->Tcp != NULL);

+    if (Packet->Tcp->Checksum == 0) {

+      Checksum = &Packet->Tcp->Checksum;

+    }

+    break;

+

+  case IP6_ICMP:

+    //

+    // Don't send ICMP packet to an IPv6 anycast address.

+    //

+    if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);

+    ASSERT (IcmpHead != NULL);

+    if (IcmpHead->Checksum == 0) {

+      Checksum = &IcmpHead->Checksum;

+    }

+    break;

+

+  default:

+    break;

+  }

+

+  if (Checksum != NULL) {

+    //

+    // Calculate the checksum for upper layer protocol if it is not calculated due to lack of

+    // IPv6 source address.

+    //

+    PacketChecksum = NetbufChecksum (Packet);

+    PseudoChecksum = NetIp6PseudoHeadChecksum (

+                      &Head->SourceAddress,

+                      &Head->DestinationAddress,

+                      NextHeader,

+                      Packet->TotalSize

+                      );

+    *Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum);

+  }

+

+  Status = Ip6IpSecProcessPacket (

+             IpSb,

+             Head,

+             LastHeader, // no need get the lasthead value for output

+             &Packet,

+             ExtHdrs,

+             ExtHdrsLen,

+             EfiIPsecOutBound,

+             Context

+             );

+

+  if (EFI_ERROR(Status)) {

+    return Status;

+  }

+

+  LastHeader = NULL;

+  //

+  // Check incoming parameters.

+  //

+  if (!Ip6IsExtsValid (

+         IpSb,

+         Packet,

+         &Head->NextHeader,

+         ExtHdrs,

+         ExtHdrsLen,

+         FALSE,

+         NULL,

+         &LastHeader,

+         &RealExtLen,

+         &UnFragmentHdrsLen,

+         NULL

+         )) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if ((RealExtLen & 0x7) != 0) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  LastHeaderBackup = *LastHeader;

+

+  //

+  // Perform next hop determination:

+  // For multicast packets, the next-hop is always the destination address and

+  // is considered to be on-link.

+  //

+  if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {

+    IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress);

+  } else {

+    //

+    // For unicast packets, use a combination of the Destination Cache, the Prefix List

+    // and the Default Router List to determine the IP address of the appropriate next hop.

+    //

+    RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress);

+    if (RouteCache == NULL) {

+      return EFI_NOT_FOUND;

+    }

+

+    IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop);

+    Ip6FreeRouteCacheEntry (RouteCache);

+  }

+

+  //

+  // Examines the Neighbor Cache for link-layer information about that neighbor.

+  // DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error.

+  //

+  if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) {

+    NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop);

+    if (NeighborCache == NULL) {

+      NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL);

+

+      if (NeighborCache == NULL) {

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      //

+      // Send out multicast neighbor solicitation for address resolution immediatly.

+      //

+      Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);

+      Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);

+      if (EFI_ERROR (Status)) {

+        return Status;

+      }

+

+      Status = Ip6SendNeighborSolicit (

+                 IpSb,

+                 &Source,

+                 &Destination,

+                 &NeighborCache->Neighbor,

+                 &IpSb->SnpMode.CurrentAddress

+                 );

+      if (EFI_ERROR (Status)) {

+        return Status;

+      }

+

+      --NeighborCache->Transmit;

+      NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1;

+    }

+

+    NeighborCache->Interface = IpIf;

+  }

+

+  UpdatedExtHdrs       = NULL;

+  ExtHdrsBackup        = NULL;

+  NextHeaderBackup     = 0;

+  FragmentHeadInserted = FALSE;

+

+  //

+  // Check whether we received Packet Too Big message for the packet sent to the

+  // Destination. If yes include a Fragment Header in the subsequent packets.

+  //

+  RouteEntry = Ip6FindRouteEntry (

+                 IpSb->RouteTable,

+                 &Head->DestinationAddress,

+                 NULL

+                 );

+  if (RouteEntry != NULL) {

+    if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) {

+

+      //

+      // FragmentHead is inserted after Hop-by-Hop Options header, Destination

+      // Options header (first occur), Routing header, and before Fragment header,

+      // Authentication header, Encapsulating Security Payload header, and

+      // Destination Options header (last occur), and upper-layer header.

+      //

+      Status = Ip6FillFragmentHeader (

+                 IpSb,

+                 Head->NextHeader,

+                 LastHeaderBackup,

+                 ExtHdrs,

+                 ExtHdrsLen,

+                 0,

+                 &UpdatedExtHdrs

+                 );

+      if (EFI_ERROR (Status)) {

+        return Status;

+      }

+

+      if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {

+        NextHeaderBackup = Head->NextHeader;

+        Head->NextHeader = IP6_FRAGMENT;

+      }

+

+      ExtHdrsBackup    = ExtHdrs;

+      ExtHdrs          = UpdatedExtHdrs;

+      ExtHdrsLen       = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);

+      RealExtLen       = RealExtLen + sizeof (IP6_FRAGMENT_HEADER);

+

+      mIp6Id++;

+

+      FragmentHeadInserted = TRUE;

+    }

+

+    Ip6FreeRouteEntry (RouteEntry);

+  }

+

+  //

+  // OK, selected the source and route, fragment the packet then send

+  // them. Tag each fragment other than the first one as spawn from it.

+  // Each extension header is an integar multiple of 8 octets long, in

+  // order to retain 8-octet alignment for subsequent headers.

+  //

+  Mtu     = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER);

+  HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen;

+

+  if (Packet->TotalSize + HeadLen > Mtu) {

+    //

+    // Remove the inserted Fragment Header since we need fragment the packet.

+    //

+    if (FragmentHeadInserted) {

+      ExtHdrs    = ExtHdrsBackup;

+      ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER);

+

+      if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {

+        Head->NextHeader = NextHeaderBackup;

+      }

+    }

+

+    FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen;

+

+    //

+    // The packet is beyond the maximum which can be described through the

+    // fragment offset field in Fragment header.

+    //

+    if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) {

+      Status = EFI_BAD_BUFFER_SIZE;

+      goto Error;

+    }

+

+    if (FragmentHdrsLen != 0) {

+      //

+      // Append the fragmentable extension hdrs before the upper layer payload

+      // to form a new NET_BUF. This NET_BUF contains all the buffer which will

+      // be fragmented below.

+      //

+      TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen);

+      ASSERT (TmpPacket != NULL);

+

+      //

+      // Allocate the space to contain the fragmentable hdrs and copy the data.

+      //

+      Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE);

+      ASSERT (Buf != NULL);

+      CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen);

+

+      //

+      // Free the old Packet.

+      //

+      NetbufFree (Packet);

+      Packet = TmpPacket;

+    }

+

+    //

+    // The unfragment part which appears in every fragmented IPv6 packet includes

+    // the IPv6 header, the unfragmentable extension hdrs and the fragment header.

+    //

+    UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER);

+

+    //

+    // Mtu now is the length of the fragment part in a full-length fragment.

+    //

+    Mtu = (Mtu - UnFragmentLen) & (~0x07);

+    Num = (Packet->TotalSize + Mtu - 1) / Mtu;

+

+    for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) {

+      //

+      // Get fragment from the Packet, append UnFragnmentLen spare buffer

+      // before the fragmented data, the corresponding data is filled in later.

+      //

+      Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen);

+      if (Fragment == NULL) {

+        Status = EFI_OUT_OF_RESOURCES;

+        goto Error;

+      }

+

+      FragmentOffset = (UINT16) ((UINT16) Offset | 0x1);

+      if (Index == Num - 1){

+        //

+        // The last fragment, clear the M flag.

+        //

+        FragmentOffset &= (~0x1);

+      }

+

+      Status = Ip6PrependHead (

+                 IpSb,

+                 Fragment,

+                 Head,

+                 FragmentOffset,

+                 ExtHdrs,

+                 ExtHdrsLen,

+                 LastHeaderBackup,

+                 UnFragmentLen

+                 );

+      ASSERT (Status == EFI_SUCCESS);

+

+      Status = Ip6SendFrame (

+                 IpIf,

+                 IpInstance,

+                 Fragment,

+                 &NextHop,

+                 Ip6SysPacketSent,

+                 Packet

+                 );

+      if (EFI_ERROR (Status)) {

+        goto Error;

+      }

+

+      //

+      // The last fragment of upper layer packet, update the IP6 token status.

+      //

+      if ((Index == Num -1) && (Context != NULL)) {

+        Wrap                = (IP6_TXTOKEN_WRAP *) Context;

+        Wrap->Token->Status = Status;

+      }

+

+      Offset    += PacketLen;

+      PacketLen = Packet->TotalSize - Offset;

+      if (PacketLen > Mtu) {

+        PacketLen = Mtu;

+      }

+    }

+

+    NetbufFree (Packet);

+    mIp6Id++;

+

+    if (UpdatedExtHdrs != NULL) {

+      FreePool (UpdatedExtHdrs);

+    }

+

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Need not fragment the packet, send it in one frame.

+  //

+  PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);

+  if (PacketHead == NULL) {

+    Status = EFI_BAD_BUFFER_SIZE;

+    goto Error;

+  }

+

+  CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));

+  Packet->Ip.Ip6 = PacketHead;

+

+  if (ExtHdrs != NULL) {

+    Buf = (UINT8 *) (PacketHead + 1);

+    CopyMem (Buf, ExtHdrs, ExtHdrsLen);

+  }

+

+  if (UpdatedExtHdrs != NULL) {

+    //

+    // A Fragment Header is inserted to the packet, update the payload length.

+    //

+    PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) +

+                                sizeof (IP6_FRAGMENT_HEADER));

+    PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength);

+    FreePool (UpdatedExtHdrs);

+  }

+

+  return Ip6SendFrame (

+           IpIf,

+           IpInstance,

+           Packet,

+           &NextHop,

+           Callback,

+           Context

+           );

+

+Error:

+  if (UpdatedExtHdrs != NULL) {

+    FreePool (UpdatedExtHdrs);

+  }

+  Ip6CancelPacket (IpIf, Packet, Status);

+  return Status;

+}

+

+/**

+  The filter function to find a packet and all its fragments.

+  The packet's fragments have their Context set to the packet.

+

+  @param[in]  Frame            The frames hold by the low level interface.

+  @param[in]  Context          Context to the function, which is the packet.

+

+  @retval TRUE                 This is the packet to cancel or its fragments.

+  @retval FALSE                This is an unrelated packet.

+

+**/

+BOOLEAN

+Ip6CancelPacketFragments (

+  IN IP6_LINK_TX_TOKEN   *Frame,

+  IN VOID                *Context

+  )

+{

+  if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Remove all the frames on the interface that pass the FrameToCancel,

+  either queued on ARP queues or that have already been delivered to

+  MNP and not yet recycled.

+

+  @param[in]  Interface     Interface to remove the frames from.

+  @param[in]  IoStatus      The transmit status returned to the frames' callback.

+  @param[in]  FrameToCancel Function to select the frame to cancel; NULL to select all.

+  @param[in]  Context       Opaque parameters passed to FrameToCancel. Ignored if

+                            FrameToCancel is NULL.

+

+**/

+VOID

+Ip6CancelFrames (

+  IN IP6_INTERFACE          *Interface,

+  IN EFI_STATUS             IoStatus,

+  IN IP6_FRAME_TO_CANCEL    FrameToCancel   OPTIONAL,

+  IN VOID                   *Context        OPTIONAL

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_LINK_TX_TOKEN         *Token;

+  IP6_SERVICE               *IpSb;

+  IP6_NEIGHBOR_ENTRY        *ArpQue;

+  EFI_STATUS                Status;

+

+  IpSb = Interface->Service;

+  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);

+

+  //

+  // Cancel all the pending frames on ARP requests

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {

+    ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);

+

+    Status = Ip6FreeNeighborEntry (

+               IpSb,

+               ArpQue,

+               FALSE,

+               FALSE,

+               IoStatus,

+               FrameToCancel,

+               Context

+               );

+    ASSERT_EFI_ERROR (Status);

+  }

+

+  //

+  // Cancel all the frames that have been delivered to MNP

+  // but not yet recycled.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {

+    Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);

+

+    if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {

+      IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken);

+    }

+  }

+}

+

+/**

+  Cancel the Packet and all its fragments.

+

+  @param[in]  IpIf                 The interface from which the Packet is sent.

+  @param[in]  Packet               The Packet to cancel.

+  @param[in]  IoStatus             The status returns to the sender.

+

+**/

+VOID

+Ip6CancelPacket (

+  IN IP6_INTERFACE    *IpIf,

+  IN NET_BUF          *Packet,

+  IN EFI_STATUS       IoStatus

+  )

+{

+  Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet);

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Output.h b/NetworkPkg/Ip6Dxe/Ip6Output.h
new file mode 100644
index 0000000..80abe85
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Output.h
@@ -0,0 +1,141 @@
+/** @file

+  The internal functions and routines to transmit the IP6 packet.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_OUTPUT_H__

+#define __EFI_IP6_OUTPUT_H__

+

+extern UINT32 mIp6Id;

+

+/**

+  Output all the available source addresses to the list entry head SourceList. The

+  number of source addresses are also returned.

+

+  @param[in]       IpSb             Points to a IP6 service binding instance.

+  @param[in]       Destination      The IPv6 destination address.

+  @param[out]      Source           The selected IPv6 source address according to

+                                    the Destination.

+

+  @retval EFI_SUCCESS           The source addresses were copied to the list entry

+                                head SourceList.

+  @retval EFI_NO_MAPPING        The IPv6 stack is not auto configured.

+

+**/

+EFI_STATUS

+Ip6SelectSourceAddress (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Destination,

+  OUT EFI_IPv6_ADDRESS      *Source

+  );

+

+/**

+  The default callback function for system generated packet.

+  It will free the packet.

+

+  @param[in]  Packet        The packet that transmitted.

+  @param[in]  IoStatus      The result of the transmission: succeeded or failed.

+  @param[in]  LinkFlag      Not used when transmission. Check IP6_FRAME_CALLBACK

+                            for reference.

+  @param[in]  Context       The context provided by us.

+

+**/

+VOID

+Ip6SysPacketSent (

+  NET_BUF                   *Packet,

+  EFI_STATUS                IoStatus,

+  UINT32                    LinkFlag,

+  VOID                      *Context

+  );

+

+/**

+  Transmit an IP6 packet. The packet comes either from the IP6

+  child's consumer (IpInstance != NULL) or the IP6 driver itself

+  (IpInstance == NULL). It will route the packet, fragment it,

+  then transmit all the fragments through an interface.

+

+  @param[in]  IpSb             The IP6 service instance to transmit the packet.

+  @param[in]  Interface        The IP6 interface to transmit the packet. Ignored

+                               if NULL.

+  @param[in]  IpInstance       The IP6 child that issues the transmission.  It is

+                               NULL if the packet is from the system.

+  @param[in]  Packet           The user data to send, excluding the IP header.

+  @param[in]  Head             The caller supplied header. The caller should set

+                               the  following header fields: NextHeader, HopLimit,

+                               Src, Dest, FlowLabel, PayloadLength. This function

+                               will fill in the Ver, TrafficClass.

+  @param[in]  ExtHdrs          The extension headers to append to the IPv6 basic

+                               header.

+  @param[in]  ExtHdrsLen       The length of the extension headers.

+  @param[in]  Callback         The callback function to issue when transmission

+                               completed.

+  @param[in]  Context          The opaque context for the callback.

+

+  @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.

+  @retval EFI_NO_MAPPING        There is no interface to the destination.

+  @retval EFI_NOT_FOUND         There is no route to the destination.

+  @retval EFI_SUCCESS           The packet successfully transmitted.

+  @retval EFI_OUT_OF_RESOURCES  Failed to finish the operation due to lack of

+                                resources.

+  @retval Others                Failed to transmit the packet.

+

+**/

+EFI_STATUS

+Ip6Output (

+  IN IP6_SERVICE            *IpSb,

+  IN IP6_INTERFACE          *Interface   OPTIONAL,

+  IN IP6_PROTOCOL           *IpInstance  OPTIONAL,

+  IN NET_BUF                *Packet,

+  IN EFI_IP6_HEADER         *Head,

+  IN UINT8                  *ExtHdrs,

+  IN UINT32                 ExtHdrsLen,

+  IN IP6_FRAME_CALLBACK     Callback,

+  IN VOID                   *Context

+  );

+

+/**

+  Remove all the frames on the interface that pass the FrameToCancel,

+  either queued on ARP queues, or that have already been delivered to

+  MNP and not yet recycled.

+

+  @param[in]  Interface     Interface to remove the frames from.

+  @param[in]  IoStatus      The transmit status returned to the frames' callback.

+  @param[in]  FrameToCancel Function to select the frame to cancel; NULL to select all.

+  @param[in]  Context       Opaque parameters passed to FrameToCancel. Ignored if

+                            FrameToCancel is NULL.

+

+**/

+VOID

+Ip6CancelFrames (

+  IN IP6_INTERFACE          *Interface,

+  IN EFI_STATUS             IoStatus,

+  IN IP6_FRAME_TO_CANCEL    FrameToCancel   OPTIONAL,

+  IN VOID                   *Context        OPTIONAL

+  );

+

+/**

+  Cancel the Packet and all its fragments.

+

+  @param[in]  IpIf                 The interface from which the Packet is sent.

+  @param[in]  Packet               The Packet to cancel.

+  @param[in]  IoStatus             The status returns to the sender.

+

+**/

+VOID

+Ip6CancelPacket (

+  IN IP6_INTERFACE    *IpIf,

+  IN NET_BUF          *Packet,

+  IN EFI_STATUS       IoStatus

+  );

+

+#endif

diff --git a/NetworkPkg/Ip6Dxe/Ip6Route.c b/NetworkPkg/Ip6Dxe/Ip6Route.c
new file mode 100644
index 0000000..bba365c
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Route.c
@@ -0,0 +1,635 @@
+/** @file

+  The functions and routines to handle the route caches and route table.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Ip6Impl.h"

+

+/**

+  This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value

+  as the index of the route cache bucket according to the prefix of two IPv6 addresses.

+

+  @param[in]  Ip1     The IPv6 address.

+  @param[in]  Ip2     The IPv6 address.

+

+  @return The hash value of the prefix of two IPv6 addresses.

+

+**/

+UINT32

+Ip6RouteCacheHash (

+  IN EFI_IPv6_ADDRESS       *Ip1,

+  IN EFI_IPv6_ADDRESS       *Ip2

+  )

+{

+  UINT32 Prefix1;

+  UINT32 Prefix2;

+

+  Prefix1 = *((UINT32 *) ((UINTN *) (Ip1)));

+  Prefix2 = *((UINT32 *) ((UINTN *) (Ip2)));

+

+  return ((UINT32) (Prefix1 ^ Prefix2) % IP6_ROUTE_CACHE_HASH_SIZE);

+}

+

+/**

+  Allocate a route entry then initialize it with the Destination/PrefixLength

+  and Gateway.

+

+  @param[in]  Destination     The IPv6 destination address. This is an optional

+                              parameter that may be NULL.

+  @param[in]  PrefixLength    The destination network's prefix length.

+  @param[in]  GatewayAddress  The next hop address. This is an optional parameter

+                              that may be NULL.

+

+  @return NULL if failed to allocate memeory; otherwise, the newly created route entry.

+

+**/

+IP6_ROUTE_ENTRY *

+Ip6CreateRouteEntry (

+  IN EFI_IPv6_ADDRESS       *Destination    OPTIONAL,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *GatewayAddress OPTIONAL

+  )

+{

+  IP6_ROUTE_ENTRY           *RtEntry;

+

+  RtEntry = AllocateZeroPool (sizeof (IP6_ROUTE_ENTRY));

+

+  if (RtEntry == NULL) {

+    return NULL;

+  }

+

+  RtEntry->RefCnt       = 1;

+  RtEntry->Flag         = 0;

+  RtEntry->PrefixLength = PrefixLength;

+

+  if (Destination != NULL) {

+    IP6_COPY_ADDRESS (&RtEntry->Destination, Destination);

+  }

+

+  if (GatewayAddress != NULL) {

+    IP6_COPY_ADDRESS (&RtEntry->NextHop, GatewayAddress);

+  }

+

+  return RtEntry;

+}

+

+/**

+  Free the route table entry. It is reference counted.

+

+  @param[in, out]  RtEntry  The route entry to free.

+

+**/

+VOID

+Ip6FreeRouteEntry (

+  IN OUT IP6_ROUTE_ENTRY    *RtEntry

+  )

+{

+  ASSERT ((RtEntry != NULL) && (RtEntry->RefCnt > 0));

+

+  if (--RtEntry->RefCnt == 0) {

+    FreePool (RtEntry);

+  }

+}

+

+/**

+  Search the route table for a most specific match to the Dst. It searches

+  from the longest route area (prefix length == 128) to the shortest route area

+  (default routes). In each route area, it will first search the instance's

+  route table, then the default route table. This is required per the following

+  requirements:

+  1. IP search the route table for a most specific match.

+  2. The local route entries have precedence over the default route entry.

+

+  @param[in]  RtTable       The route table to search from.

+  @param[in]  Destination   The destionation address to search. If NULL, search

+                            the route table by NextHop.

+  @param[in]  NextHop       The next hop address. If NULL, search the route table

+                            by Destination.

+

+  @return NULL if no route matches the Dst. Otherwise, the point to the

+  @return most specific route to the Dst.

+

+**/

+IP6_ROUTE_ENTRY *

+Ip6FindRouteEntry (

+  IN IP6_ROUTE_TABLE        *RtTable,

+  IN EFI_IPv6_ADDRESS       *Destination OPTIONAL,

+  IN EFI_IPv6_ADDRESS       *NextHop     OPTIONAL

+  )

+{

+  LIST_ENTRY                *Entry;

+  IP6_ROUTE_ENTRY           *RtEntry;

+  INTN                      Index;

+

+  ASSERT (Destination != NULL || NextHop != NULL);

+

+  RtEntry = NULL;

+

+  for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) {

+    NET_LIST_FOR_EACH (Entry, &RtTable->RouteArea[Index]) {

+      RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);

+

+      if (Destination != NULL) {

+        if (NetIp6IsNetEqual (Destination, &RtEntry->Destination, RtEntry->PrefixLength)) {

+          NET_GET_REF (RtEntry);

+          return RtEntry;

+        }

+      } else if (NextHop != NULL) {

+        if (NetIp6IsNetEqual (NextHop, &RtEntry->NextHop, RtEntry->PrefixLength)) {

+          NET_GET_REF (RtEntry);

+          return RtEntry;

+        }

+      }

+

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  Allocate and initialize a IP6 route cache entry.

+

+  @param[in]  Dst           The destination address.

+  @param[in]  Src           The source address.

+  @param[in]  GateWay       The next hop address.

+  @param[in]  Tag           The tag from the caller. This marks all the cache entries

+                            spawned from one route table entry.

+

+  @return NULL if failed to allocate memory for the cache. Otherwise, point

+          to the created route cache entry.

+

+**/

+IP6_ROUTE_CACHE_ENTRY *

+Ip6CreateRouteCacheEntry (

+  IN EFI_IPv6_ADDRESS       *Dst,

+  IN EFI_IPv6_ADDRESS       *Src,

+  IN EFI_IPv6_ADDRESS       *GateWay,

+  IN UINTN                  Tag

+  )

+{

+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;

+

+  RtCacheEntry = AllocatePool (sizeof (IP6_ROUTE_CACHE_ENTRY));

+

+  if (RtCacheEntry == NULL) {

+    return NULL;

+  }

+

+  RtCacheEntry->RefCnt = 1;

+  RtCacheEntry->Tag    = Tag;

+

+  IP6_COPY_ADDRESS (&RtCacheEntry->Destination, Dst);

+  IP6_COPY_ADDRESS (&RtCacheEntry->Source, Src);

+  IP6_COPY_ADDRESS (&RtCacheEntry->NextHop, GateWay);

+

+  return RtCacheEntry;

+}

+

+/**

+  Free the route cache entry. It is reference counted.

+

+  @param[in, out]  RtCacheEntry  The route cache entry to free.

+

+**/

+VOID

+Ip6FreeRouteCacheEntry (

+  IN OUT IP6_ROUTE_CACHE_ENTRY  *RtCacheEntry

+  )

+{

+  ASSERT (RtCacheEntry->RefCnt > 0);

+

+  if (--RtCacheEntry->RefCnt == 0) {

+    FreePool (RtCacheEntry);

+  }

+}

+

+/**

+  Find a route cache with the destination and source address. This is

+  used by the ICMPv6 redirect messasge process.

+

+  @param[in]  RtTable       The route table to search the cache for.

+  @param[in]  Dest          The destination address.

+  @param[in]  Src           The source address.

+

+  @return NULL if no route entry to the (Dest, Src). Otherwise, the pointer

+          to the correct route cache entry.

+

+**/

+IP6_ROUTE_CACHE_ENTRY *

+Ip6FindRouteCache (

+  IN IP6_ROUTE_TABLE        *RtTable,

+  IN EFI_IPv6_ADDRESS       *Dest,

+  IN EFI_IPv6_ADDRESS       *Src

+  )

+{

+  LIST_ENTRY                *Entry;

+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;

+  UINT32                    Index;

+

+  Index = IP6_ROUTE_CACHE_HASH (Dest, Src);

+

+  NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) {

+    RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);

+

+    if (EFI_IP6_EQUAL (Dest, &RtCacheEntry->Destination)&& EFI_IP6_EQUAL (Src, &RtCacheEntry->Source)) {

+      NET_GET_REF (RtCacheEntry);

+      return RtCacheEntry;

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  Build an array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number

+  of EFI_IP6_ROUTE_TABLE is also returned.

+

+  @param[in]  RouteTable        The pointer of IP6_ROUTE_TABLE internal used.

+  @param[out] EfiRouteCount     The number of returned route entries.

+  @param[out] EfiRouteTable     The pointer to the array of EFI_IP6_ROUTE_TABLE.

+                                If NULL, only the route entry count is returned.

+

+  @retval EFI_SUCCESS           The EFI_IP6_ROUTE_TABLE successfully built.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.

+

+**/

+EFI_STATUS

+Ip6BuildEfiRouteTable (

+  IN IP6_ROUTE_TABLE        *RouteTable,

+  OUT UINT32                *EfiRouteCount,

+  OUT EFI_IP6_ROUTE_TABLE   **EfiRouteTable OPTIONAL

+  )

+{

+  LIST_ENTRY                *Entry;

+  IP6_ROUTE_ENTRY           *RtEntry;

+  EFI_IP6_ROUTE_TABLE       *EfiTable;

+  UINT32                    Count;

+  INT32                     Index;

+

+  ASSERT (EfiRouteCount != NULL);

+

+  Count          = RouteTable->TotalNum;

+  *EfiRouteCount = Count;

+

+  if ((EfiRouteTable == NULL) || (Count == 0)) {

+    return EFI_SUCCESS;

+  }

+

+  if (*EfiRouteTable == NULL) {

+    *EfiRouteTable = AllocatePool (sizeof (EFI_IP6_ROUTE_TABLE) * Count);

+    if (*EfiRouteTable == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  }

+

+  EfiTable = *EfiRouteTable;

+

+  //

+  // Copy the route entry to EFI route table.

+  //

+  Count = 0;

+

+  for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) {

+

+    NET_LIST_FOR_EACH (Entry, &(RouteTable->RouteArea[Index])) {

+      RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);

+

+      Ip6CopyAddressByPrefix (

+        &EfiTable[Count].Destination,

+        &RtEntry->Destination,

+        RtEntry->PrefixLength

+        );

+

+      IP6_COPY_ADDRESS (&EfiTable[Count].Gateway, &RtEntry->NextHop);

+      EfiTable[Count].PrefixLength = RtEntry->PrefixLength;

+

+      Count++;

+    }

+  }

+

+  ASSERT (Count == RouteTable->TotalNum);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Create an empty route table. This includes its internal route cache.

+

+  @return NULL if failed to allocate memory for the route table. Otherwise,

+          the point to newly created route table.

+

+**/

+IP6_ROUTE_TABLE *

+Ip6CreateRouteTable (

+  VOID

+  )

+{

+  IP6_ROUTE_TABLE           *RtTable;

+  UINT32                    Index;

+

+  RtTable = AllocatePool (sizeof (IP6_ROUTE_TABLE));

+  if (RtTable == NULL) {

+    return NULL;

+  }

+

+  RtTable->RefCnt   = 1;

+  RtTable->TotalNum = 0;

+

+  for (Index = 0; Index < IP6_PREFIX_NUM; Index++) {

+    InitializeListHead (&RtTable->RouteArea[Index]);

+  }

+

+  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {

+    InitializeListHead (&RtTable->Cache.CacheBucket[Index]);

+    RtTable->Cache.CacheNum[Index] = 0;

+  }

+

+  return RtTable;

+}

+

+/**

+  Free the route table and its associated route cache. Route

+  table is reference counted.

+

+  @param[in, out]  RtTable      The route table to free.

+

+**/

+VOID

+Ip6CleanRouteTable (

+  IN OUT IP6_ROUTE_TABLE        *RtTable

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_ROUTE_ENTRY           *RtEntry;

+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;

+  UINT32                    Index;

+

+  ASSERT (RtTable->RefCnt > 0);

+

+  if (--RtTable->RefCnt > 0) {

+    return ;

+  }

+

+  //

+  // Free all the route table entry and its route cache.

+  //

+  for (Index = 0; Index < IP6_PREFIX_NUM; Index++) {

+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->RouteArea[Index]) {

+      RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);

+      RemoveEntryList (Entry);

+      Ip6FreeRouteEntry (RtEntry);

+    }

+  }

+

+  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {

+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->Cache.CacheBucket[Index]) {

+      RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);

+      RemoveEntryList (Entry);

+      Ip6FreeRouteCacheEntry (RtCacheEntry);

+    }

+  }

+

+  FreePool (RtTable);

+}

+

+/**

+  Remove all the cache entries bearing the Tag. When a route cache

+  entry is created, it is tagged with the address of route entry

+  from which it is spawned. When a route entry is deleted, the cache

+  entries spawned from it are also deleted.

+

+  @param[in]  RtCache       Route cache to remove the entries from.

+  @param[in]  Tag           The Tag of the entries to remove.

+

+**/

+VOID

+Ip6PurgeRouteCache (

+  IN IP6_ROUTE_CACHE        *RtCache,

+  IN UINTN                  Tag

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;

+  UINT32                    Index;

+

+  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {

+    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) {

+

+      RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);

+

+      if (RtCacheEntry->Tag == Tag) {

+        RemoveEntryList (Entry);

+        Ip6FreeRouteCacheEntry (RtCacheEntry);

+      }

+    }

+  }

+}

+

+/**

+  Add a route entry to the route table. It is the help function for EfiIp6Routes.

+

+  @param[in, out]  RtTable        Route table to add route to.

+  @param[in]       Destination    The destination of the network.

+  @param[in]       PrefixLength   The PrefixLength of the destination.

+  @param[in]       GatewayAddress The next hop address.

+

+  @retval EFI_ACCESS_DENIED     The same route already exists.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the entry.

+  @retval EFI_SUCCESS           The route was added successfully.

+

+**/

+EFI_STATUS

+Ip6AddRoute (

+  IN OUT IP6_ROUTE_TABLE    *RtTable,

+  IN EFI_IPv6_ADDRESS       *Destination,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *GatewayAddress

+  )

+{

+  LIST_ENTRY                *ListHead;

+  LIST_ENTRY                *Entry;

+  IP6_ROUTE_ENTRY           *Route;

+

+  ListHead = &RtTable->RouteArea[PrefixLength];

+

+  //

+  // First check whether the route exists

+  //

+  NET_LIST_FOR_EACH (Entry, ListHead) {

+    Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);

+

+    if (NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength) &&

+        EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {

+      return EFI_ACCESS_DENIED;

+    }

+  }

+

+  //

+  // Create a route entry and insert it to the route area.

+  //

+  Route = Ip6CreateRouteEntry (Destination, PrefixLength, GatewayAddress);

+

+  if (Route == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  if (NetIp6IsUnspecifiedAddr (GatewayAddress)) {

+    Route->Flag = IP6_DIRECT_ROUTE;

+  }

+

+  InsertHeadList (ListHead, &Route->Link);

+  RtTable->TotalNum++;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Remove a route entry and all the route caches spawn from it.

+  It is the help function for EfiIp6Routes.

+

+  @param[in, out] RtTable           The route table to remove the route from.

+  @param[in]      Destination       The destination network.

+  @param[in]      PrefixLength      The PrefixLength of the Destination.

+  @param[in]      GatewayAddress    The next hop address.

+

+  @retval EFI_SUCCESS           The route entry was successfully removed.

+  @retval EFI_NOT_FOUND         There is no route entry in the table with that

+                                property.

+

+**/

+EFI_STATUS

+Ip6DelRoute (

+  IN OUT IP6_ROUTE_TABLE    *RtTable,

+  IN EFI_IPv6_ADDRESS       *Destination,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *GatewayAddress

+  )

+{

+  LIST_ENTRY                *ListHead;

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  IP6_ROUTE_ENTRY           *Route;

+  UINT32                    TotalNum;

+

+  ListHead = &RtTable->RouteArea[PrefixLength];

+  TotalNum = RtTable->TotalNum;

+

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, ListHead) {

+    Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);

+

+    if (Destination != NULL && !NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength)) {

+      continue;

+    }

+    if (GatewayAddress != NULL && !EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {

+      continue;

+    }

+

+    Ip6PurgeRouteCache (&RtTable->Cache, (UINTN) Route);

+    RemoveEntryList (Entry);

+    Ip6FreeRouteEntry (Route);

+

+    ASSERT (RtTable->TotalNum > 0);

+    RtTable->TotalNum--;

+  }

+

+  return TotalNum == RtTable->TotalNum ? EFI_NOT_FOUND : EFI_SUCCESS;

+}

+

+/**

+  Search the route table to route the packet. Return/create a route

+  cache if there is a route to the destination.

+

+  @param[in]  IpSb          The IP6 service data.

+  @param[in]  Dest          The destination address to search for.

+  @param[in]  Src           The source address to search for.

+

+  @return NULL if it failed to route the packet. Otherwise, a route cache

+          entry that can be used to route packets.

+

+**/

+IP6_ROUTE_CACHE_ENTRY *

+Ip6Route (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Dest,

+  IN EFI_IPv6_ADDRESS       *Src

+  )

+{

+  IP6_ROUTE_TABLE           *RtTable;

+  LIST_ENTRY                *ListHead;

+  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;

+  IP6_ROUTE_ENTRY           *RtEntry;

+  EFI_IPv6_ADDRESS          NextHop;

+  UINT32                    Index;

+

+  RtTable = IpSb->RouteTable;

+

+  ASSERT (RtTable != NULL);

+

+  //

+  // Search the destination cache in IP6_ROUTE_TABLE.

+  //

+  Index    = IP6_ROUTE_CACHE_HASH (Dest, Src);

+  ListHead = &RtTable->Cache.CacheBucket[Index];

+

+  RtCacheEntry = Ip6FindRouteCache (RtTable, Dest, Src);

+

+  //

+  // If found, promote the cache entry to the head of the hash bucket.

+  //

+  if (RtCacheEntry != NULL) {

+    RemoveEntryList (&RtCacheEntry->Link);

+    InsertHeadList (ListHead, &RtCacheEntry->Link);

+    return RtCacheEntry;

+  }

+

+  //

+  // Search the route table for the most specific route

+  //

+  RtEntry = Ip6FindRouteEntry (RtTable, Dest, NULL);

+  if (RtEntry == NULL) {

+    return NULL;

+  }

+

+  //

+  // Found a route to the Dest, if it is a direct route, the packet

+  // will be send directly to the destination, such as for connected

+  // network. Otherwise, it is an indirect route, the packet will be

+  // send the next hop router.

+  //

+  if ((RtEntry->Flag & IP6_DIRECT_ROUTE) == IP6_DIRECT_ROUTE) {

+    IP6_COPY_ADDRESS (&NextHop, Dest);

+  } else {

+    IP6_COPY_ADDRESS (&NextHop, &RtEntry->NextHop);

+  }

+

+  Ip6FreeRouteEntry (RtEntry);

+

+  //

+  // Create a route cache entry, and tag it as spawned from this route entry

+  //

+  RtCacheEntry = Ip6CreateRouteCacheEntry (Dest, Src, &NextHop, (UINTN) RtEntry);

+

+  if (RtCacheEntry == NULL) {

+    return NULL;

+  }

+

+  InsertHeadList (ListHead, &RtCacheEntry->Link);

+  NET_GET_REF (RtCacheEntry);

+  RtTable->Cache.CacheNum[Index]++;

+

+  return RtCacheEntry;

+}

+

diff --git a/NetworkPkg/Ip6Dxe/Ip6Route.h b/NetworkPkg/Ip6Dxe/Ip6Route.h
new file mode 100644
index 0000000..d81e07b
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Route.h
@@ -0,0 +1,299 @@
+/** @file

+  EFI IP6 route table and route cache table defintions.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_IP6_ROUTE_H__

+#define __EFI_IP6_ROUTE_H__

+

+#define IP6_DIRECT_ROUTE          0x00000001

+#define IP6_PACKET_TOO_BIG        0x00000010

+

+#define IP6_ROUTE_CACHE_HASH_SIZE 31

+///

+/// Max NO. of cache entry per hash bucket

+///

+#define IP6_ROUTE_CACHE_MAX       32

+

+#define IP6_ROUTE_CACHE_HASH(Ip1, Ip2) Ip6RouteCacheHash ((Ip1), (Ip2))

+

+typedef struct {

+  LIST_ENTRY                Link;

+  INTN                      RefCnt;

+  UINT32                    Flag;

+  UINT8                     PrefixLength;

+  EFI_IPv6_ADDRESS          Destination;

+  EFI_IPv6_ADDRESS          NextHop;

+} IP6_ROUTE_ENTRY;

+

+typedef struct {

+  LIST_ENTRY                Link;

+  INTN                      RefCnt;

+  UINTN                     Tag;

+  EFI_IPv6_ADDRESS          Destination;

+  EFI_IPv6_ADDRESS          Source;

+  EFI_IPv6_ADDRESS          NextHop;

+} IP6_ROUTE_CACHE_ENTRY;

+

+typedef struct {

+  LIST_ENTRY                CacheBucket[IP6_ROUTE_CACHE_HASH_SIZE];

+  UINT8                     CacheNum[IP6_ROUTE_CACHE_HASH_SIZE];

+} IP6_ROUTE_CACHE;

+

+//

+// Each IP6 instance has its own route table. Each ServiceBinding

+// instance has a default route table and default address.

+//

+// All the route table entries with the same prefix length are linked

+// together in one route area. For example, RouteArea[0] contains

+// the default routes. A route table also contains a route cache.

+//

+

+typedef struct _IP6_ROUTE_TABLE {

+  INTN                      RefCnt;

+  UINT32                    TotalNum;

+  LIST_ENTRY                RouteArea[IP6_PREFIX_NUM];

+  IP6_ROUTE_CACHE           Cache;

+} IP6_ROUTE_TABLE;

+

+/**

+  This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value

+  as the index of the route cache bucket according to the prefix of two IPv6 addresses.

+

+  @param[in]  Ip1     The IPv6 address.

+  @param[in]  Ip2     The IPv6 address.

+

+  @return The hash value of the prefix of two IPv6 addresses.

+

+**/

+UINT32

+Ip6RouteCacheHash (

+  IN EFI_IPv6_ADDRESS       *Ip1,

+  IN EFI_IPv6_ADDRESS       *Ip2

+  );

+

+/**

+  Allocate and initialize an IP6 route cache entry.

+

+  @param[in]  Dst           The destination address.

+  @param[in]  Src           The source address.

+  @param[in]  GateWay       The next hop address.

+  @param[in]  Tag           The tag from the caller. This marks all the cache entries

+                            spawned from one route table entry.

+

+  @return NULL if it failed to allocate memory for the cache. Otherwise, point

+          to the created route cache entry.

+

+**/

+IP6_ROUTE_CACHE_ENTRY *

+Ip6CreateRouteCacheEntry (

+  IN EFI_IPv6_ADDRESS       *Dst,

+  IN EFI_IPv6_ADDRESS       *Src,

+  IN EFI_IPv6_ADDRESS       *GateWay,

+  IN UINTN                  Tag

+  );

+

+/**

+  Free the route cache entry. It is reference counted.

+

+  @param[in, out]  RtCacheEntry  The route cache entry to free.

+

+**/

+VOID

+Ip6FreeRouteCacheEntry (

+  IN OUT IP6_ROUTE_CACHE_ENTRY  *RtCacheEntry

+  );

+

+/**

+  Find a route cache with the destination and source address. This is

+  used by the ICMPv6 redirect messasge process.

+

+  @param[in]  RtTable       The route table to search the cache for.

+  @param[in]  Dest          The destination address.

+  @param[in]  Src           The source address.

+

+  @return NULL if no route entry to the (Dest, Src). Otherwise, point

+          to the correct route cache entry.

+

+**/

+IP6_ROUTE_CACHE_ENTRY *

+Ip6FindRouteCache (

+  IN IP6_ROUTE_TABLE        *RtTable,

+  IN EFI_IPv6_ADDRESS       *Dest,

+  IN EFI_IPv6_ADDRESS       *Src

+  );

+

+/**

+  Build a array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number

+  of EFI_IP6_ROUTE_TABLE is also returned.

+

+  @param[in]  RouteTable        The pointer of IP6_ROUTE_TABLE internal used.

+  @param[out] EfiRouteCount     The number of returned route entries.

+  @param[out] EfiRouteTable     The pointer to the array of EFI_IP6_ROUTE_TABLE.

+                                If NULL, only the route entry count is returned.

+

+  @retval EFI_SUCCESS           The EFI_IP6_ROUTE_TABLE successfully built.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.

+

+**/

+EFI_STATUS

+Ip6BuildEfiRouteTable (

+  IN IP6_ROUTE_TABLE        *RouteTable,

+  OUT UINT32                *EfiRouteCount,

+  OUT EFI_IP6_ROUTE_TABLE   **EfiRouteTable OPTIONAL

+  );

+

+/**

+  Create an empty route table, includes its internal route cache.

+

+  @return NULL if failed to allocate memory for the route table. Otherwise,

+          the point to newly created route table.

+

+**/

+IP6_ROUTE_TABLE *

+Ip6CreateRouteTable (

+  VOID

+  );

+

+/**

+  Free the route table and its associated route cache. Route

+  table is reference counted.

+

+  @param[in, out]  RtTable      The route table to free.

+

+**/

+VOID

+Ip6CleanRouteTable (

+  IN OUT IP6_ROUTE_TABLE        *RtTable

+  );

+

+/**

+  Allocate a route entry then initialize it with the Destination/PrefixLength

+  and Gateway.

+

+  @param[in]  Destination     The IPv6 destination address. This is an optional

+                              parameter that may be NULL.

+  @param[in]  PrefixLength    The destination network's prefix length.

+  @param[in]  GatewayAddress  The next hop address. This is optional parameter

+                              that may be NULL.

+

+  @return NULL if it failed to allocate memeory. Otherwise, the newly created route entry.

+

+**/

+IP6_ROUTE_ENTRY *

+Ip6CreateRouteEntry (

+  IN EFI_IPv6_ADDRESS       *Destination    OPTIONAL,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *GatewayAddress OPTIONAL

+  );

+

+/**

+  Search the route table for a most specific match to the Dst. It searches

+  from the longest route area (prefix length == 128) to the shortest route area

+  (default routes). In each route area, it will first search the instance's

+  route table, then the default route table. This is required per the following

+  requirements:

+  1. IP search the route table for a most specific match.

+  2. The local route entries have precedence over the default route entry.

+

+  @param[in]  RtTable       The route table to search from.

+  @param[in]  Destination   The destionation address to search. If NULL, search

+                            the route table by NextHop.

+  @param[in]  NextHop       The next hop address. If NULL, search the route table

+                            by Destination.

+

+  @return NULL if no route matches the Dst. Otherwise the point to the

+          most specific route to the Dst.

+

+**/

+IP6_ROUTE_ENTRY *

+Ip6FindRouteEntry (

+  IN IP6_ROUTE_TABLE        *RtTable,

+  IN EFI_IPv6_ADDRESS       *Destination OPTIONAL,

+  IN EFI_IPv6_ADDRESS       *NextHop     OPTIONAL

+  );

+

+/**

+  Free the route table entry. It is reference counted.

+

+  @param[in, out]  RtEntry  The route entry to free.

+

+**/

+VOID

+Ip6FreeRouteEntry (

+  IN OUT IP6_ROUTE_ENTRY    *RtEntry

+  );

+

+/**

+  Add a route entry to the route table. It is the help function for EfiIp6Routes.

+

+  @param[in, out]  RtTable        Route table to add route to.

+  @param[in]       Destination    The destination of the network.

+  @param[in]       PrefixLength   The PrefixLength of the destination.

+  @param[in]       GatewayAddress The next hop address.

+

+  @retval EFI_ACCESS_DENIED     The same route already exists.

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the entry.

+  @retval EFI_SUCCESS           The route was added successfully.

+

+**/

+EFI_STATUS

+Ip6AddRoute (

+  IN OUT IP6_ROUTE_TABLE    *RtTable,

+  IN EFI_IPv6_ADDRESS       *Destination,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *GatewayAddress

+  );

+

+/**

+  Remove a route entry and all the route caches spawn from it.

+  It is the help function for EfiIp6Routes.

+

+  @param[in, out] RtTable           The route table to remove the route from.

+  @param[in]      Destination       The destination network.

+  @param[in]      PrefixLength      The PrefixLength of the Destination.

+  @param[in]      GatewayAddress    The next hop address.

+

+  @retval EFI_SUCCESS           Successfully removed the route entry.

+  @retval EFI_NOT_FOUND         There is no route entry in the table with that

+                                properity.

+

+**/

+EFI_STATUS

+Ip6DelRoute (

+  IN OUT IP6_ROUTE_TABLE    *RtTable,

+  IN EFI_IPv6_ADDRESS       *Destination,

+  IN UINT8                  PrefixLength,

+  IN EFI_IPv6_ADDRESS       *GatewayAddress

+  );

+

+/**

+  Search the route table to route the packet. Return/create a route

+  cache if there is a route to the destination.

+

+  @param[in]  IpSb          The IP6 service data.

+  @param[in]  Dest          The destination address to search for.

+  @param[in]  Src           The source address to search for.

+

+  @return NULL if failed to route packet. Otherwise, a route cache

+          entry that can be used to route packet.

+

+**/

+IP6_ROUTE_CACHE_ENTRY *

+Ip6Route (

+  IN IP6_SERVICE            *IpSb,

+  IN EFI_IPv6_ADDRESS       *Dest,

+  IN EFI_IPv6_ADDRESS       *Src

+  );

+

+#endif

diff --git a/NetworkPkg/IpSecDxe/ComponentName.c b/NetworkPkg/IpSecDxe/ComponentName.c
new file mode 100644
index 0000000..22b3861
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/ComponentName.c
@@ -0,0 +1,310 @@
+/** @file

+  UEFI Component Name(2) protocol implementation for IPsec driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecImpl.h"

+

+//

+// EFI Component Name Functions

+//

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  EFI_HANDLE                   ChildHandle,    OPTIONAL

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **ControllerName

+  );

+

+//

+// EFI Component Name Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL    gIpSecComponentName = {

+  IpSecComponentNameGetDriverName,

+  IpSecComponentNameGetControllerName,

+  "eng"

+};

+

+//

+// EFI Component Name 2 Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL     gIpSecComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IpSecComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IpSecComponentNameGetControllerName,

+  "en"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIpSecDriverNameTable[] = {

+  {

+    "eng;en",

+    L"IpSec Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This, and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+           Language,

+           This->SupportedLanguages,

+           mIpSecDriverNameTable,

+           DriverName,

+           (BOOLEAN) (This == &gIpSecComponentName)

+           );

+}

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  EFI_HANDLE                   ChildHandle,        OPTIONAL

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

diff --git a/NetworkPkg/IpSecDxe/IpSecConfigImpl.c b/NetworkPkg/IpSecDxe/IpSecConfigImpl.c
new file mode 100644
index 0000000..e671e42
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecConfigImpl.c
@@ -0,0 +1,2928 @@
+/** @file

+  The implementation of IPSEC_CONFIG_PROTOCOL.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecConfigImpl.h"

+#include "IpSecDebug.h"

+

+LIST_ENTRY                mConfigData[IPsecConfigDataTypeMaximum];

+BOOLEAN                   mSetBySelf = FALSE;

+

+//

+// Common CompareSelector routine entry for spd/sad/pad.

+//

+IPSEC_COMPARE_SELECTOR    mCompareSelector[] = {

+  (IPSEC_COMPARE_SELECTOR) CompareSpdSelector,

+  (IPSEC_COMPARE_SELECTOR) CompareSaId,

+  (IPSEC_COMPARE_SELECTOR) ComparePadId

+};

+

+//

+// Common IsZeroSelector routine entry for spd/sad/pad.

+//

+IPSEC_IS_ZERO_SELECTOR    mIsZeroSelector[] = {

+  (IPSEC_IS_ZERO_SELECTOR) IsZeroSpdSelector,

+  (IPSEC_IS_ZERO_SELECTOR) IsZeroSaId,

+  (IPSEC_IS_ZERO_SELECTOR) IsZeroPadId

+};

+

+//

+// Common DuplicateSelector routine entry for spd/sad/pad.

+//

+IPSEC_DUPLICATE_SELECTOR  mDuplicateSelector[] = {

+  (IPSEC_DUPLICATE_SELECTOR) DuplicateSpdSelector,

+  (IPSEC_DUPLICATE_SELECTOR) DuplicateSaId,

+  (IPSEC_DUPLICATE_SELECTOR) DuplicatePadId

+};

+

+//

+// Common FixPolicyEntry routine entry for spd/sad/pad.

+//

+IPSEC_FIX_POLICY_ENTRY    mFixPolicyEntry[] = {

+  (IPSEC_FIX_POLICY_ENTRY) FixSpdEntry,

+  (IPSEC_FIX_POLICY_ENTRY) FixSadEntry,

+  (IPSEC_FIX_POLICY_ENTRY) FixPadEntry

+};

+

+//

+// Common UnfixPolicyEntry routine entry for spd/sad/pad.

+//

+IPSEC_FIX_POLICY_ENTRY    mUnfixPolicyEntry[] = {

+  (IPSEC_FIX_POLICY_ENTRY) UnfixSpdEntry,

+  (IPSEC_FIX_POLICY_ENTRY) UnfixSadEntry,

+  (IPSEC_FIX_POLICY_ENTRY) UnfixPadEntry

+};

+

+//

+// Common SetPolicyEntry routine entry for spd/sad/pad.

+//

+IPSEC_SET_POLICY_ENTRY    mSetPolicyEntry[] = {

+  (IPSEC_SET_POLICY_ENTRY) SetSpdEntry,

+  (IPSEC_SET_POLICY_ENTRY) SetSadEntry,

+  (IPSEC_SET_POLICY_ENTRY) SetPadEntry

+};

+

+//

+// Common GetPolicyEntry routine entry for spd/sad/pad.

+//

+IPSEC_GET_POLICY_ENTRY    mGetPolicyEntry[] = {

+  (IPSEC_GET_POLICY_ENTRY) GetSpdEntry,

+  (IPSEC_GET_POLICY_ENTRY) GetSadEntry,

+  (IPSEC_GET_POLICY_ENTRY) GetPadEntry

+};

+

+//

+// Routine entry for IpSecConfig protocol.

+//

+EFI_IPSEC_CONFIG_PROTOCOL mIpSecConfigInstance = {

+  EfiIpSecConfigSetData,

+  EfiIpSecConfigGetData,

+  EfiIpSecConfigGetNextSelector,

+  EfiIpSecConfigRegisterNotify,

+  EfiIpSecConfigUnregisterNotify

+};

+

+/**

+  Get the all IPSec configuration variables and store those variables

+  to the internal data structure.

+

+  This founction is called by IpSecConfigInitialize() that is to intialize the

+  IPsecConfiguration Protocol.

+

+  @param[in]  Private            Point to IPSEC_PRIVATE_DATA.

+

+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.

+  @retval EFI_SUCCESS            Restore the IPsec Configuration successfully.

+  @retval  others                Other errors is found during the variable getting.

+

+**/

+EFI_STATUS

+IpSecConfigRestore (

+  IN IPSEC_PRIVATE_DATA               *Private

+  );

+

+/**

+  Check if the specified EFI_IP_ADDRESS_INFO is in EFI_IP_ADDRESS_INFO list.

+

+  @param[in]   AddressInfo         Pointer of IP_ADDRESS_INFO to be search in AddressInfo list.

+  @param[in]   AddressInfoList     A list that contains IP_ADDRESS_INFOs.

+  @param[in]   AddressCount        Point out how many IP_ADDRESS_INFO in the list.

+

+  @retval  TRUE    The specified AddressInfo is in the AddressInfoList.

+  @retval  FALSE   The specified AddressInfo is not in the AddressInfoList.

+

+**/

+BOOLEAN

+IsInAddressInfoList(

+  IN EFI_IP_ADDRESS_INFO              *AddressInfo,

+  IN EFI_IP_ADDRESS_INFO              *AddressInfoList,

+  IN UINT32                           AddressCount

+  )

+{

+  UINT8  Index;

+

+  for (Index = 0; Index < AddressCount ; Index++) {

+    if (CompareMem (

+          AddressInfo,

+          &AddressInfoList[Index].Address,

+          sizeof (EFI_IP_ADDRESS)

+          ) == 0 &&

+          AddressInfo->PrefixLength == AddressInfoList[Index].PrefixLength

+          ) {

+       return TRUE;

+     }

+  }

+  return FALSE;

+}

+

+/**

+  Compare two SPD Selectors.

+

+  Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/

+  NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the

+  Local Addresses and remote Addresses.

+

+  @param[in]   Selector1           Pointer of first SPD Selector.

+  @param[in]   Selector2           Pointer of second SPD Selector.

+

+  @retval  TRUE    This two Selector have the same value in above fields.

+  @retval  FALSE   Not all above fields have the same value in these two Selectors.

+

+**/

+BOOLEAN

+CompareSpdSelector (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2

+  )

+{

+  EFI_IPSEC_SPD_SELECTOR  *SpdSel1;

+  EFI_IPSEC_SPD_SELECTOR  *SpdSel2;

+  BOOLEAN                 IsMatch;

+  UINTN                   Index;

+

+  SpdSel1 = &Selector1->SpdSelector;

+  SpdSel2 = &Selector2->SpdSelector;

+  IsMatch = TRUE;

+

+  //

+  // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/

+  // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the

+  // two Spdselectors. Since the SPD supports two directions, it needs to

+  // compare two directions.

+  //

+  if ((SpdSel1->LocalAddressCount != SpdSel2->LocalAddressCount &&

+       SpdSel1->LocalAddressCount != SpdSel2->RemoteAddressCount) ||

+      (SpdSel1->RemoteAddressCount != SpdSel2->RemoteAddressCount &&

+       SpdSel1->RemoteAddressCount != SpdSel2->LocalAddressCount) ||

+       SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol ||

+       SpdSel1->LocalPort != SpdSel2->LocalPort ||

+       SpdSel1->LocalPortRange != SpdSel2->LocalPortRange ||

+       SpdSel1->RemotePort != SpdSel2->RemotePort ||

+       SpdSel1->RemotePortRange != SpdSel2->RemotePortRange

+       ) {

+    IsMatch = FALSE;

+    return IsMatch;

+  }

+

+  //

+  // Compare the all LocalAddress fields in the two Spdselectors.

+  // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare

+  // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return

+  // TRUE.

+  //

+  for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {

+    if (!IsInAddressInfoList (

+          &SpdSel1->LocalAddress[Index],

+          SpdSel2->LocalAddress,

+          SpdSel2->LocalAddressCount

+          )) {

+      IsMatch = FALSE;

+      break;

+    }

+  }

+  if (IsMatch) {

+    for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {

+      if (!IsInAddressInfoList (

+            &SpdSel2->LocalAddress[Index],

+            SpdSel1->LocalAddress,

+            SpdSel1->LocalAddressCount

+            )) {

+        IsMatch = FALSE;

+        break;

+      }

+    }

+  }

+  if (IsMatch) {

+    for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {

+      if (!IsInAddressInfoList (

+            &SpdSel1->RemoteAddress[Index],

+            SpdSel2->RemoteAddress,

+            SpdSel2->RemoteAddressCount

+            )) {

+        IsMatch = FALSE;

+        break;

+      }

+    }

+  }

+  if (IsMatch) {

+    for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {

+      if (!IsInAddressInfoList (

+            &SpdSel2->RemoteAddress[Index],

+            SpdSel1->RemoteAddress,

+            SpdSel1->RemoteAddressCount

+            )) {

+        IsMatch = FALSE;

+        break;

+      }

+    }

+  }

+  //

+  // Finish the one direction compare. If it is matched, return; otherwise,

+  // compare the other direction.

+  //

+  if (IsMatch) {

+    return IsMatch;

+  }

+  //

+  // Secondly, the SpdSel1->LocalAddress doesn't equal to  SpdSel2->LocalAddress and

+  // SpdSel1->RemoteAddress doesn't equal to SpdSel2->RemoteAddress. Try to compare

+  // the RemoteAddress to LocalAddress.

+  //

+  IsMatch = TRUE;

+  for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {

+    if (!IsInAddressInfoList (

+          &SpdSel1->RemoteAddress[Index],

+          SpdSel2->LocalAddress,

+          SpdSel2->LocalAddressCount

+          )) {

+      IsMatch = FALSE;

+      break;

+    }

+  }

+  if (IsMatch) {

+    for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {

+      if (!IsInAddressInfoList (

+            &SpdSel2->RemoteAddress[Index],

+            SpdSel1->LocalAddress,

+            SpdSel1->LocalAddressCount

+            )) {

+        IsMatch = FALSE;

+        break;

+      }

+    }

+  }

+  if (IsMatch) {

+    for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {

+      if (!IsInAddressInfoList (

+            &SpdSel1->LocalAddress[Index],

+            SpdSel2->RemoteAddress,

+            SpdSel2->RemoteAddressCount

+            )) {

+        IsMatch = FALSE;

+        break;

+      }

+    }

+  }

+  if (IsMatch) {

+    for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {

+      if (!IsInAddressInfoList (

+            &SpdSel2->LocalAddress[Index],

+            SpdSel1->RemoteAddress,

+            SpdSel1->RemoteAddressCount

+            )) {

+        IsMatch = FALSE;

+        break;

+      }

+    }

+  }

+  return IsMatch;

+}

+

+/**

+  Compare two SA IDs.

+

+  @param[in]   Selector1           Pointer of first SA ID.

+  @param[in]   Selector2           Pointer of second SA ID.

+

+  @retval  TRUE    This two Selectors have the same SA ID.

+  @retval  FALSE   This two Selecotrs don't have the same SA ID.

+

+**/

+BOOLEAN

+CompareSaId (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2

+  )

+{

+  EFI_IPSEC_SA_ID *SaId1;

+  EFI_IPSEC_SA_ID *SaId2;

+  BOOLEAN         IsMatch;

+

+  SaId1   = &Selector1->SaId;

+  SaId2   = &Selector2->SaId;

+  IsMatch = TRUE;

+

+  if (CompareMem (SaId1, SaId2, sizeof (EFI_IPSEC_SA_ID)) != 0) {

+    IsMatch = FALSE;

+  }

+

+  return IsMatch;

+}

+

+/**

+  Compare two PAD IDs.

+

+  @param[in]   Selector1           Pointer of first PAD ID.

+  @param[in]   Selector2           Pointer of second PAD ID.

+

+  @retval  TRUE    This two Selectors have the same PAD ID.

+  @retval  FALSE   This two Selecotrs don't have the same PAD ID.

+

+**/

+BOOLEAN

+ComparePadId (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2

+  )

+{

+  EFI_IPSEC_PAD_ID  *PadId1;

+  EFI_IPSEC_PAD_ID  *PadId2;

+  BOOLEAN           IsMatch;

+

+  PadId1  = &Selector1->PadId;

+  PadId2  = &Selector2->PadId;

+  IsMatch = TRUE;

+

+  //

+  // Compare the PeerIdValid fields in PadId.

+  //

+  if (PadId1->PeerIdValid != PadId2->PeerIdValid) {

+    IsMatch = FALSE;

+  }

+  //

+  // Compare the PeerId fields in PadId if PeerIdValid is true.

+  //

+  if (IsMatch &&

+      PadId1->PeerIdValid &&

+      AsciiStriCmp ((CONST CHAR8 *) PadId1->Id.PeerId, (CONST CHAR8 *) PadId2->Id.PeerId) != 0

+      ) {

+    IsMatch = FALSE;

+  }

+  //

+  // Compare the IpAddress fields in PadId if PeerIdValid is false.

+  //

+  if (IsMatch &&

+      !PadId1->PeerIdValid &&

+      (PadId1->Id.IpAddress.PrefixLength != PadId2->Id.IpAddress.PrefixLength ||

+       CompareMem (&PadId1->Id.IpAddress.Address, &PadId2->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0)

+      ) {

+    IsMatch = FALSE;

+  }

+

+  return IsMatch;

+}

+

+/**

+  Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount

+  fields.

+

+  @param[in]  Selector      Pointer of the SPD Selector.

+

+  @retval     TRUE          If the SPD Selector is Zero.

+  @retval     FALSE         If the SPD Selector is not Zero.

+

+**/

+BOOLEAN

+IsZeroSpdSelector (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector

+  )

+{

+  EFI_IPSEC_SPD_SELECTOR  *SpdSel;

+  BOOLEAN                 IsZero;

+

+  SpdSel  = &Selector->SpdSelector;

+  IsZero  = FALSE;

+

+  if (SpdSel->LocalAddressCount == 0 && SpdSel->RemoteAddressCount == 0) {

+    IsZero = TRUE;

+  }

+

+  return IsZero;

+}

+

+/**

+  Check if the SA ID is Zero by its DestAddress.

+

+  @param[in]  Selector      Pointer of the SA ID.

+

+  @retval     TRUE          If the SA ID is Zero.

+  @retval     FALSE         If the SA ID is not Zero.

+

+**/

+BOOLEAN

+IsZeroSaId (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector

+  )

+{

+  EFI_IP_ADDRESS  *DestAddr;

+  EFI_IP_ADDRESS  ZeroAddr;

+  BOOLEAN         IsZero;

+

+  DestAddr  = &Selector->SaId.DestAddress;

+  IsZero    = FALSE;

+

+  ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));

+

+  if (CompareMem (DestAddr, &ZeroAddr, sizeof (EFI_IP_ADDRESS)) == 0) {

+    IsZero = TRUE;

+  }

+

+  return IsZero;

+}

+

+/**

+  Check if the PAD ID is Zero.

+

+  @param[in]  Selector      Pointer of the PAD ID.

+

+  @retval     TRUE          If the PAD ID is Zero.

+  @retval     FALSE         If the PAD ID is not Zero.

+

+**/

+BOOLEAN

+IsZeroPadId (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector

+  )

+{

+  EFI_IPSEC_PAD_ID  *PadId;

+  EFI_IPSEC_PAD_ID  ZeroId;

+  BOOLEAN           IsZero;

+

+  PadId   = &Selector->PadId;

+  IsZero  = FALSE;

+

+  ZeroMem (&ZeroId, sizeof (EFI_IPSEC_PAD_ID));

+

+  if (CompareMem (PadId, &ZeroId, sizeof (EFI_IPSEC_PAD_ID)) == 0) {

+    IsZero = TRUE;

+  }

+

+  return IsZero;

+}

+

+/**

+  Copy Source SPD Selector to the Destination SPD Selector.

+

+  @param[in, out] DstSel             Pointer of Destination SPD Selector.

+  @param[in]      SrcSel             Pointer of Source SPD Selector.

+  @param[in, out] Size               The size of the Destination SPD Selector. If it

+                                     not NULL and its value less than the size of

+                                     Source SPD Selector, the value of Source SPD

+                                     Selector's size will be passed to caller by this

+                                     parameter.

+

+  @retval EFI_INVALID_PARAMETER  If the Destination or Source SPD Selector is NULL

+  @retval EFI_BUFFER_TOO_SMALL   If the input Size is less than size of the Source SPD Selector.

+  @retval EFI_SUCCESS            Copy Source SPD Selector to the Destination SPD

+                                 Selector successfully.

+

+**/

+EFI_STATUS

+DuplicateSpdSelector (

+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,

+  IN OUT UINTN                        *Size

+  )

+{

+  EFI_IPSEC_SPD_SELECTOR  *Dst;

+  EFI_IPSEC_SPD_SELECTOR  *Src;

+

+  Dst = &DstSel->SpdSelector;

+  Src = &SrcSel->SpdSelector;

+

+  if (Dst == NULL || Src == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Size != NULL && (*Size) < SIZE_OF_SPD_SELECTOR (Src)) {

+    *Size = SIZE_OF_SPD_SELECTOR (Src);

+    return EFI_BUFFER_TOO_SMALL;

+  }

+  //

+  // Copy the base structure of spd selector.

+  //

+  CopyMem (Dst, Src, sizeof (EFI_IPSEC_SPD_SELECTOR));

+

+  //

+  // Copy the local address array of spd selector.

+  //

+  Dst->LocalAddress = (EFI_IP_ADDRESS_INFO *) (Dst + 1);

+  CopyMem (

+    Dst->LocalAddress,

+    Src->LocalAddress,

+    sizeof (EFI_IP_ADDRESS_INFO) * Dst->LocalAddressCount

+    );

+

+  //

+  // Copy the remote address array of spd selector.

+  //

+  Dst->RemoteAddress = Dst->LocalAddress + Dst->LocalAddressCount;

+  CopyMem (

+    Dst->RemoteAddress,

+    Src->RemoteAddress,

+    sizeof (EFI_IP_ADDRESS_INFO) * Dst->RemoteAddressCount

+    );

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Copy Source SA ID to the Destination SA ID.

+

+  @param[in, out] DstSel             Pointer of Destination SA ID.

+  @param[in]      SrcSel             Pointer of Source SA ID.

+  @param[in, out] Size               The size of the Destination SA ID. If it

+                                     not NULL and its value less than the size of

+                                     Source SA ID, the value of Source SA ID's size

+                                     will be passed to caller by this parameter.

+

+  @retval EFI_INVALID_PARAMETER  If the Destination or Source SA ID is NULL.

+  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source SA ID.

+  @retval EFI_SUCCESS            Copy Source SA ID  to the Destination SA ID successfully.

+

+**/

+EFI_STATUS

+DuplicateSaId (

+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,

+  IN OUT UINTN                        *Size

+  )

+{

+  EFI_IPSEC_SA_ID *Dst;

+  EFI_IPSEC_SA_ID *Src;

+

+  Dst = &DstSel->SaId;

+  Src = &SrcSel->SaId;

+

+  if (Dst == NULL || Src == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Size != NULL && *Size < sizeof (EFI_IPSEC_SA_ID)) {

+    *Size = sizeof (EFI_IPSEC_SA_ID);

+    return EFI_BUFFER_TOO_SMALL;

+  }

+

+  CopyMem (Dst, Src, sizeof (EFI_IPSEC_SA_ID));

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Copy Source PAD ID to the Destination PAD ID.

+

+  @param[in, out] DstSel             Pointer of Destination PAD ID.

+  @param[in]      SrcSel             Pointer of Source PAD ID.

+  @param[in, out] Size               The size of the Destination PAD ID. If it

+                                     not NULL and its value less than the size of

+                                     Source PAD ID, the value of Source PAD ID's size

+                                     will be passed to caller by this parameter.

+

+  @retval EFI_INVALID_PARAMETER  If the Destination or Source PAD ID is NULL.

+  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source PAD ID .

+  @retval EFI_SUCCESS            Copy Source PAD ID  to the Destination PAD ID successfully.

+

+**/

+EFI_STATUS

+DuplicatePadId (

+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,

+  IN OUT UINTN                        *Size

+  )

+{

+  EFI_IPSEC_PAD_ID  *Dst;

+  EFI_IPSEC_PAD_ID  *Src;

+

+  Dst = &DstSel->PadId;

+  Src = &SrcSel->PadId;

+

+  if (Dst == NULL || Src == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Size != NULL && *Size < sizeof (EFI_IPSEC_PAD_ID)) {

+    *Size = sizeof (EFI_IPSEC_PAD_ID);

+    return EFI_BUFFER_TOO_SMALL;

+  }

+

+  CopyMem (Dst, Src, sizeof (EFI_IPSEC_PAD_ID));

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Fix the value of some members of SPD Selector.

+

+  This function is called by IpSecCopyPolicyEntry()which copy the Policy

+  Entry into the Variable. Since some members in SPD Selector are pointers,

+  a physical address to relative address convertion is required before copying

+  this SPD entry into the variable.

+

+  @param[in]       Selector              Pointer of SPD Selector.

+  @param[in, out]  Data                  Pointer of SPD Data.

+

+**/

+VOID

+FixSpdEntry (

+  IN     EFI_IPSEC_SPD_SELECTOR            *Selector,

+  IN OUT EFI_IPSEC_SPD_DATA                *Data

+  )

+{

+  //

+  // It assumes that all ref buffers in spd selector and data are

+  // stored in the continous memory and close to the base structure.

+  //

+  FIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);

+  FIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);

+

+  if (Data->ProcessingPolicy != NULL) {

+    if (Data->ProcessingPolicy->TunnelOption != NULL) {

+      FIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);

+    }

+

+    FIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);

+  }

+

+}

+

+/**

+  Fix the value of some members of SA ID.

+

+  This function is called by IpSecCopyPolicyEntry()which copy the Policy

+  Entry into the Variable. Since some members in SA ID are pointers,

+  a physical address to relative address conversion is required before copying

+  this SAD into the variable.

+

+  @param[in]       SaId                  Pointer of SA ID

+  @param[in, out]  Data                  Pointer of SA Data.

+

+**/

+VOID

+FixSadEntry (

+  IN     EFI_IPSEC_SA_ID                  *SaId,

+  IN OUT EFI_IPSEC_SA_DATA                *Data

+  )

+{

+  //

+  // It assumes that all ref buffers in sad selector and data are

+  // stored in the continous memory and close to the base structure.

+  //

+  if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {

+    FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);

+  }

+

+  if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {

+    FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);

+  }

+

+  if (Data->SpdSelector != NULL) {

+    if (Data->SpdSelector->LocalAddress != NULL) {

+      FIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);

+    }

+

+    FIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);

+    FIX_REF_BUF_ADDR (Data->SpdSelector, Data);

+  }

+

+}

+

+/**

+  Fix the value of some members of PAD ID.

+

+  This function is called by IpSecCopyPolicyEntry()which copy the Policy

+  Entry into the Variable. Since some members in PAD ID are pointers,

+  a physical address to relative address conversion is required before copying

+  this PAD into the variable.

+

+  @param[in]       PadId              Pointer of PAD ID.

+  @param[in, out]  Data               Pointer of PAD Data.

+

+**/

+VOID

+FixPadEntry (

+  IN     EFI_IPSEC_PAD_ID                  *PadId,

+  IN OUT EFI_IPSEC_PAD_DATA                *Data

+  )

+{

+  //

+  // It assumes that all ref buffers in pad selector and data are

+  // stored in the continous memory and close to the base structure.

+  //

+  if (Data->AuthData != NULL) {

+    FIX_REF_BUF_ADDR (Data->AuthData, Data);

+  }

+

+  if (Data->RevocationData != NULL) {

+    FIX_REF_BUF_ADDR (Data->RevocationData, Data);

+  }

+

+}

+

+/**

+  Recover the value of some members of SPD Selector.

+

+  This function is corresponding to FixSpdEntry(). It recovers the value of members

+  of SPD Selector that are fixed by FixSpdEntry().

+

+  @param[in, out]  Selector              Pointer of SPD Selector.

+  @param[in, out]  Data                  Pointer of SPD Data.

+

+**/

+VOID

+UnfixSpdEntry (

+  IN OUT EFI_IPSEC_SPD_SELECTOR           *Selector,

+  IN OUT EFI_IPSEC_SPD_DATA               *Data

+  )

+{

+  //

+  // It assumes that all ref buffers in spd selector and data are

+  // stored in the continous memory and close to the base structure.

+  //

+  UNFIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);

+  UNFIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);

+

+  if (Data->ProcessingPolicy != NULL) {

+    UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);

+    if (Data->ProcessingPolicy->TunnelOption != NULL) {

+      UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);

+    }

+  }

+

+}

+

+/**

+  Recover the value of some members of SA ID.

+

+  This function is corresponding to FixSadEntry(). It recovers the value of members

+  of SAD ID that are fixed by FixSadEntry().

+

+  @param[in, out]  SaId              Pointer of SAD ID.

+  @param[in, out]  Data              Pointer of SAD Data.

+

+**/

+VOID

+UnfixSadEntry (

+  IN OUT EFI_IPSEC_SA_ID                     *SaId,

+  IN OUT EFI_IPSEC_SA_DATA                   *Data

+  )

+{

+  //

+  // It assumes that all ref buffers in sad selector and data are

+  // stored in the continous memory and close to the base structure.

+  //

+  if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {

+    UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);

+  }

+

+  if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {

+    UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);

+  }

+

+  if (Data->SpdSelector != NULL) {

+    UNFIX_REF_BUF_ADDR (Data->SpdSelector, Data);

+    if (Data->SpdSelector->LocalAddress != NULL) {

+      UNFIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);

+    }

+

+    UNFIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);

+  }

+

+}

+

+/**

+  Recover the value of some members of PAD ID.

+

+  This function is corresponding to FixPadEntry(). It recovers the value of members

+  of PAD ID that are fixed by FixPadEntry().

+

+  @param[in]       PadId              Pointer of PAD ID.

+  @param[in, out]  Data               Pointer of PAD Data.

+

+**/

+VOID

+UnfixPadEntry (

+  IN     EFI_IPSEC_PAD_ID                 *PadId,

+  IN OUT EFI_IPSEC_PAD_DATA               *Data

+  )

+{

+  //

+  // It assumes that all ref buffers in pad selector and data are

+  // stored in the continous memory and close to the base structure.

+  //

+  if (Data->AuthData != NULL) {

+    UNFIX_REF_BUF_ADDR (Data->AuthData, Data);

+  }

+

+  if (Data->RevocationData != NULL) {

+    UNFIX_REF_BUF_ADDR (Data->RevocationData, Data);

+  }

+

+}

+

+/**

+  Set the security policy information for the EFI IPsec driver.

+

+  The IPsec configuration data has a unique selector/identifier separately to

+  identify a data entry.

+

+  @param[in]  Selector           Pointer to an entry selector on operated

+                                 configuration data specified by DataType.

+                                 A NULL Selector causes the entire specified-type

+                                 configuration information to be flushed.

+  @param[in]  Data               The data buffer to be set. The structure

+                                 of the data buffer should be EFI_IPSEC_SPD_DATA.

+  @param[in]  Context            Pointer to one entry selector that describes

+                                 the expected position the new data entry will

+                                 be added. If Context is NULL, the new entry will

+                                 be appended the end of database.

+

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                   - Selector is not NULL and its LocalAddress

+                                     is NULL or its RemoteAddress is NULL.

+                                   - Data is not NULL and its Action is Protected

+                                     and its plolicy is NULL.

+                                   - Data is not NULL, its Action is not protected,

+                                     and its policy is not NULL.

+                                   - The Action of Data is Protected, its policy

+                                     mode is Tunnel, and its tunnel option is NULL.

+                                   - The Action of Data is protected and its policy

+                                     mode is not Tunnel and it tunnel option is not NULL.

+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+

+**/

+EFI_STATUS

+SetSpdEntry (

+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,

+  IN VOID                            *Data,

+  IN VOID                            *Context OPTIONAL

+  )

+{

+  EFI_IPSEC_SPD_SELECTOR  *SpdSel;

+  EFI_IPSEC_SPD_DATA      *SpdData;

+  EFI_IPSEC_SPD_SELECTOR  *InsertBefore;

+  LIST_ENTRY              *SpdList;

+  LIST_ENTRY              *SadList;

+  LIST_ENTRY              *SpdSas;

+  LIST_ENTRY              *EntryInsertBefore;

+  LIST_ENTRY              *Entry;

+  LIST_ENTRY              *NextEntry;

+  LIST_ENTRY              *Entry2;

+  IPSEC_SPD_ENTRY         *SpdEntry;

+  IPSEC_SAD_ENTRY         *SadEntry;

+  UINTN                   SpdEntrySize;

+  UINTN                   Index;

+

+  SpdSel        = (Selector == NULL) ? NULL : &Selector->SpdSelector;

+  SpdData       = (Data == NULL) ? NULL : (EFI_IPSEC_SPD_DATA *) Data;

+  InsertBefore  = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SpdSelector;

+  SpdList       = &mConfigData[IPsecConfigDataTypeSpd];

+

+  if (SpdSel != NULL) {

+    if (SpdSel->LocalAddress == NULL || SpdSel->RemoteAddress == NULL) {

+      return EFI_INVALID_PARAMETER;

+    }

+  }

+

+  if (SpdData != NULL) {

+    if ((SpdData->Action == EfiIPsecActionProtect && SpdData->ProcessingPolicy == NULL) ||

+        (SpdData->Action != EfiIPsecActionProtect && SpdData->ProcessingPolicy != NULL)

+        ) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (SpdData->Action == EfiIPsecActionProtect) {

+      if ((SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption == NULL) ||

+          (SpdData->ProcessingPolicy->Mode != EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption != NULL)

+          ) {

+        return EFI_INVALID_PARAMETER;

+      }

+    }

+  }

+  //

+  // The default behavior is to insert the node ahead of the header.

+  //

+  EntryInsertBefore = SpdList;

+

+  //

+  // Remove the existed spd entry.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SpdList) {

+

+    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);

+

+    if (SpdSel == NULL ||

+        CompareSpdSelector ((EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel)

+        ) {

+      //

+      // Record the existed entry position to keep the original order.

+      //

+      EntryInsertBefore = SpdEntry->List.ForwardLink;

+      RemoveEntryList (&SpdEntry->List);

+

+      //

+      // Update the reverse ref of sad entry in the spd.sas list.

+      //

+      SpdSas = &SpdEntry->Data->Sas;

+      NET_LIST_FOR_EACH (Entry2, SpdSas) {

+        SadEntry                  = IPSEC_SAD_ENTRY_FROM_SPD (Entry2);

+        SadEntry->Data->SpdEntry  = NULL;

+      }

+      //

+      // Free the existed spd entry

+      //

+      FreePool (SpdEntry);

+    }

+  }

+  //

+  // Return success here if only want to remove the spd entry.

+  //

+  if (SpdData == NULL || SpdSel == NULL) {

+    return EFI_SUCCESS;

+  }

+  //

+  // Search the appointed entry position if InsertBefore is not NULL.

+  //

+  if (InsertBefore != NULL) {

+

+    NET_LIST_FOR_EACH (Entry, SpdList) {

+      SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);

+

+      if (CompareSpdSelector (

+            (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,

+            (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore

+            )) {

+        EntryInsertBefore = Entry;

+        break;

+      }

+    }

+  }

+

+  //

+  // Do Padding for the different Arch.

+  //

+  SpdEntrySize  = ALIGN_VARIABLE (sizeof (IPSEC_SPD_ENTRY));

+  SpdEntrySize  = ALIGN_VARIABLE (SpdEntrySize + (UINTN)SIZE_OF_SPD_SELECTOR (SpdSel));

+  SpdEntrySize += IpSecGetSizeOfEfiSpdData (SpdData);

+

+  SpdEntry = AllocateZeroPool (SpdEntrySize);

+

+  if (SpdEntry == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+  //

+  // Fix the address of Selector and Data buffer and copy them, which is

+  // continous memory and close to the base structure of spd entry.

+  //

+  SpdEntry->Selector  = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER ((SpdEntry + 1), sizeof (UINTN));

+  SpdEntry->Data      = (IPSEC_SPD_DATA *) ALIGN_POINTER (

+                                            ((UINT8 *) SpdEntry->Selector + SIZE_OF_SPD_SELECTOR (SpdSel)),

+                                            sizeof (UINTN)

+                                            );

+

+  DuplicateSpdSelector (

+    (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,

+    (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,

+    NULL

+    );

+

+  CopyMem (

+    SpdEntry->Data->Name,

+    SpdData->Name,

+    sizeof (SpdData->Name)

+    );

+  SpdEntry->Data->PackageFlag = SpdData->PackageFlag;

+  SpdEntry->Data->Action      = SpdData->Action;

+

+  //

+  // Fix the address of ProcessingPolicy and copy it if need, which is continous

+  // memory and close to the base structure of sad data.

+  //

+  if (SpdData->Action != EfiIPsecActionProtect) {

+    SpdEntry->Data->ProcessingPolicy = NULL;

+  } else {

+    SpdEntry->Data->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (

+                                                                      SpdEntry->Data + 1,

+                                                                      sizeof (UINTN)

+                                                                      );

+    IpSecDuplicateProcessPolicy (SpdEntry->Data->ProcessingPolicy, SpdData->ProcessingPolicy);

+  }

+  //

+  // Update the sas list of the new spd entry.

+  //

+  InitializeListHead (&SpdEntry->Data->Sas);

+

+  SadList = &mConfigData[IPsecConfigDataTypeSad];

+

+  NET_LIST_FOR_EACH (Entry, SadList) {

+    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);

+

+    for (Index = 0; Index < SpdData->SaIdCount; Index++) {

+

+      if (CompareSaId (

+            (EFI_IPSEC_CONFIG_SELECTOR *) &SpdData->SaId[Index],

+            (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id

+            )) {

+        InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);

+        SadEntry->Data->SpdEntry = SpdEntry;

+      }

+    }

+  }

+  //

+  // Insert the new spd entry.

+  //

+  InsertTailList (EntryInsertBefore, &SpdEntry->List);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Set the security association information for the EFI IPsec driver.

+

+  The IPsec configuration data has a unique selector/identifier separately to

+  identify a data entry.

+

+  @param[in]  Selector           Pointer to an entry selector on operated

+                                 configuration data specified by DataType.

+                                 A NULL Selector causes the entire specified-type

+                                 configuration information to be flushed.

+  @param[in]  Data               The data buffer to be set. The structure

+                                 of the data buffer should be EFI_IPSEC_SA_DATA.

+  @param[in]  Context            Pointer to one entry selector which describes

+                                 the expected position the new data entry will

+                                 be added. If Context is NULL,the new entry will

+                                 be appended the end of database.

+

+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+

+**/

+EFI_STATUS

+SetSadEntry (

+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,

+  IN VOID                            *Data,

+  IN VOID                            *Context OPTIONAL

+  )

+{

+  IPSEC_SAD_ENTRY   *SadEntry;

+  IPSEC_SPD_ENTRY   *SpdEntry;

+  LIST_ENTRY        *Entry;

+  LIST_ENTRY        *NextEntry;

+  LIST_ENTRY        *SadList;

+  LIST_ENTRY        *SpdList;

+  EFI_IPSEC_SA_ID   *SaId;

+  EFI_IPSEC_SA_DATA *SaData;

+  EFI_IPSEC_SA_ID   *InsertBefore;

+  LIST_ENTRY        *EntryInsertBefore;

+  UINTN             SadEntrySize;

+

+  SaId          = (Selector == NULL) ? NULL : &Selector->SaId;

+  SaData        = (Data == NULL) ? NULL : (EFI_IPSEC_SA_DATA *) Data;

+  InsertBefore  = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SaId;

+  SadList       = &mConfigData[IPsecConfigDataTypeSad];

+

+  //

+  // The default behavior is to insert the node ahead of the header.

+  //

+  EntryInsertBefore = SadList;

+

+  //

+  // Remove the existed sad entry.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SadList) {

+

+    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);

+

+    if (SaId == NULL ||

+        CompareSaId (

+          (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,

+          (EFI_IPSEC_CONFIG_SELECTOR *) SaId

+          )) {

+      //

+      // Record the existed entry position to keep the original order.

+      //

+      EntryInsertBefore = SadEntry->List.ForwardLink;

+

+      //

+      // Update the related sad.byspd field.

+      //

+      if (SadEntry->Data->SpdEntry != NULL) {

+        RemoveEntryList (&SadEntry->BySpd);

+      }

+

+      RemoveEntryList (&SadEntry->List);

+      FreePool (SadEntry);

+    }

+  }

+  //

+  // Return success here if only want to remove the sad entry

+  //

+  if (SaData == NULL || SaId == NULL) {

+    return EFI_SUCCESS;

+  }

+  //

+  // Search the appointed entry position if InsertBefore is not NULL.

+  //

+  if (InsertBefore != NULL) {

+

+    NET_LIST_FOR_EACH (Entry, SadList) {

+      SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);

+

+      if (CompareSaId (

+           (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,

+           (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore

+           )) {

+        EntryInsertBefore = Entry;

+        break;

+      }

+    }

+  }

+

+  //

+  // Do Padding for different Arch.

+  //

+  SadEntrySize  = ALIGN_VARIABLE (sizeof (IPSEC_SAD_ENTRY));

+  SadEntrySize  = ALIGN_VARIABLE (SadEntrySize + sizeof (EFI_IPSEC_SA_DATA));

+  SadEntrySize  = ALIGN_VARIABLE (SadEntrySize + sizeof (IPSEC_SAD_DATA));

+

+  if (SaId->Proto == EfiIPsecAH) {

+    SadEntrySize += SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength;

+  } else {

+    SadEntrySize  = ALIGN_VARIABLE (SadEntrySize + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength);

+    SadEntrySize += SaData->AlgoInfo.EspAlgoInfo.EncKeyLength;

+  }

+

+  SadEntry      = AllocateZeroPool (SadEntrySize);

+

+  if (SadEntry == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+  //

+  // Fix the address of Id and Data buffer and copy them, which is

+  // continous memory and close to the base structure of sad entry.

+  //

+  SadEntry->Id    = (EFI_IPSEC_SA_ID *) ALIGN_POINTER ((SadEntry + 1), sizeof (UINTN));

+  SadEntry->Data  = (IPSEC_SAD_DATA *) ALIGN_POINTER ((SadEntry->Id + 1), sizeof (UINTN));

+

+  CopyMem (SadEntry->Id, SaId, sizeof (EFI_IPSEC_SA_ID));

+

+  SadEntry->Data->Mode                  = SaData->Mode;

+  SadEntry->Data->SequenceNumber        = SaData->SNCount;

+  SadEntry->Data->AntiReplayWindowSize  = SaData->AntiReplayWindows;

+

+  ZeroMem (

+    &SadEntry->Data->AntiReplayBitmap,

+    sizeof (SadEntry->Data->AntiReplayBitmap)

+    );

+

+  ZeroMem (

+    &SadEntry->Data->AlgoInfo,

+    sizeof (EFI_IPSEC_ALGO_INFO)

+    );

+

+  SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId     = SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId;

+  SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength  = SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength;

+

+  if (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {

+    SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SadEntry->Data + 1), sizeof (UINTN));

+    CopyMem (

+      SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,

+      SaData->AlgoInfo.EspAlgoInfo.AuthKey,

+      SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength

+      );

+  }

+

+  if (SaId->Proto == EfiIPsecESP) {

+    SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId    = SaData->AlgoInfo.EspAlgoInfo.EncAlgoId;

+    SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength = SaData->AlgoInfo.EspAlgoInfo.EncKeyLength;

+

+    if (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {

+      SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (

+                                                               ((UINT8 *) (SadEntry->Data + 1) +

+                                                                 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength),

+                                                                 sizeof (UINTN)

+                                                                 );

+      CopyMem (

+        SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,

+        SaData->AlgoInfo.EspAlgoInfo.EncKey,

+        SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength

+        );

+    }

+  }

+

+  CopyMem (

+    &SadEntry->Data->SaLifetime,

+    &SaData->SaLifetime,

+    sizeof (EFI_IPSEC_SA_LIFETIME)

+    );

+

+  SadEntry->Data->PathMTU     = SaData->PathMTU;

+  SadEntry->Data->SpdEntry    = NULL;

+  SadEntry->Data->ESNEnabled  = FALSE;

+  SadEntry->Data->ManualSet   = SaData->ManualSet;

+

+  //

+  // Update the spd.sas list of the spd entry specified by sad.selector

+  //

+  SpdList = &mConfigData[IPsecConfigDataTypeSpd];

+

+  for (Entry = SpdList->ForwardLink; Entry != SpdList && SaData->SpdSelector != NULL; Entry = Entry->ForwardLink) {

+

+    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);

+    if (CompareSpdSelector (

+          (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,

+          (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector

+          ) && SpdEntry->Data->Action == EfiIPsecActionProtect) {

+      SadEntry->Data->SpdEntry = SpdEntry;

+      InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);

+    }

+  }

+  //

+  // Insert the new sad entry.

+  //

+  InsertTailList (EntryInsertBefore, &SadEntry->List);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Set the peer authorization configuration information for the EFI IPsec driver.

+

+  The IPsec configuration data has a unique selector/identifier separately to

+  identify a data entry.

+

+  @param[in]  Selector           Pointer to an entry selector on operated

+                                 configuration data specified by DataType.

+                                 A NULL Selector causes the entire specified-type

+                                 configuration information to be flushed.

+  @param[in]  Data               The data buffer to be set. The structure

+                                 of the data buffer should be EFI_IPSEC_PAD_DATA.

+  @param[in]  Context            Pointer to one entry selector that describes

+                                 the expected position the new data entry will

+                                 be added. If Context is NULL, the new entry will

+                                 be appended the end of database.

+

+  @retval EFI_OUT_OF_RESOURCES  The required system resources could not be allocated.

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+

+**/

+EFI_STATUS

+SetPadEntry (

+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,

+  IN VOID                            *Data,

+  IN VOID                            *Context OPTIONAL

+  )

+{

+  IPSEC_PAD_ENTRY     *PadEntry;

+  EFI_IPSEC_PAD_ID    *PadId;

+  EFI_IPSEC_PAD_DATA  *PadData;

+  LIST_ENTRY          *PadList;

+  LIST_ENTRY          *Entry;

+  LIST_ENTRY          *NextEntry;

+  EFI_IPSEC_PAD_ID    *InsertBefore;

+  LIST_ENTRY          *EntryInsertBefore;

+  UINTN               PadEntrySize;

+

+  PadId         = (Selector == NULL) ? NULL : &Selector->PadId;

+  PadData       = (Data == NULL) ? NULL : (EFI_IPSEC_PAD_DATA *) Data;

+  InsertBefore  = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->PadId;

+  PadList       = &mConfigData[IPsecConfigDataTypePad];

+

+  //

+  // The default behavior is to insert the node ahead of the header.

+  //

+  EntryInsertBefore = PadList;

+

+  //

+  // Remove the existed pad entry.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, PadList) {

+

+    PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);

+

+    if (PadId == NULL ||

+        ComparePadId ((EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id, (EFI_IPSEC_CONFIG_SELECTOR *) PadId)

+        ) {

+      //

+      // Record the existed entry position to keep the original order.

+      //

+      EntryInsertBefore = PadEntry->List.ForwardLink;

+      RemoveEntryList (&PadEntry->List);

+

+      FreePool (PadEntry);

+    }

+  }

+  //

+  // Return success here if only want to remove the pad entry

+  //

+  if (PadData == NULL || PadId == NULL) {

+    return EFI_SUCCESS;

+  }

+  //

+  // Search the appointed entry position if InsertBefore is not NULL.

+  //

+  if (InsertBefore != NULL) {

+

+    NET_LIST_FOR_EACH (Entry, PadList) {

+      PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);

+

+      if (ComparePadId (

+            (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id,

+            (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore

+            )) {

+        EntryInsertBefore = Entry;

+        break;

+      }

+    }

+  }

+

+  //

+  // Do PADDING for different arch.

+  //

+  PadEntrySize  = ALIGN_VARIABLE (sizeof (IPSEC_PAD_ENTRY));

+  PadEntrySize  = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_ID));

+  PadEntrySize  = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_DATA));

+  PadEntrySize  = ALIGN_VARIABLE (PadEntrySize + (PadData->AuthData != NULL ? PadData->AuthDataSize : 0));

+  PadEntrySize += PadData->RevocationData != NULL ? PadData->RevocationDataSize : 0;

+

+  PadEntry      = AllocateZeroPool (PadEntrySize);

+

+  if (PadEntry == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+  //

+  // Fix the address of Id and Data buffer and copy them, which is

+  // continous memory and close to the base structure of pad entry.

+  //

+  PadEntry->Id    = (EFI_IPSEC_PAD_ID *) ALIGN_POINTER ((PadEntry + 1), sizeof (UINTN));

+  PadEntry->Data  = (EFI_IPSEC_PAD_DATA *) ALIGN_POINTER ((PadEntry->Id + 1), sizeof (UINTN));

+

+  CopyMem (PadEntry->Id, PadId, sizeof (EFI_IPSEC_PAD_ID));

+

+  PadEntry->Data->AuthProtocol  = PadData->AuthProtocol;

+  PadEntry->Data->AuthMethod    = PadData->AuthMethod;

+  PadEntry->Data->IkeIdFlag     = PadData->IkeIdFlag;

+

+  if (PadData->AuthData != NULL) {

+    PadEntry->Data->AuthDataSize  = PadData->AuthDataSize;

+    PadEntry->Data->AuthData      = (VOID *) ALIGN_POINTER (PadEntry->Data + 1, sizeof (UINTN));

+    CopyMem (

+      PadEntry->Data->AuthData,

+      PadData->AuthData,

+      PadData->AuthDataSize

+      );

+  } else {

+    PadEntry->Data->AuthDataSize  = 0;

+    PadEntry->Data->AuthData      = NULL;

+  }

+

+  if (PadData->RevocationData != NULL) {

+    PadEntry->Data->RevocationDataSize  = PadData->RevocationDataSize;

+    PadEntry->Data->RevocationData      = (VOID *) ALIGN_POINTER (

+                                                    ((UINT8 *) (PadEntry->Data + 1) + PadData->AuthDataSize),

+                                                    sizeof (UINTN)

+                                                    );

+    CopyMem (

+      PadEntry->Data->RevocationData,

+      PadData->RevocationData,

+      PadData->RevocationDataSize

+      );

+  } else {

+    PadEntry->Data->RevocationDataSize  = 0;

+    PadEntry->Data->RevocationData      = NULL;

+  }

+  //

+  // Insert the new pad entry.

+  //

+  InsertTailList (EntryInsertBefore, &PadEntry->List);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function lookup the data entry from IPsec SPD. Return the configuration

+  value of the specified SPD Entry.

+

+  @param[in]      Selector      Pointer to an entry selector which is an identifier

+                                of the SPD entry.

+  @param[in, out] DataSize      On output the size of data returned in Data.

+  @param[out]     Data          The buffer to return the contents of the IPsec

+                                configuration data. The type of the data buffer

+                                is associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.

+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.

+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been

+                                updated with the size needed to complete the request.

+

+**/

+EFI_STATUS

+GetSpdEntry (

+  IN     EFI_IPSEC_CONFIG_SELECTOR       *Selector,

+  IN OUT UINTN                           *DataSize,

+     OUT VOID                            *Data

+  )

+{

+  IPSEC_SPD_ENTRY         *SpdEntry;

+  IPSEC_SAD_ENTRY         *SadEntry;

+  EFI_IPSEC_SPD_SELECTOR  *SpdSel;

+  EFI_IPSEC_SPD_DATA      *SpdData;

+  LIST_ENTRY              *SpdList;

+  LIST_ENTRY              *SpdSas;

+  LIST_ENTRY              *Entry;

+  UINTN                   RequiredSize;

+

+  SpdSel  = &Selector->SpdSelector;

+  SpdData = (EFI_IPSEC_SPD_DATA *) Data;

+  SpdList = &mConfigData[IPsecConfigDataTypeSpd];

+

+  NET_LIST_FOR_EACH (Entry, SpdList) {

+    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);

+

+    //

+    // Find the required spd entry

+    //

+    if (CompareSpdSelector (

+          (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,

+          (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector

+          )) {

+

+      RequiredSize = IpSecGetSizeOfSpdData (SpdEntry->Data);

+      if (*DataSize < RequiredSize) {

+        *DataSize = RequiredSize;

+        return EFI_BUFFER_TOO_SMALL;

+      }

+

+      if (SpdData == NULL) {

+        return EFI_INVALID_PARAMETER;

+      }

+

+      *DataSize = RequiredSize;

+

+      //

+      // Extract and fill all SaId array from the spd.sas list

+      //

+      SpdSas              = &SpdEntry->Data->Sas;

+      SpdData->SaIdCount  = 0;

+

+      NET_LIST_FOR_EACH (Entry, SpdSas) {

+        SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);

+        CopyMem (

+          &SpdData->SaId[SpdData->SaIdCount++],

+          SadEntry->Id,

+          sizeof (EFI_IPSEC_SA_ID)

+          );

+      }

+      //

+      // Fill the other fields in spd data.

+      //

+      CopyMem (SpdData->Name, SpdEntry->Data->Name, sizeof (SpdData->Name));

+

+      SpdData->PackageFlag  = SpdEntry->Data->PackageFlag;

+      SpdData->Action       = SpdEntry->Data->Action;

+

+      if (SpdData->Action != EfiIPsecActionProtect) {

+        SpdData->ProcessingPolicy = NULL;

+      } else {

+        SpdData->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ((UINT8 *) SpdData + sizeof (EFI_IPSEC_SPD_DATA) + (SpdData->SaIdCount - 1) * sizeof (EFI_IPSEC_SA_ID));

+

+        IpSecDuplicateProcessPolicy (

+          SpdData->ProcessingPolicy,

+          SpdEntry->Data->ProcessingPolicy

+          );

+      }

+

+      return EFI_SUCCESS;

+    }

+  }

+

+  return EFI_NOT_FOUND;

+}

+

+/**

+  This function lookup the data entry from IPsec SAD. Return the configuration

+  value of the specified SAD Entry.

+

+  @param[in]      Selector      Pointer to an entry selector which is an identifier

+                                of the SAD entry.

+  @param[in, out] DataSize      On output, the size of data returned in Data.

+  @param[out]     Data          The buffer to return the contents of the IPsec

+                                configuration data. The type of the data buffer

+                                is associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.

+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been

+                                updated with the size needed to complete the request.

+

+**/

+EFI_STATUS

+GetSadEntry (

+  IN     EFI_IPSEC_CONFIG_SELECTOR     *Selector,

+  IN OUT UINTN                         *DataSize,

+     OUT VOID                          *Data

+  )

+{

+  IPSEC_SAD_ENTRY   *SadEntry;

+  LIST_ENTRY        *Entry;

+  LIST_ENTRY        *SadList;

+  EFI_IPSEC_SA_ID   *SaId;

+  EFI_IPSEC_SA_DATA *SaData;

+  UINTN             RequiredSize;

+

+  SaId    = &Selector->SaId;

+  SaData  = (EFI_IPSEC_SA_DATA *) Data;

+  SadList = &mConfigData[IPsecConfigDataTypeSad];

+

+  NET_LIST_FOR_EACH (Entry, SadList) {

+    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);

+

+    //

+    // Find the required sad entry.

+    //

+    if (CompareSaId (

+         (EFI_IPSEC_CONFIG_SELECTOR *) SaId,

+         (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id

+         )) {

+      //

+      // Calculate the required size of the sad entry.

+      // Data Layout is follows:

+      // |EFI_IPSEC_SA_DATA

+      // |AuthKey

+      // |EncryptKey  (Optional)

+      // |SpdSelector (Optional)

+      //

+      RequiredSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA));

+

+      if (SaId->Proto == EfiIPsecAH) {

+        RequiredSize  = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength);

+      } else {

+        RequiredSize  = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength);

+        RequiredSize  = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength);

+      }

+

+      if (SadEntry->Data->SpdEntry != NULL) {

+        RequiredSize += SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector);

+      }

+

+

+

+      if (*DataSize < RequiredSize) {

+        *DataSize = RequiredSize;

+        return EFI_BUFFER_TOO_SMALL;

+      }

+      //

+      // Fill the data fields of sad entry.

+      //

+      *DataSize                 = RequiredSize;

+      SaData->Mode              = SadEntry->Data->Mode;

+      SaData->SNCount           = SadEntry->Data->SequenceNumber;

+      SaData->AntiReplayWindows = SadEntry->Data->AntiReplayWindowSize;

+

+      CopyMem (

+        &SaData->SaLifetime,

+        &SadEntry->Data->SaLifetime,

+        sizeof (EFI_IPSEC_SA_LIFETIME)

+        );

+

+      ZeroMem (

+        &SaData->AlgoInfo,

+        sizeof (EFI_IPSEC_ALGO_INFO)

+        );

+

+      if (SaId->Proto == EfiIPsecAH) {

+        //

+        // Copy AH alogrithm INFO to SaData

+        //

+        SaData->AlgoInfo.AhAlgoInfo.AuthAlgoId    = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthAlgoId;

+        SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength;

+        if (SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength != 0) {

+          SaData->AlgoInfo.AhAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));

+          CopyMem (

+            SaData->AlgoInfo.AhAlgoInfo.AuthKey,

+            SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKey,

+            SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength

+            );

+        }

+      } else if (SaId->Proto == EfiIPsecESP) {

+        //

+        // Copy ESP alogrithem INFO to SaData

+        //

+        SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId     = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId;

+        SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength  = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength;

+        if (SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {

+          SaData->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));

+          CopyMem (

+            SaData->AlgoInfo.EspAlgoInfo.AuthKey,

+            SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,

+            SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength

+            );

+        }

+

+        SaData->AlgoInfo.EspAlgoInfo.EncAlgoId    = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId;

+        SaData->AlgoInfo.EspAlgoInfo.EncKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength;

+

+        if (SaData->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {

+          SaData->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (

+                                                          ((UINT8 *) (SaData + 1) +

+                                                            SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength),

+                                                            sizeof (UINTN)

+                                                            );

+          CopyMem (

+            SaData->AlgoInfo.EspAlgoInfo.EncKey,

+            SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,

+            SaData->AlgoInfo.EspAlgoInfo.EncKeyLength

+            );

+        }

+      }

+

+      SaData->PathMTU = SadEntry->Data->PathMTU;

+

+      //

+      // Fill the spd selector field of sad data

+      //

+      if (SadEntry->Data->SpdEntry != NULL) {

+

+        SaData->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) (

+                                (UINT8 *)SaData +

+                                RequiredSize -

+                                SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector)

+                                );

+

+        DuplicateSpdSelector (

+          (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector,

+          (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdEntry->Selector,

+          NULL

+          );

+

+      } else {

+

+        SaData->SpdSelector = NULL;

+      }

+

+      SaData->ManualSet = SadEntry->Data->ManualSet;

+

+      return EFI_SUCCESS;

+    }

+  }

+

+  return EFI_NOT_FOUND;

+}

+

+/**

+  This function lookup the data entry from IPsec PAD. Return the configuration

+  value of the specified PAD Entry.

+

+  @param[in]      Selector      Pointer to an entry selector which is an identifier

+                                of the PAD entry.

+  @param[in, out] DataSize      On output the size of data returned in Data.

+  @param[out]     Data          The buffer to return the contents of the IPsec

+                                configuration data. The type of the data buffer

+                                is associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.

+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been

+                                updated with the size needed to complete the request.

+

+**/

+EFI_STATUS

+GetPadEntry (

+  IN     EFI_IPSEC_CONFIG_SELECTOR   *Selector,

+  IN OUT UINTN                       *DataSize,

+     OUT VOID                        *Data

+  )

+{

+  IPSEC_PAD_ENTRY     *PadEntry;

+  LIST_ENTRY          *PadList;

+  LIST_ENTRY          *Entry;

+  EFI_IPSEC_PAD_ID    *PadId;

+  EFI_IPSEC_PAD_DATA  *PadData;

+  UINTN               RequiredSize;

+

+  PadId   = &Selector->PadId;

+  PadData = (EFI_IPSEC_PAD_DATA *) Data;

+  PadList = &mConfigData[IPsecConfigDataTypePad];

+

+  NET_LIST_FOR_EACH (Entry, PadList) {

+    PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);

+

+    //

+    // Find the required pad entry.

+    //

+    if (ComparePadId (

+          (EFI_IPSEC_CONFIG_SELECTOR *) PadId,

+          (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id

+          )) {

+      //

+      // Calculate the required size of the pad entry.

+      //

+      RequiredSize  = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));

+      RequiredSize  = ALIGN_VARIABLE (RequiredSize + PadEntry->Data->AuthDataSize);

+      RequiredSize += PadEntry->Data->RevocationDataSize;

+

+      if (*DataSize < RequiredSize) {

+        *DataSize = RequiredSize;

+        return EFI_BUFFER_TOO_SMALL;

+      }

+      //

+      // Fill the data fields of pad entry

+      //

+      *DataSize             = RequiredSize;

+      PadData->AuthProtocol = PadEntry->Data->AuthProtocol;

+      PadData->AuthMethod   = PadEntry->Data->AuthMethod;

+      PadData->IkeIdFlag    = PadEntry->Data->IkeIdFlag;

+

+      //

+      // Copy Authentication data.

+      //

+      if (PadEntry->Data->AuthData != NULL) {

+

+        PadData->AuthDataSize = PadEntry->Data->AuthDataSize;

+        PadData->AuthData     = (VOID *) ALIGN_POINTER ((PadData + 1), sizeof (UINTN));

+        CopyMem (

+          PadData->AuthData,

+          PadEntry->Data->AuthData,

+          PadData->AuthDataSize

+          );

+      } else {

+

+        PadData->AuthDataSize = 0;

+        PadData->AuthData     = NULL;

+      }

+      //

+      // Copy Revocation Data.

+      //

+      if (PadEntry->Data->RevocationData != NULL) {

+

+        PadData->RevocationDataSize = PadEntry->Data->RevocationDataSize;

+        PadData->RevocationData     = (VOID *) ALIGN_POINTER (

+                                                 ((UINT8 *) (PadData + 1) + PadData->AuthDataSize),

+                                                  sizeof (UINTN)

+                                                  );

+        CopyMem (

+          PadData->RevocationData,

+          PadEntry->Data->RevocationData,

+          PadData->RevocationDataSize

+          );

+      } else {

+

+        PadData->RevocationDataSize = 0;

+        PadData->RevocationData     = NULL;

+      }

+

+      return EFI_SUCCESS;

+    }

+  }

+

+  return EFI_NOT_FOUND;

+}

+

+/**

+  Copy Source Process Policy to the Destination Process Policy.

+

+  @param[in]  Dst                  Pointer to the Source Process Policy.

+  @param[in]  Src                  Pointer to the Destination Process Policy.

+

+**/

+VOID

+IpSecDuplicateProcessPolicy (

+  IN EFI_IPSEC_PROCESS_POLICY            *Dst,

+  IN EFI_IPSEC_PROCESS_POLICY            *Src

+  )

+{

+  //

+  // Firstly copy the structure content itself.

+  //

+  CopyMem (Dst, Src, sizeof (EFI_IPSEC_PROCESS_POLICY));

+

+  //

+  // Recursively copy the tunnel option if needed.

+  //

+  if (Dst->Mode != EfiIPsecTunnel) {

+    ASSERT (Dst->TunnelOption == NULL);

+  } else {

+    Dst->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER ((Dst + 1), sizeof (UINTN));

+    CopyMem (

+      Dst->TunnelOption,

+      Src->TunnelOption,

+      sizeof (EFI_IPSEC_TUNNEL_OPTION)

+      );

+  }

+}

+

+/**

+  Calculate the a whole size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed

+  to by the pointer members.

+

+  @param[in]  SpdData             Pointer to a specified EFI_IPSEC_SPD_DATA.

+

+  @return the whole size the specified EFI_IPSEC_SPD_DATA.

+

+**/

+UINTN

+IpSecGetSizeOfEfiSpdData (

+  IN EFI_IPSEC_SPD_DATA               *SpdData

+  )

+{

+  UINTN Size;

+

+  Size = ALIGN_VARIABLE (sizeof (IPSEC_SPD_DATA));

+

+  if (SpdData->Action == EfiIPsecActionProtect) {

+    Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_PROCESS_POLICY));

+

+    if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {

+      Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_TUNNEL_OPTION));

+    }

+  }

+

+  return Size;

+}

+

+/**

+  Calculate the a whole size of IPSEC_SPD_DATA which includes the buffer size pointed

+  to by the pointer members and the buffer size used by the Sa List.

+

+  @param[in]  SpdData       Pointer to the specified IPSEC_SPD_DATA.

+

+  @return the whole size of IPSEC_SPD_DATA.

+

+**/

+UINTN

+IpSecGetSizeOfSpdData (

+  IN IPSEC_SPD_DATA                   *SpdData

+  )

+{

+  UINTN       Size;

+  LIST_ENTRY  *Link;

+

+  Size = sizeof (EFI_IPSEC_SPD_DATA) - sizeof (EFI_IPSEC_SA_ID);

+

+  if (SpdData->Action == EfiIPsecActionProtect) {

+    Size += sizeof (EFI_IPSEC_PROCESS_POLICY);

+

+    if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {

+      Size += sizeof (EFI_IPSEC_TUNNEL_OPTION);

+    }

+  }

+

+  NET_LIST_FOR_EACH (Link, &SpdData->Sas) {

+    Size += sizeof (EFI_IPSEC_SA_ID);

+  }

+

+  return Size;

+}

+

+/**

+  Get the IPsec Variable.

+

+  Get the all variables which start with the string contained in VaraiableName.

+  Since all IPsec related variable store in continual space, those kinds of

+  variable can be searched by the EfiGetNextVariableName. Those variables also are

+  returned in a continual buffer.

+

+  @param[in]      VariableName          Pointer to a specified Variable Name.

+  @param[in]      VendorGuid            Pointer to a specified Vendor Guid.

+  @param[in]      Attributes            Point to memory location to return the attributes

+                                        of variable. If the point is NULL, the parameter

+                                        would be ignored.

+  @param[in, out] DataSize              As input, point to the maximum size of return

+                                        Data-Buffer. As output, point to the actual

+                                        size of the returned Data-Buffer.

+  @param[in]      Data                  Point to return Data-Buffer.

+

+  @retval  EFI_ABORTED           If the Variable size which contained in the variable

+                                 structure doesn't match the variable size obtained

+                                 from the EFIGetVariable.

+  @retval  EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has

+                                 been updated with the size needed to complete the request.

+  @retval  EFI_SUCCESS           The function completed successfully.

+  @retval  others                Other errors found during the variable getting.

+**/

+EFI_STATUS

+IpSecGetVariable (

+  IN     CHAR16                       *VariableName,

+  IN     EFI_GUID                     *VendorGuid,

+  IN     UINT32                       *Attributes, OPTIONAL

+  IN OUT UINTN                        *DataSize,

+  IN     VOID                         *Data

+  )

+{

+  EFI_STATUS            Status;

+  EFI_GUID              VendorGuidI;

+  UINTN                 VariableNameLength;

+  CHAR16                *VariableNameI;

+  UINTN                 VariableNameISize;

+  UINTN                 VariableNameISizeNew;

+  UINTN                 VariableIndex;

+  UINTN                 VariableCount;

+  IP_SEC_VARIABLE_INFO  IpSecVariableInfo;

+  UINTN                 DataSizeI;

+

+  //

+  // The variable name constructor is "VariableName + Info/0001/0002/... + NULL".

+  // So the varialbe name is like "VariableNameInfo", "VariableName0001", ...

+  // "VariableNameNULL".

+  //

+  VariableNameLength  = StrLen (VariableName);

+  VariableNameISize   = (VariableNameLength + 5) * sizeof (CHAR16);

+  VariableNameI       = AllocateZeroPool (VariableNameISize);

+  ASSERT (VariableNameI != NULL);

+

+  //

+  // Construct the varible name of ipsecconfig meta data.

+  //

+  UnicodeSPrint (VariableNameI, VariableNameISize, L"%s%s", VariableName, L"Info");

+

+  DataSizeI = sizeof (IpSecVariableInfo);

+

+  Status = gRT->GetVariable (

+                  VariableNameI,

+                  VendorGuid,

+                  Attributes,

+                  &DataSizeI,

+                  &IpSecVariableInfo

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  if (*DataSize < IpSecVariableInfo.VariableSize) {

+    *DataSize = IpSecVariableInfo.VariableSize;

+    Status    = EFI_BUFFER_TOO_SMALL;

+    goto ON_EXIT;

+  }

+

+  VariableCount     = IpSecVariableInfo.VariableCount;

+  VariableNameI[0]  = L'\0';

+

+  while (VariableCount != 0) {

+    //

+    // Get the variable name one by one in the variable database.

+    //

+    VariableNameISizeNew = VariableNameISize;

+    Status = gRT->GetNextVariableName (

+                    &VariableNameISizeNew,

+                    VariableNameI,

+                    &VendorGuidI

+                    );

+    if (Status == EFI_BUFFER_TOO_SMALL) {

+      VariableNameI = ReallocatePool (

+                        VariableNameISize,

+                        VariableNameISizeNew,

+                        VariableNameI

+                        );

+      VariableNameISize = VariableNameISizeNew;

+

+      Status = gRT->GetNextVariableName (

+                      &VariableNameISizeNew,

+                      VariableNameI,

+                      &VendorGuidI

+                      );

+    }

+

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+    //

+    // Check whether the current variable is the required "ipsecconfig".

+    //

+    if (StrnCmp (VariableNameI, VariableName, VariableNameLength) == 0 ||

+        CompareGuid (VendorGuid, &VendorGuidI)

+        ) {

+      //

+      // Parse the variable count of the current ipsecconfig data.

+      //

+      VariableIndex = StrDecimalToUintn (VariableNameI + VariableNameLength);

+      if (VariableIndex!= 0 && VariableIndex <= IpSecVariableInfo.VariableCount) {

+        //

+        // Get the variable size of the current ipsecconfig data.

+        //

+        DataSizeI = 0;

+        Status = gRT->GetVariable (

+                        VariableNameI,

+                        VendorGuid,

+                        Attributes,

+                        &DataSizeI,

+                        NULL

+                        );

+        ASSERT (Status == EFI_BUFFER_TOO_SMALL);

+        //

+        // Validate the variable count and variable size.

+        //

+        if (VariableIndex != IpSecVariableInfo.VariableCount) {

+          //

+          // If the varaibe is not the last one, its size should be the max

+          // size of the single variable.

+          //

+          if (DataSizeI != IpSecVariableInfo.SingleVariableSize) {

+            return EFI_ABORTED;

+          }

+        } else {

+          if (DataSizeI != IpSecVariableInfo.VariableSize % IpSecVariableInfo.SingleVariableSize) {

+            return EFI_ABORTED;

+          }

+        }

+        //

+        // Get the variable data of the current ipsecconfig data and

+        // store it into user buffer continously.

+        //

+        Status = gRT->GetVariable (

+                        VariableNameI,

+                        VendorGuid,

+                        Attributes,

+                        &DataSizeI,

+                        (UINT8 *) Data + (VariableIndex - 1) * IpSecVariableInfo.SingleVariableSize

+                        );

+        ASSERT_EFI_ERROR (Status);

+        VariableCount--;

+      }

+    }

+  }

+  //

+  // The VariableCount in "VariableNameInfo" varaible should have the correct

+  // numbers of variables which name starts with VariableName.

+  //

+  if (VariableCount != 0) {

+    Status = EFI_ABORTED;

+  }

+

+ON_EXIT:

+  FreePool (VariableNameI);

+  return Status;

+}

+

+/**

+  Set the IPsec variables.

+

+  Set all IPsec variables which start with the specified variable name. Those variables

+  are set one by one.

+

+  @param[in]  VariableName  The name of the vendor's variable. It is a

+                            Null-Terminated Unicode String.

+  @param[in]  VendorGuid    Unify identifier for vendor.

+  @param[in]  Attributes    Point to memory location to return the attributes of

+                            variable. If the point is NULL, the parameter would be ignored.

+  @param[in]  DataSize      The size in bytes of Data-Buffer.

+  @param[in]  Data          Points to the content of the variable.

+

+  @retval  EFI_SUCCESS      The firmware successfully stored the variable and its data, as

+                            defined by the Attributes.

+  @retval  others           Storing the variables failed.

+

+**/

+EFI_STATUS

+IpSecSetVariable (

+  IN CHAR16                           *VariableName,

+  IN EFI_GUID                         *VendorGuid,

+  IN UINT32                           Attributes,

+  IN UINTN                            DataSize,

+  IN VOID                             *Data

+  )

+{

+  EFI_STATUS            Status;

+  CHAR16                *VariableNameI;

+  UINTN                 VariableNameSize;

+  UINTN                 VariableIndex;

+  IP_SEC_VARIABLE_INFO  IpSecVariableInfo;

+  UINT64                MaximumVariableStorageSize;

+  UINT64                RemainingVariableStorageSize;

+  UINT64                MaximumVariableSize;

+

+  Status = gRT->QueryVariableInfo (

+                  Attributes,

+                  &MaximumVariableStorageSize,

+                  &RemainingVariableStorageSize,

+                  &MaximumVariableSize

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // "VariableName + Info/0001/0002/... + NULL"

+  //

+  VariableNameSize  = (StrLen (VariableName) + 5) * sizeof (CHAR16);

+  VariableNameI     = AllocateZeroPool (VariableNameSize);

+

+  if (VariableNameI == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+  //

+  // Construct the variable of ipsecconfig general information. Like the total

+  // numbers of the Ipsecconfig variables, the total size of all ipsecconfig variables.

+  //

+  UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%s", VariableName, L"Info");

+  MaximumVariableSize -= VariableNameSize;

+

+  IpSecVariableInfo.VariableCount       = (UINT32) ((DataSize + (UINTN) MaximumVariableSize - 1) / (UINTN) MaximumVariableSize);

+  IpSecVariableInfo.VariableSize        = (UINT32) DataSize;

+  IpSecVariableInfo.SingleVariableSize  = (UINT32) MaximumVariableSize;

+

+  //

+  // Set the variable of ipsecconfig general information.

+  //

+  Status = gRT->SetVariable (

+                  VariableNameI,

+                  VendorGuid,

+                  Attributes,

+                  sizeof (IpSecVariableInfo),

+                  &IpSecVariableInfo

+                  );

+  if (EFI_ERROR (Status)) {

+    DEBUG ((DEBUG_ERROR, "Error set ipsecconfig meta data with %r\n", Status));

+    goto ON_EXIT;

+  }

+

+  for (VariableIndex = 0; VariableIndex < IpSecVariableInfo.VariableCount; VariableIndex++) {

+    //

+    // Construct and set the variable of ipsecconfig data one by one.

+    // The index of variable name begin from 0001, and the varaible name

+    // likes "VariableName0001", "VaraiableName0002"....

+    //

+    UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%04d", VariableName, VariableIndex + 1);

+    Status = gRT->SetVariable (

+                    VariableNameI,

+                    VendorGuid,

+                    Attributes,

+                    (VariableIndex == IpSecVariableInfo.VariableCount - 1) ?

+                    (DataSize % (UINTN) MaximumVariableSize) :

+                    (UINTN) MaximumVariableSize,

+                    (UINT8 *) Data + VariableIndex * (UINTN) MaximumVariableSize

+                    );

+

+    if (EFI_ERROR (Status)) {

+      DEBUG ((DEBUG_ERROR, "Error set ipsecconfig variable data with %r\n", Status));

+      goto ON_EXIT;

+    }

+  }

+

+ON_EXIT:

+  if (VariableNameI != NULL) {

+    FreePool (VariableNameI);

+  }

+

+  return Status;

+}

+

+/**

+  Return the configuration value for the EFI IPsec driver.

+

+  This function lookup the data entry from IPsec database or IKEv2 configuration

+  information. The expected data type and unique identification are described in

+  DataType and Selector parameters.

+

+  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in]      DataType      The type of data to retrieve.

+  @param[in]      Selector      Pointer to an entry selector that is an identifier of the IPsec

+                                configuration data entry.

+  @param[in, out] DataSize      On output the size of data returned in Data.

+  @param[out]     Data          The buffer to return the contents of the IPsec configuration data.

+                                The type of the data buffer associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:

+                                - This is NULL.

+                                - Selector is NULL.

+                                - DataSize is NULL.

+                                - Data is NULL and *DataSize is not zero

+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.

+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.

+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been

+                                updated with the size needed to complete the request.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigGetData (

+  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,

+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,

+  IN OUT UINTN                        *DataSize,

+     OUT VOID                         *Data

+  )

+{

+  if (This == NULL || Selector == NULL || DataSize == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (*DataSize != 0 && Data == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (DataType >= IPsecConfigDataTypeMaximum) {

+    return EFI_UNSUPPORTED;

+  }

+

+  return mGetPolicyEntry[DataType](Selector, DataSize, Data);

+}

+

+/**

+  Set the security association, security policy and peer authorization configuration

+  information for the EFI IPsec driver.

+

+  This function is used to set the IPsec configuration information of type DataType for

+  the EFI IPsec driver.

+  The IPsec configuration data has a unique selector/identifier separately to identify

+  a data entry. The selector structure depends on DataType's definition.

+  Using SetData() with a Data of NULL causes the IPsec configuration data entry identified

+  by DataType and Selector to be deleted.

+

+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in] DataType           The type of data to be set.

+  @param[in] Selector           Pointer to an entry selector on operated configuration data

+                                specified by DataType. A NULL Selector causes the entire

+                                specified-type configuration information to be flushed.

+  @param[in] Data               The data buffer to be set. The structure of the data buffer is

+                                associated with the DataType.

+  @param[in] InsertBefore       Pointer to one entry selector which describes the expected

+                                position the new data entry will be added. If InsertBefore is NULL,

+                                the new entry will be appended to the end of the database.

+

+  @retval EFI_SUCCESS           The specified configuration entry data was set successfully.

+  @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:

+                                - This is NULL.

+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.

+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigSetData (

+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,

+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector,

+  IN VOID                             *Data,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *InsertBefore OPTIONAL

+  )

+{

+  EFI_STATUS  Status;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (DataType >= IPsecConfigDataTypeMaximum) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Status = mSetPolicyEntry[DataType](Selector, Data, InsertBefore);

+

+  if (!EFI_ERROR (Status) && !mSetBySelf) {

+    //

+    // Save the updated config data into variable.

+    //

+    IpSecConfigSave ();

+  }

+

+  return Status;

+}

+

+/**

+  Enumerates the current selector for IPsec configuration data entry.

+

+  This function is called multiple times to retrieve the entry Selector in IPsec

+  configuration database. On each call to GetNextSelector(), the next entry

+  Selector are retrieved into the output interface.

+

+  If the entire IPsec configuration database has been iterated, the error

+  EFI_NOT_FOUND is returned.

+  If the Selector buffer is too small for the next Selector copy, an

+  EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect

+  the size of buffer needed.

+

+  On the initial call to GetNextSelector() to start the IPsec configuration database

+  search, a pointer to the buffer with all zero value is passed in Selector. Calls

+  to SetData() between calls to GetNextSelector may produce unpredictable results.

+

+  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in]      DataType      The type of IPsec configuration data to retrieve.

+  @param[in, out] SelectorSize  The size of the Selector buffer.

+  @param[in, out] Selector      On input, supplies the pointer to last Selector that was

+                                returned by GetNextSelector().

+                                On output, returns one copy of the current entry Selector

+                                of a given DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:

+                                - This is NULL.

+                                - SelectorSize is NULL.

+                                - Selector is NULL.

+  @retval EFI_NOT_FOUND         The next configuration data entry was not found.

+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.

+  @retval EFI_BUFFER_TOO_SMALL  The SelectorSize is too small for the result. This parameter

+                                has been updated with the size needed to complete the search

+                                request.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigGetNextSelector (

+  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,

+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,

+  IN OUT UINTN                        *SelectorSize,

+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *Selector

+  )

+{

+  LIST_ENTRY                *Link;

+  IPSEC_COMMON_POLICY_ENTRY *CommonEntry;

+  BOOLEAN                   IsFound;

+

+  if (This == NULL || Selector == NULL || SelectorSize == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (DataType >= IPsecConfigDataTypeMaximum) {

+    return EFI_UNSUPPORTED;

+  }

+

+  IsFound = FALSE;

+

+  NET_LIST_FOR_EACH (Link, &mConfigData[DataType]) {

+    CommonEntry = BASE_CR (Link, IPSEC_COMMON_POLICY_ENTRY, List);

+

+    if (IsFound || mIsZeroSelector[DataType](Selector)) {

+      //

+      // If found the appointed entry, then duplicate the next one and return,

+      // or if the appointed entry is zero, then return the first one directly.

+      //

+      return mDuplicateSelector[DataType](Selector, CommonEntry->Selector, SelectorSize);

+    } else {

+      //

+      // Set the flag if find the appointed entry.

+      //

+      IsFound = mCompareSelector[DataType](Selector, CommonEntry->Selector);

+    }

+  }

+

+  return EFI_NOT_FOUND;

+}

+

+/**

+  Register an event that is to be signaled whenever a configuration process on the

+  specified IPsec configuration information is done.

+

+  The register function is not surpport now and always returns EFI_UNSUPPORTED.

+

+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in] DataType           The type of data to be registered the event for.

+  @param[in] Event              The event to be registered.

+

+  @retval EFI_SUCCESS           The event is registered successfully.

+  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.

+  @retval EFI_ACCESS_DENIED     The Event is already registered for the DataType.

+  @retval EFI_UNSUPPORTED       The notify registration is unsupported, or the specified

+                                DataType is not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigRegisterNotify (

+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,

+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,

+  IN EFI_EVENT                        Event

+  )

+{

+  return EFI_UNSUPPORTED;

+}

+

+/**

+  Remove the specified event that was previously registered on the specified IPsec

+  configuration data.

+

+  This function is not support now and alwasy return EFI_UNSUPPORTED.

+

+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in] DataType           The configuration data type to remove the registered event for.

+  @param[in] Event              The event to be unregistered.

+

+  @retval EFI_SUCCESS           The event was removed successfully.

+  @retval EFI_NOT_FOUND         The Event specified by DataType could not be found in the

+                                database.

+  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.

+  @retval EFI_UNSUPPORTED       The notify registration is unsupported, or the specified

+                                DataType is not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigUnregisterNotify (

+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,

+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,

+  IN EFI_EVENT                        Event

+  )

+{

+  return EFI_UNSUPPORTED;

+}

+

+/**

+  Copy whole data in specified EFI_SIPEC_CONFIG_SELECTOR and the Data to a buffer.

+

+  This function is a caller defined function, and it is called by the IpSecVisitConfigData().

+  The orignal caller is IpSecConfigSave(), which calls the IpsecVisitConfigData() to

+  copy all types of IPsec Config datas into one buffer and store this buffer into firmware in

+  the form of several variables.

+

+  @param[in]      Type              A specified IPSEC_CONFIG_DATA_TYPE.

+  @param[in]      Selector          Points to a EFI_IPSEC_CONFIG_SELECTOR to be copied

+                                    to the buffer.

+  @param[in]      Data              Points to data to be copied to the buffer. The

+                                    Data type is related to the Type.

+  @param[in]      SelectorSize      The size of the Selector.

+  @param[in]      DataSize          The size of the Data.

+  @param[in, out] Buffer            The buffer to store the Selector and Data.

+

+  @retval EFI_SUCCESS            Copy the Selector and Data to a buffer successfully.

+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.

+

+**/

+EFI_STATUS

+IpSecCopyPolicyEntry (

+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   Type,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,

+  IN     VOID                         *Data,

+  IN     UINTN                        SelectorSize,

+  IN     UINTN                        DataSize,

+  IN OUT IPSEC_VARIABLE_BUFFER        *Buffer

+  )

+{

+  IPSEC_VAR_ITEM_HEADER SelectorHeader;

+  IPSEC_VAR_ITEM_HEADER DataHeader;

+  UINTN                 EntrySize;

+  UINT8                 *TempPoint;

+

+  if (Type == IPsecConfigDataTypeSad) {

+    //

+    // Don't save automatically-generated sa entry into variable.

+    //

+    if (((EFI_IPSEC_SA_DATA *) Data)->ManualSet == FALSE) {

+      return EFI_SUCCESS;

+    }

+  }

+  //

+  // Increase the capacity size of the buffer if needed.

+  //

+  EntrySize  = ALIGN_VARIABLE (sizeof (SelectorHeader));

+  EntrySize  = ALIGN_VARIABLE (EntrySize + SelectorSize);

+  EntrySize  = ALIGN_VARIABLE (EntrySize + sizeof (SelectorHeader));

+  EntrySize  = ALIGN_VARIABLE (EntrySize + DataSize);

+

+  //EntrySize = SelectorSize + DataSize + 2 * sizeof (SelectorHeader);

+  if (Buffer->Capacity - Buffer->Size < EntrySize) {

+    //

+    // Calculate the required buffer

+    //

+    Buffer->Capacity += EntrySize;

+    TempPoint         = AllocatePool (Buffer->Capacity);

+

+    if (Buffer->Ptr == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+    //

+    // Copy the old Buffer to new buffer and free the old one.

+    //

+    CopyMem (TempPoint, Buffer->Ptr, Buffer->Size);

+    FreePool (Buffer->Ptr);

+

+    Buffer->Ptr       =  TempPoint;

+  }

+

+  mFixPolicyEntry[Type](Selector, Data);

+

+  //

+  // Fill the selector header and copy it into buffer.

+  //

+  SelectorHeader.Type = (UINT8) (Type | IPSEC_VAR_ITEM_HEADER_LOGO_BIT);

+  SelectorHeader.Size = (UINT16) SelectorSize;

+

+  CopyMem (

+    Buffer->Ptr + Buffer->Size,

+    &SelectorHeader,

+    sizeof (SelectorHeader)

+    );

+  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + sizeof (SelectorHeader));

+

+  //

+  // Copy the selector into buffer.

+  //

+  CopyMem (

+    Buffer->Ptr + Buffer->Size,

+    Selector,

+    SelectorSize

+    );

+  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + SelectorSize);

+

+  //

+  // Fill the data header and copy it into buffer.

+  //

+  DataHeader.Type = (UINT8) Type;

+  DataHeader.Size = (UINT16) DataSize;

+

+  CopyMem (

+    Buffer->Ptr + Buffer->Size,

+    &DataHeader,

+    sizeof (DataHeader)

+    );

+  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + sizeof (DataHeader));

+  //

+  // Copy the data into buffer.

+  //

+  CopyMem (

+    Buffer->Ptr + Buffer->Size,

+    Data,

+    DataSize

+    );

+  Buffer->Size  = ALIGN_VARIABLE (Buffer->Size + DataSize);

+

+  mUnfixPolicyEntry[Type](Selector, Data);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Visit all IPsec Configurations of specified Type and call the caller defined

+  interface.

+

+  @param[in]  DataType          The specified IPsec Config Data Type.

+  @param[in]  Routine           The function defined by the caller.

+  @param[in]  Context           The data passed to the Routine.

+

+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated

+  @retval EFI_SUCCESS            This function completed successfully.

+

+**/

+EFI_STATUS

+IpSecVisitConfigData (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,

+  IN IPSEC_COPY_POLICY_ENTRY    Routine,

+  IN VOID                       *Context

+  )

+{

+  EFI_STATUS                GetNextStatus;

+  EFI_STATUS                GetDataStatus;

+  EFI_STATUS                RoutineStatus;

+  EFI_IPSEC_CONFIG_SELECTOR *Selector;

+  VOID                      *Data;

+  UINTN                     SelectorSize;

+  UINTN                     DataSize;

+  UINTN                     SelectorBufferSize;

+  UINTN                     DataBufferSize;

+  BOOLEAN                   FirstGetNext;

+

+  FirstGetNext        = TRUE;

+  DataBufferSize      = 0;

+  Data                = NULL;

+  SelectorBufferSize  = sizeof (EFI_IPSEC_CONFIG_SELECTOR);

+  Selector            = AllocateZeroPool (SelectorBufferSize);

+

+  if (Selector == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  while (TRUE) {

+    //

+    // Get the real size of the selector.

+    //

+    SelectorSize = SelectorBufferSize;

+    GetNextStatus = EfiIpSecConfigGetNextSelector (

+                      &mIpSecConfigInstance,

+                      DataType,

+                      &SelectorSize,

+                      Selector

+                      );

+    if (GetNextStatus == EFI_BUFFER_TOO_SMALL) {

+      FreePool (Selector);

+      SelectorBufferSize = SelectorSize;

+      //

+      // Allocate zero pool for the first selector, while store the last

+      // selector content for the other selectors.

+      //

+      if (FirstGetNext) {

+        Selector = AllocateZeroPool (SelectorBufferSize);

+      } else {

+        Selector = AllocateCopyPool (SelectorBufferSize, Selector);

+      }

+

+      if (Selector == NULL) {

+        return EFI_OUT_OF_RESOURCES;

+      }

+      //

+      // Get the content of the selector.

+      //

+      GetNextStatus = EfiIpSecConfigGetNextSelector (

+                        &mIpSecConfigInstance,

+                        DataType,

+                        &SelectorSize,

+                        Selector

+                        );

+    }

+

+    if (EFI_ERROR (GetNextStatus)) {

+      break;

+    }

+

+    FirstGetNext = FALSE;

+

+    //

+    // Get the real size of the policy entry according to the selector.

+    //

+    DataSize = DataBufferSize;

+    GetDataStatus = EfiIpSecConfigGetData (

+                      &mIpSecConfigInstance,

+                      DataType,

+                      Selector,

+                      &DataSize,

+                      Data

+                      );

+    if (GetDataStatus == EFI_BUFFER_TOO_SMALL) {

+      if (Data != NULL) {

+        FreePool (Data);

+      }

+

+      DataBufferSize  = DataSize;

+      Data            = AllocateZeroPool (DataBufferSize);

+

+      if (Data == NULL) {

+        return EFI_OUT_OF_RESOURCES;

+      }

+      //

+      // Get the content of the policy entry according to the selector.

+      //

+      GetDataStatus = EfiIpSecConfigGetData (

+                        &mIpSecConfigInstance,

+                        DataType,

+                        Selector,

+                        &DataSize,

+                        Data

+                        );

+    }

+

+    if (EFI_ERROR (GetDataStatus)) {

+      break;

+    }

+    //

+    // Prepare the buffer of updated policy entry, which is stored in

+    // the continous memory, and then save into variable later.

+    //

+    RoutineStatus = Routine (

+                      DataType,

+                      Selector,

+                      Data,

+                      SelectorSize,

+                      DataSize,

+                      Context

+                      );

+    if (EFI_ERROR (RoutineStatus)) {

+      break;

+    }

+  }

+

+  if (Data != NULL) {

+    FreePool (Data);

+  }

+

+  if (Selector != NULL) {

+    FreePool (Selector);

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This function is the subfunction of  EFIIpSecConfigSetData.

+

+  This function call IpSecSetVaraible to set the IPsec Configuration into the firmware.

+

+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.

+  @retval EFI_SUCCESS            Saved the configration successfully.

+  @retval Others                 Other errors were found while obtaining the variable.

+

+**/

+EFI_STATUS

+IpSecConfigSave (

+  VOID

+  )

+{

+  IPSEC_VARIABLE_BUFFER       Buffer;

+  EFI_STATUS                  Status;

+  EFI_IPSEC_CONFIG_DATA_TYPE  Type;

+

+  Buffer.Size     = 0;

+  Buffer.Capacity = IPSEC_DEFAULT_VARIABLE_SIZE;

+  Buffer.Ptr      = AllocateZeroPool (Buffer.Capacity);

+

+  if (Buffer.Ptr == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+  //

+  // For each policy database, prepare the contious buffer to save into variable.

+  //

+  for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {

+    IpSecVisitConfigData (

+      Type,

+      (IPSEC_COPY_POLICY_ENTRY) IpSecCopyPolicyEntry,

+      &Buffer

+      );

+  }

+  //

+  // Save the updated policy database into variable.

+  //

+  Status = IpSecSetVariable (

+             IPSECCONFIG_VARIABLE_NAME,

+             &gEfiIpSecConfigProtocolGuid,

+             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,

+             Buffer.Size,

+             Buffer.Ptr

+             );

+

+  FreePool (Buffer.Ptr);

+

+  return Status;

+}

+

+/**

+  Get the all IPSec configuration variables and store those variables

+  to the internal data structure.

+

+  This founction is called by IpSecConfigInitialize() which is to intialize the

+  IPsecConfiguration Protocol.

+

+  @param[in]  Private            Point to IPSEC_PRIVATE_DATA.

+

+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated

+  @retval EFI_SUCCESS            Restore the IPsec Configuration successfully.

+  @retval  others                Other errors is found while obtaining the variable.

+

+**/

+EFI_STATUS

+IpSecConfigRestore (

+  IN IPSEC_PRIVATE_DATA           *Private

+  )

+{

+  EFI_STATUS                  Status;

+  UINTN                       BufferSize;

+  UINT8                       *Buffer;

+  IPSEC_VAR_ITEM_HEADER       *Header;

+  UINT8                       *Ptr;

+  EFI_IPSEC_CONFIG_SELECTOR   *Selector;

+  EFI_IPSEC_CONFIG_DATA_TYPE  Type;

+  VOID                        *Data;

+  UINT8                       Value;

+  UINTN                       Size;

+

+  Value       = 0;

+  Size        = sizeof (Value);

+  BufferSize  = 0;

+  Buffer      = NULL;

+

+  Status = gRT->GetVariable (

+                  IPSECCONFIG_STATUS_NAME,

+                  &gEfiIpSecConfigProtocolGuid,

+                  NULL,

+                  &Size,

+                  &Value

+             );

+

+  if (!EFI_ERROR (Status) && Value == IPSEC_STATUS_ENABLED) {

+    Private->IpSec.DisabledFlag = FALSE;

+  }

+  //

+  // Get the real size of policy database in variable.

+  //

+  Status = IpSecGetVariable (

+             IPSECCONFIG_VARIABLE_NAME,

+             &gEfiIpSecConfigProtocolGuid,

+             NULL,

+             &BufferSize,

+             Buffer

+             );

+  if (Status == EFI_BUFFER_TOO_SMALL) {

+

+    Buffer = AllocateZeroPool (BufferSize);

+    if (Buffer == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+    //

+    // Get the content of policy database in variable.

+    //

+    Status = IpSecGetVariable (

+               IPSECCONFIG_VARIABLE_NAME,

+               &gEfiIpSecConfigProtocolGuid,

+               NULL,

+               &BufferSize,

+               Buffer

+               );

+    if (EFI_ERROR (Status)) {

+      FreePool (Buffer);

+      return Status;

+    }

+

+    for (Ptr = Buffer; Ptr < Buffer + BufferSize;) {

+

+      Header  = (IPSEC_VAR_ITEM_HEADER *) Ptr;

+      Type    = (EFI_IPSEC_CONFIG_DATA_TYPE) (Header->Type & IPSEC_VAR_ITEM_HEADER_CONTENT_BIT);

+      ASSERT (((Header->Type & 0x80) == IPSEC_VAR_ITEM_HEADER_LOGO_BIT) && (Type < IPsecConfigDataTypeMaximum));

+

+      Selector  = (EFI_IPSEC_CONFIG_SELECTOR *) ALIGN_POINTER (Header + 1, sizeof (UINTN));

+      Header    = (IPSEC_VAR_ITEM_HEADER *) ALIGN_POINTER (

+                                              (UINT8 *) Selector + Header->Size,

+                                              sizeof (UINTN)

+                                              );

+      ASSERT (Header->Type == Type);

+

+      Data = ALIGN_POINTER (Header + 1, sizeof (UINTN));

+

+      mUnfixPolicyEntry[Type](Selector, Data);

+

+      //

+      // Update each policy entry according to the content in variable.

+      //

+      mSetBySelf = TRUE;

+      Status = EfiIpSecConfigSetData (

+                 &Private->IpSecConfig,

+                 Type,

+                 Selector,

+                 Data,

+                 NULL

+                 );

+      mSetBySelf = FALSE;

+

+      if (EFI_ERROR (Status)) {

+        FreePool (Buffer);

+        return Status;

+      }

+

+      Ptr =  ALIGN_POINTER ((UINT8 *) Data + Header->Size, sizeof (UINTN));

+    }

+

+    FreePool (Buffer);

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Install and Initialize IPsecConfig protocol

+

+  @param[in, out]  Private   Pointer to IPSEC_PRIVATE_DATA. After this function finish,

+                             the pointer of IPsecConfig Protocol implementation will copy

+                             into its IPsecConfig member.

+

+  @retval     EFI_SUCCESS    Initialized the IPsecConfig Protocol successfully.

+  @retval     Others         Initializing the IPsecConfig Protocol failed.

+**/

+EFI_STATUS

+IpSecConfigInitialize (

+  IN OUT IPSEC_PRIVATE_DATA        *Private

+  )

+{

+  EFI_IPSEC_CONFIG_DATA_TYPE  Type;

+

+  CopyMem (

+    &Private->IpSecConfig,

+    &mIpSecConfigInstance,

+    sizeof (EFI_IPSEC_CONFIG_PROTOCOL)

+    );

+

+  //

+  // Initialize the list head of policy database.

+  //

+  for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {

+    InitializeListHead (&mConfigData[Type]);

+  }

+  //

+  // Restore the content of policy database according to the variable.

+  //

+  IpSecConfigRestore (Private);

+

+  return gBS->InstallMultipleProtocolInterfaces (

+                &Private->Handle,

+                &gEfiIpSecConfigProtocolGuid,

+                &Private->IpSecConfig,

+                NULL

+                );

+}

diff --git a/NetworkPkg/IpSecDxe/IpSecConfigImpl.h b/NetworkPkg/IpSecDxe/IpSecConfigImpl.h
new file mode 100644
index 0000000..54d43bd
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecConfigImpl.h
@@ -0,0 +1,952 @@
+/** @file

+  Definitions related to IPSEC_CONFIG_PROTOCOL implementations.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _IPSEC_CONFIG_IMPL_H_

+#define _IPSEC_CONFIG_IMPL_H_

+

+#include <Protocol/IpSec.h>

+#include <Protocol/IpSecConfig.h>

+

+#include <Library/BaseLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/PrintLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/UefiRuntimeServicesTableLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/DebugLib.h>

+

+#include "IpSecImpl.h"

+

+#define EFI_IPSEC_ANY_PROTOCOL    0xFFFF

+#define EFI_IPSEC_ANY_PORT        0

+

+#define IPSEC_VAR_ITEM_HEADER_LOGO_BIT     0x80

+#define IPSEC_VAR_ITEM_HEADER_CONTENT_BIT  0x7F

+

+#define IPSECCONFIG_VARIABLE_NAME       L"IpSecConfig"

+#define IPSECCONFIG_STATUS_NAME         L"IpSecStatus"

+

+#define SIZE_OF_SPD_SELECTOR(x) (UINTN) (sizeof (EFI_IPSEC_SPD_SELECTOR) \

+       + sizeof (EFI_IP_ADDRESS_INFO) * ((x)->LocalAddressCount + (x)->RemoteAddressCount))

+

+#define FIX_REF_BUF_ADDR(addr, base)    addr = (VOID *) ((UINTN) (addr) - (UINTN) (base))

+#define UNFIX_REF_BUF_ADDR(addr, base)  addr = (VOID *) ((UINTN) (addr) + (UINTN) (base))

+

+//

+// The data structure used to store the genernall information of IPsec configuration.

+//

+typedef struct {

+  UINT32 VariableCount;      // the total number of the IPsecConfig variables.

+  UINT32 VariableSize;       // The total size of all IpsecConfig variables.

+  UINT32 SingleVariableSize; // The max size of single variable

+} IP_SEC_VARIABLE_INFO;

+

+typedef struct {

+  EFI_IPSEC_CONFIG_SELECTOR *Selector;

+  VOID                      *Data;

+  LIST_ENTRY                List;

+} IPSEC_COMMON_POLICY_ENTRY;

+

+typedef struct {

+  UINT8 *Ptr;

+  UINTN Size;

+  UINTN Capacity;

+} IPSEC_VARIABLE_BUFFER;

+

+#pragma pack(1)

+typedef struct {

+  UINT8   Type;

+  UINT16  Size;

+} IPSEC_VAR_ITEM_HEADER;

+#pragma pack()

+

+/**

+  The prototype of Copy Source Selector to the Destination Selector.

+

+  @param[in out]  DstSel             Pointer of Destination Selector. It would be

+                                     SPD Selector, or SAD Selector or PAD Selector.

+  @param[in]      SrcSel             Pointer of Source  Selector. It would be

+                                     SPD Selector, or SAD Selector or PAD Selector.

+  @param[in out]  Size               The size of the Destination Selector. If it

+                                     is not NULL and its value is less than the size of

+                                     Source Selector, the value of Source Selector's

+                                     size will be passed to the caller by this parameter.

+

+  @retval EFI_INVALID_PARAMETER  If the Destination or Source Selector is NULL.

+  @retval EFI_BUFFER_TOO_SMALL   If the input Size is less than size of Source Selector.

+  @retval EFI_SUCCESS            Copy Source Selector to the Destination

+                                 Selector successfully.

+

+**/

+typedef

+EFI_STATUS

+(*IPSEC_DUPLICATE_SELECTOR) (

+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,

+  IN OUT UINTN                        *Size

+  );

+

+/**

+  It is prototype of compare two Selectors. The Selector would be SPD Selector,

+  or SAD Selector, or PAD selector.

+

+  @param[in]   Selector1           Pointer of the first  Selector.

+  @param[in]   Selector2           Pointer of the second Selector.

+

+  @retval  TRUE    These two Selectors have the same value in certain fields.

+  @retval  FALSE   Not all fields have the same value in these two Selectors.

+

+**/

+typedef

+BOOLEAN

+(*IPSEC_COMPARE_SELECTOR) (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2

+  );

+

+/**

+  The prototype of a function to check if the Selector is Zero by its certain fields.

+

+  @param[in]  Selector      Pointer of the Selector.

+

+  @retval     TRUE          If the Selector is Zero.

+  @retval     FALSE         If the Selector is not Zero.

+

+**/

+typedef

+BOOLEAN

+(*IPSEC_IS_ZERO_SELECTOR) (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector

+  );

+

+/**

+  The prototype of a function to fix the value of particular members of the Selector.

+

+  @param[in]  Selector              Pointer of Selector.

+  @param[in]  Data                  Pointer of Data.

+

+**/

+typedef

+VOID

+(*IPSEC_FIX_POLICY_ENTRY) (

+  IN EFI_IPSEC_CONFIG_SELECTOR           *Selector,

+  IN VOID                                *Data

+  );

+

+/**

+  It is prototype function to define a routine function by the caller of IpSecVisitConfigData().

+

+  @param[in]      Type              A specified IPSEC_CONFIG_DATA_TYPE.

+  @param[in]      Selector          Points to EFI_IPSEC_CONFIG_SELECTOR to be copied

+                                    to the buffer.

+  @param[in]      Data              Points to data to be copied to the buffer. The

+                                    Data type is related to the Type.

+  @param[in]      SelectorSize      The size of the Selector.

+  @param[in]      DataSize          The size of the Data.

+  @param[in out]  Buffer            The buffer to store the Selector and Data.

+

+  @retval EFI_SUCCESS            Copied the Selector and Data to a buffer successfully.

+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.

+

+**/

+typedef

+EFI_STATUS

+(*IPSEC_COPY_POLICY_ENTRY) (

+  IN     EFI_IPSEC_CONFIG_DATA_TYPE          Type,

+  IN     EFI_IPSEC_CONFIG_SELECTOR           *Selector,

+  IN     VOID                                *Data,

+  IN     UINTN                               SelectorSize,

+  IN     UINTN                               DataSize,

+  IN OUT VOID                                *Context

+  );

+

+/**

+  Set the security policy information for the EFI IPsec driver.

+

+  The IPsec configuration data has a unique selector/identifier separately to

+  identify a data entry.

+

+  @param[in]  Selector           Pointer to an entry selector on operated

+                                 configuration data specified by DataType.

+                                 A NULL Selector causes the entire specified-type

+                                 configuration information to be flushed.

+  @param[in]  Data               The data buffer to be set.

+  @param[in]  Context            Pointer to one entry selector that describes

+                                 the expected position the new data entry will

+                                 be added. If Context is NULL, the new entry will

+                                 be appended to the end of the database.

+

+  @retval EFI_INVALID_PARAMETER Certain Parameters are not correct. The Parameter

+                                requiring a check depends on the Selector type.

+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+

+**/

+typedef

+EFI_STATUS

+(*IPSEC_SET_POLICY_ENTRY) (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector,

+  IN VOID                             *Data,

+  IN VOID                             *Context OPTIONAL

+  );

+

+/**

+  A prototype function definition to lookup the data entry from IPsec. Return the configuration

+  value of the specified Entry.

+

+  @param[in]      Selector      Pointer to an entry selector that is an identifier

+                                of the  entry.

+  @param[in, out] DataSize      On output, the size of data returned in Data.

+  @param[out]     Data          The buffer to return the contents of the IPsec

+                                configuration data. The type of the data buffer

+                                is associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.

+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.

+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been

+                                updated with the size needed to complete the request.

+

+**/

+typedef

+EFI_STATUS

+(*IPSEC_GET_POLICY_ENTRY) (

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,

+  IN OUT UINTN                        *DataSize,

+  IN     VOID                         *Data

+  );

+

+/**

+  Compare two SPD Selectors.

+

+  Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/

+  NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the

+  Local Addresses and remote Addresses.

+

+  @param[in]   Selector1           Pointer of the first SPD Selector.

+  @param[in]   Selector2           Pointer of the second SPD Selector.

+

+  @retval  TRUE    These two Selectors have the same value in above fields.

+  @retval  FALSE   Not all of the above fields have the same value in these two Selectors.

+

+**/

+BOOLEAN

+CompareSpdSelector (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2

+  );

+

+

+/**

+  Visit all IPsec Configurations of specified Type and call the caller defined

+  interface.

+

+  @param[in]  DataType          The specified IPsec Config Data Type.

+  @param[in]  Routine           The function caller defined.

+  @param[in]  Context           The data passed to the Routine.

+

+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.

+  @retval EFI_SUCCESS            This function complete successfully.

+

+**/

+EFI_STATUS

+IpSecVisitConfigData (

+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,

+  IN IPSEC_COPY_POLICY_ENTRY          Routine,

+  IN VOID                             *Context

+  );

+

+

+/**

+  This function is the subfunction of the EFIIpSecConfigSetData.

+

+  This function call IpSecSetVaraible to set the IPsec Configuration into the firmware.

+

+  @retval EFI_OUT_OF_RESOURCES   The required system resource could not be allocated.

+  @retval EFI_SUCCESS            Saved the configration successfully.

+  @retval Others                 Other errors were found while obtaining the variable.

+

+**/

+EFI_STATUS

+IpSecConfigSave (

+  VOID

+  );

+

+/**

+  Initialize IPsecConfig protocol

+

+  @param[in, out]  Private   Pointer to IPSEC_PRIVATE_DATA. After this function finish,

+                             the pointer of IPsecConfig Protocol implementation will copy

+                             into its IPsecConfig member.

+

+  @retval     EFI_SUCCESS    Initialized the IPsecConfig Protocol successfully.

+  @retval     Others         Initializing the IPsecConfig Protocol failed.

+

+**/

+EFI_STATUS

+IpSecConfigInitialize (

+  IN OUT IPSEC_PRIVATE_DATA               *Private

+  );

+

+/**

+  Calculate the entire size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed

+  by the pointer members.

+

+  @param[in]  SpdData             Pointer to a specified EFI_IPSEC_SPD_DATA.

+

+  @return The entire size of the specified EFI_IPSEC_SPD_DATA.

+

+**/

+UINTN

+IpSecGetSizeOfEfiSpdData (

+  IN EFI_IPSEC_SPD_DATA               *SpdData

+  );

+

+/**

+  Calculate the a entire size of IPSEC_SPD_DATA, which includes the buffer size pointed

+  by the pointer members and the buffer size used by Sa List.

+

+  @param[in]  SpdData       Pointer to the specified IPSEC_SPD_DATA.

+

+  @return The entire size of IPSEC_SPD_DATA.

+

+**/

+UINTN

+IpSecGetSizeOfSpdData (

+  IN IPSEC_SPD_DATA                   *SpdData

+  );

+

+/**

+  Copy Source Process Policy to the Destination Process Policy.

+

+  @param[in]  Dst                  Pointer to the Source Process Policy.

+  @param[in]  Src                  Pointer to the Destination Process Policy.

+

+**/

+VOID

+IpSecDuplicateProcessPolicy (

+  IN EFI_IPSEC_PROCESS_POLICY            *Dst,

+  IN EFI_IPSEC_PROCESS_POLICY            *Src

+  );

+

+/**

+  Compare two SPD Selectors.

+

+  Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/

+  NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the

+  Local Addresses and remote Addresses.

+

+  @param[in]   Selector1           Pointer of the first SPD Selector.

+  @param[in]   Selector2           Pointer of the second SPD Selector.

+

+  @retval  TRUE    This two Selector have the same value in above fields.

+  @retval  FALSE   Not all of the above fields have the same value in these two Selectors.

+

+**/

+BOOLEAN

+CompareSpdSelector (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2

+  );

+

+/**

+  Compare two SA IDs.

+

+  @param[in]   Selector1           Pointer of the first SA ID.

+  @param[in]   Selector2           Pointer of the second SA ID.

+

+  @retval  TRUE    This two Selectors have the same SA ID.

+  @retval  FALSE   This two Selecotrs don't have the same SA ID.

+

+**/

+BOOLEAN

+CompareSaId (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2

+  );

+

+/**

+  Compare two PAD IDs.

+

+  @param[in]   Selector1           Pointer of the first PAD ID.

+  @param[in]   Selector2           Pointer of the second PAD ID.

+

+  @retval  TRUE    This two Selectors have the same PAD ID.

+  @retval  FALSE   This two Selecotrs don't have the same PAD ID.

+

+**/

+BOOLEAN

+ComparePadId (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector1,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector2

+  );

+

+/**

+  Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount

+  fields.

+

+  @param[in]  Selector      Pointer of the SPD Selector.

+

+  @retval     TRUE          If the SPD Selector is Zero.

+  @retval     FALSE         If the SPD Selector is not Zero.

+

+**/

+BOOLEAN

+IsZeroSpdSelector (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector

+  );

+

+/**

+  Check if the SA ID is Zero by its DestAddress.

+

+  @param[in]  Selector      Pointer of the SA ID.

+

+  @retval     TRUE          If the SA ID is Zero.

+  @retval     FALSE         If the SA ID is not Zero.

+

+**/

+BOOLEAN

+IsZeroSaId (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector

+  );

+

+/**

+  Check if the PAD ID is Zero.

+

+  @param[in]  Selector      Pointer of the PAD ID.

+

+  @retval     TRUE          If the PAD ID is Zero.

+  @retval     FALSE         If the PAD ID is not Zero.

+

+**/

+BOOLEAN

+IsZeroPadId (

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector

+  );

+

+/**

+  Copy Source SPD Selector to the Destination SPD Selector.

+

+  @param[in, out] DstSel             Pointer of Destination SPD Selector.

+  @param[in]      SrcSel             Pointer of Source SPD Selector.

+  @param[in, out] Size               The size of the Destination SPD Selector. If

+                                     it is not NULL and its value is less than the

+                                     size of Source SPD Selector, the value of

+                                     Source SPD Selector's size will be passed to

+                                     the caller by this parameter.

+

+  @retval EFI_INVALID_PARAMETER  If the Destination or Source SPD Selector is NULL.

+  @retval EFI_BUFFER_TOO_SMALL   If the input Size is less than size of Source SPD Selector.

+  @retval EFI_SUCCESS            Copy Source SPD Selector to the Destination SPD

+                                 Selector successfully.

+

+**/

+EFI_STATUS

+DuplicateSpdSelector (

+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,

+  IN OUT UINTN                        *Size

+  );

+

+/**

+  Copy Source SA ID to the Destination SA ID.

+

+  @param[in, out] DstSel             Pointer of the Destination SA ID.

+  @param[in]      SrcSel             Pointer of the Source SA ID.

+  @param[in, out] Size               The size of the Destination SA ID. If it

+                                     not NULL, and its value is less than the size of

+                                     Source SA ID, the value of Source SA ID's size

+                                     will be passed to the caller by this parameter.

+

+  @retval EFI_INVALID_PARAMETER  If the Destination or Source SA ID is NULL.

+  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source SA ID.

+  @retval EFI_SUCCESS            Copied Source SA ID to the Destination SA ID successfully.

+

+**/

+EFI_STATUS

+DuplicateSaId (

+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,

+  IN OUT UINTN                        *Size

+  );

+

+/**

+  Copy Source PAD ID to the Destination PAD ID.

+

+  @param[in, out] DstSel             Pointer of Destination PAD ID.

+  @param[in]      SrcSel             Pointer of Source PAD ID.

+  @param[in, out] Size               The size of the Destination PAD ID. If it

+                                     not NULL, and its value less than the size of

+                                     Source PAD ID, the value of Source PAD ID's size

+                                     will be passed to the caller by this parameter.

+

+  @retval EFI_INVALID_PARAMETER  If the Destination or Source PAD ID is NULL.

+  @retval EFI_BUFFER_TOO_SMALL   If the input Size less than size of source PAD ID.

+  @retval EFI_SUCCESS            Copied Source PAD ID to the Destination PAD ID successfully.

+

+**/

+EFI_STATUS

+DuplicatePadId (

+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *DstSel,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *SrcSel,

+  IN OUT UINTN                        *Size

+  );

+

+/**

+  Fix the value of some members of the  SPD Selector.

+

+  This function is called by IpSecCopyPolicyEntry(), which copies the Policy

+  Entry into the Variable. Since some members in SPD Selector are pointers,

+  a physical address to relative address conversion is required before copying

+  this SPD entry into the variable.

+

+  @param[in]       Selector              Pointer of SPD Selector.

+  @param[in, out]  Data                  Pointer of SPD Data.

+

+**/

+VOID

+FixSpdEntry (

+  IN     EFI_IPSEC_SPD_SELECTOR            *Selector,

+  IN OUT EFI_IPSEC_SPD_DATA                *Data

+  );

+

+/**

+  Fix the value of some members of SA ID.

+

+  This function is called by IpSecCopyPolicyEntry(), which copies the Policy

+  Entry into the Variable. Since some members in SA ID are pointers,

+  a physical address to relative address conversion is required before copying

+  this SAD into the variable.

+

+  @param[in]       SaId              Pointer of SA ID.

+  @param[in, out]  Data              Pointer of SA Data.

+

+**/

+VOID

+FixSadEntry (

+  IN     EFI_IPSEC_SA_ID                  *SaId,

+  IN OUT EFI_IPSEC_SA_DATA                *Data

+  );

+

+/**

+  Fix the value of some members of PAD ID.

+

+  This function is called by IpSecCopyPolicyEntry(), which copy the Policy

+  Entry into the Variable. Since some members in PAD ID are pointers,

+  a physical address to relative address conversion is required before copying

+  this PAD into the variable.

+

+  @param[in]       PadId              Pointer of PAD ID.

+  @param[in, out]  Data               Pointer of PAD Data.

+

+**/

+VOID

+FixPadEntry (

+  IN     EFI_IPSEC_PAD_ID                  *PadId,

+  IN OUT EFI_IPSEC_PAD_DATA                *Data

+  );

+

+/**

+  Recover the value of some members of SPD Selector.

+

+  This function is corresponding to FixSpdEntry(). It recovers the value of members

+  of SPD Selector which fix by the FixSpdEntry().

+

+  @param[in, out]  Selector              Pointer of SPD Selector.

+  @param[in, out]  Data                  Pointer of SPD Data.

+

+**/

+VOID

+UnfixSpdEntry (

+  IN OUT EFI_IPSEC_SPD_SELECTOR           *Selector,

+  IN OUT EFI_IPSEC_SPD_DATA               *Data

+  );

+

+

+/**

+  Recover the value of some members of SA ID.

+

+  This function is corresponding to FixSadEntry(). It recovers the value of members

+  of SAD ID which fix by the FixSadEntry().

+

+  @param[in, out]       SaId              Pointer of SAD ID

+  @param[in, out]  Data              Pointer of SAD Data.

+

+**/

+VOID

+UnfixSadEntry (

+  IN OUT EFI_IPSEC_SA_ID                     *SaId,

+  IN OUT EFI_IPSEC_SA_DATA                   *Data

+  );

+

+/**

+  Recover the value of some members of PAD ID.

+

+  This function is corresponding to FixPadEntry(). It recovers the value of members

+  of PAD ID which fix by the FixPadEntry().

+

+  @param[in]       PadId              Pointer of PAD ID

+  @param[in, out]  Data               Pointer of PAD Data.

+

+**/

+VOID

+UnfixPadEntry (

+  IN     EFI_IPSEC_PAD_ID                 *PadId,

+  IN OUT EFI_IPSEC_PAD_DATA               *Data

+  );

+

+/**

+  Set the security policy information for the EFI IPsec driver.

+

+  The IPsec configuration data has a unique selector/identifier separately to

+  identify a data entry.

+

+  @param[in]  Selector           Pointer to an entry selector on operated

+                                 configuration data specified by DataType.

+                                 A NULL Selector causes the entire specified-type

+                                 configuration information to be flushed.

+  @param[in]  Data               The data buffer to be set. The structure

+                                 of the data buffer should be EFI_IPSEC_SPD_DATA.

+  @param[in]  Context            Pointer to one entry selector that describes

+                                 the expected position the new data entry will

+                                 be added. If Context is NULL,the new entry will

+                                 be appended the end of database.

+

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                   - Selector is not NULL and its LocalAddress

+                                     is NULL or its RemoteAddress is NULL.

+                                   - Data is not NULL, its Action is Protected,

+                                     and its policy is NULL.

+                                   - Data is not NULL and its Action is not protected

+                                     and its policy is not NULL.

+                                   - The Action of Data is Protected, its policy

+                                     mode is Tunnel, and its tunnel option is NULL.

+                                   - The Action of Data is protected, its policy

+                                     mode is not Tunnel, and it tunnel option is not NULL.

+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+

+**/

+EFI_STATUS

+SetSpdEntry (

+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,

+  IN VOID                            *Data,

+  IN VOID                            *Context OPTIONAL

+  );

+

+/**

+  Set the security association information for the EFI IPsec driver.

+

+  The IPsec configuration data has a unique selector/identifier separately to

+  identify a data entry.

+

+  @param[in]  Selector           Pointer to an entry selector on operated

+                                 configuration data specified by DataType.

+                                 A NULL Selector causes the entire specified-type

+                                 configuration information to be flushed.

+  @param[in]  Data               The data buffer to be set. The structure

+                                 of the data buffer should be EFI_IPSEC_SA_DATA.

+  @param[in]  Context            Pointer to one entry selector which describes

+                                 the expected position the new data entry will

+                                 be added. If Context is NULL,the new entry will

+                                 be appended to the end of database.

+

+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+

+**/

+EFI_STATUS

+SetSadEntry (

+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,

+  IN VOID                            *Data,

+  IN VOID                            *Context OPTIONAL

+  );

+

+/**

+  Set the peer authorization configuration information for the EFI IPsec driver.

+

+  The IPsec configuration data has a unique selector/identifier separately to

+  identify a data entry.

+

+  @param[in]  Selector           Pointer to an entry selector on operated

+                                 configuration data specified by DataType.

+                                 A NULL Selector causes the entire specified-type

+                                 configuration information to be flushed.

+  @param[in]  Data               The data buffer to be set. The structure

+                                 of the data buffer should be EFI_IPSEC_PAD_DATA.

+  @param[in]  Context            Pointer to one entry selector that describes

+                                 the expected position where the new data entry will

+                                 be added. If Context is NULL, the new entry will

+                                 be appended the end of database.

+

+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+

+**/

+EFI_STATUS

+SetPadEntry (

+  IN EFI_IPSEC_CONFIG_SELECTOR       *Selector,

+  IN VOID                            *Data,

+  IN VOID                            *Context OPTIONAL

+  );

+

+/**

+  This function looks up the data entry from IPsec SPD, and returns the configuration

+  value of the specified SPD Entry.

+

+  @param[in]      Selector      Pointer to an entry selector which is an identifier

+                                of the SPD entry.

+  @param[in, out] DataSize      On output the size of data returned in Data.

+  @param[out]     Data          The buffer to return the contents of the IPsec

+                                configuration data. The type of the data buffer

+                                is associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.

+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.

+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been

+                                updated with the size needed to complete the request.

+

+**/

+EFI_STATUS

+GetSpdEntry (

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,

+  IN OUT UINTN                        *DataSize,

+     OUT VOID                         *Data

+  );

+

+/**

+  This function looks up the data entry from IPsec SAD and returns the configuration

+  value of the specified SAD Entry.

+

+  @param[in]      Selector      Pointer to an entry selector that is an identifier

+                                of the SAD entry.

+  @param[in, out] DataSize      On output, the size of data returned in Data.

+  @param[out]     Data          The buffer to return the contents of the IPsec

+                                configuration data. This type of the data buffer

+                                is associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.

+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been

+                                updated with the size needed to complete the request.

+

+**/

+EFI_STATUS

+GetSadEntry (

+  IN     EFI_IPSEC_CONFIG_SELECTOR   *Selector,

+  IN OUT UINTN                       *DataSize,

+     OUT VOID                        *Data

+  );

+

+/**

+  This function looks up the data entry from IPsec PADand returns the configuration

+  value of the specified PAD Entry.

+

+  @param[in]      Selector      Pointer to an entry selector that  is an identifier

+                                of the PAD entry.

+  @param[in, out] DataSize      On output the size of data returned in Data.

+  @param[out]     Data          The buffer to return the contents of the IPsec

+                                configuration data. This type of the data buffer

+                                is associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.

+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been

+                                updated with the size needed to complete the request.

+

+**/

+EFI_STATUS

+GetPadEntry (

+  IN     EFI_IPSEC_CONFIG_SELECTOR   *Selector,

+  IN OUT UINTN                       *DataSize,

+     OUT VOID                        *Data

+  );

+

+/**

+  Return the configuration value for the EFI IPsec driver.

+

+  This function lookup the data entry from IPsec database or IKEv2 configuration

+  information. The expected data type and unique identification are described in

+  DataType and Selector parameters.

+

+  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in]      DataType      The type of data to retrieve.

+  @param[in]      Selector      Pointer to an entry selector that is an identifier of the IPsec

+                                configuration data entry.

+  @param[in, out] DataSize      On output the size of data returned in Data.

+  @param[out]     Data          The buffer to return the contents of the IPsec configuration data.

+                                The type of the data buffer is associated with the DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:

+                                - This is NULL.

+                                - Selector is NULL.

+                                - DataSize is NULL.

+                                - Data is NULL and *DataSize is not zero

+  @retval EFI_NOT_FOUND         The configuration data specified by Selector is not found.

+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.

+  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the result. DataSize has been

+                                updated with the size needed to complete the request.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigGetData (

+  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,

+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,

+  IN     EFI_IPSEC_CONFIG_SELECTOR    *Selector,

+  IN OUT UINTN                        *DataSize,

+     OUT VOID                         *Data

+  );

+

+/**

+  Set the security association, security policy and peer authorization configuration

+  information for the EFI IPsec driver.

+

+  This function is used to set the IPsec configuration information of type DataType for

+  the EFI IPsec driver.

+  The IPsec configuration data has a unique selector/identifier separately to identify

+  a data entry. The selector structure depends on DataType's definition.

+  Using SetData() with a Data of NULL causes the IPsec configuration data entry identified

+  by DataType and Selector to be deleted.

+

+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in] DataType           The type of data to be set.

+  @param[in] Selector           Pointer to an entry selector on operated configuration data

+                                specified by DataType. A NULL Selector causes the entire

+                                specified-type configuration information to be flushed.

+  @param[in] Data               The data buffer to be set. The structure of the data buffer is

+                                associated with the DataType.

+  @param[in] InsertBefore       Pointer to one entry selector which describes the expected

+                                position the new data entry will be added. If InsertBefore is NULL,

+                                the new entry will be appended the end of database.

+

+  @retval EFI_SUCCESS           The specified configuration entry data was set successfully.

+  @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:

+                                - This is NULL.

+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.

+  @retval EFI_OUT_OF_RESOURCED  The required system resource could not be allocated.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigSetData (

+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,

+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *Selector,

+  IN VOID                             *Data,

+  IN EFI_IPSEC_CONFIG_SELECTOR        *InsertBefore OPTIONAL

+  );

+

+/**

+  Enumerates the current selector for IPsec configuration data entry.

+

+  This function is called multiple times to retrieve the entry Selector in IPsec

+  configuration database. On each call to GetNextSelector(), the next entry

+  Selector are retrieved into the output interface.

+

+  If the entire IPsec configuration database has been iterated, the error

+  EFI_NOT_FOUND is returned.

+  If the Selector buffer is too small for the next Selector copy, an

+  EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect

+  the size of buffer needed.

+

+  On the initial call to GetNextSelector() to start the IPsec configuration database

+  search, a pointer to the buffer with all zero value is passed in Selector. Calls

+  to SetData() between calls to GetNextSelector may produce unpredictable results.

+

+  @param[in]      This          Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in]      DataType      The type of IPsec configuration data to retrieve.

+  @param[in, out] SelectorSize  The size of the Selector buffer.

+  @param[in, out] Selector      On input, supplies the pointer to last Selector that was

+                                returned by GetNextSelector().

+                                On output, returns one copy of the current entry Selector

+                                of a given DataType.

+

+  @retval EFI_SUCCESS           The specified configuration data was obtained successfully.

+  @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:

+                                - This is NULL.

+                                - SelectorSize is NULL.

+                                - Selector is NULL.

+  @retval EFI_NOT_FOUND         The next configuration data entry was not found.

+  @retval EFI_UNSUPPORTED       The specified DataType is not supported.

+  @retval EFI_BUFFER_TOO_SMALL  The SelectorSize is too small for the result. This parameter

+                                has been updated with the size needed to complete the search

+                                request.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigGetNextSelector (

+  IN     EFI_IPSEC_CONFIG_PROTOCOL    *This,

+  IN     EFI_IPSEC_CONFIG_DATA_TYPE   DataType,

+  IN OUT UINTN                        *SelectorSize,

+  IN OUT EFI_IPSEC_CONFIG_SELECTOR    *Selector

+  );

+

+/**

+  Register an event that is to be signaled whenever a configuration process on the

+  specified IPsec configuration information is done.

+

+  The register function is not surpport now and always returns EFI_UNSUPPORTED.

+

+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in] DataType           The type of data to be registered the event for.

+  @param[in] Event              The event to be registered.

+

+  @retval EFI_SUCCESS           The event is registered successfully.

+  @retval EFI_INVALID_PARAMETER This is NULL, or Event is NULL.

+  @retval EFI_ACCESS_DENIED     The Event is already registered for the DataType.

+  @retval EFI_UNSUPPORTED       The notify registration unsupported, or the specified

+                                DataType is not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigRegisterNotify (

+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,

+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,

+  IN EFI_EVENT                        Event

+  );

+

+

+/**

+  Remove the specified event that was previously registered on the specified IPsec

+  configuration data.

+

+  This function is not supported now and always returns EFI_UNSUPPORTED.

+

+  @param[in] This               Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.

+  @param[in] DataType           The configuration data type to remove the registered event for.

+  @param[in] Event              The event to be unregistered.

+

+  @retval EFI_SUCCESS           The event was removed successfully.

+  @retval EFI_NOT_FOUND         The Event specified by DataType could not be found in the

+                                database.

+  @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.

+  @retval EFI_UNSUPPORTED       The notify registration unsupported or the specified

+                                DataType is not supported.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiIpSecConfigUnregisterNotify (

+  IN EFI_IPSEC_CONFIG_PROTOCOL        *This,

+  IN EFI_IPSEC_CONFIG_DATA_TYPE       DataType,

+  IN EFI_EVENT                        Event

+  );

+

+#endif

diff --git a/NetworkPkg/IpSecDxe/IpSecCryptIo.c b/NetworkPkg/IpSecDxe/IpSecCryptIo.c
new file mode 100644
index 0000000..7011f98
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecCryptIo.c
@@ -0,0 +1,133 @@
+/** @file

+  Common operation for Security.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecCryptIo.h"

+//

+// Alogrithm's informations for the Encrypt/Decrpt Alogrithm.

+//

+ENCRYPT_ALGORITHM mIpsecEncryptAlgorithmList[IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE] = {

+  {EFI_IPSEC_EALG_NULL, 0, 0, 1, NULL, NULL, NULL, NULL},

+  {(UINT8)-1,           0, 0, 0, NULL, NULL, NULL, NULL}

+};

+//

+// Alogrithm's informations for the Authentication algorithm

+//

+AUTH_ALGORITHM mIpsecAuthAlgorithmList[IPSEC_AUTH_ALGORITHM_LIST_SIZE] = {

+  {EFI_IPSEC_AALG_NONE, 0, 0, 0, NULL, NULL, NULL, NULL},

+  {EFI_IPSEC_AALG_NULL, 0, 0, 0, NULL, NULL, NULL, NULL},

+  {(UINT8)-1,           0, 0, 0, NULL, NULL, NULL, NULL}

+};

+

+

+/**

+  Get the block size of encrypt alogrithm. The block size is based on the algorithm used.

+

+  @param[in]  AlgorithmId          The encrypt algorithm ID.

+

+  @return The value of block size.

+

+**/

+UINTN

+IpSecGetEncryptBlockSize (

+  IN UINT8   AlgorithmId

+  )

+{

+  UINT8 Index;

+

+  for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) {

+    if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) {

+      //

+      // The BlockSize is same with IvSize.

+      //

+      return mIpsecEncryptAlgorithmList[Index].BlockSize;

+    }

+  }

+

+  return (UINTN) -1;

+}

+

+/**

+  Get the IV size of encrypt alogrithm. The IV size is based on the algorithm used.

+

+  @param[in]  AlgorithmId          The encrypt algorithm ID.

+

+  @return The value of IV size.

+

+**/

+UINTN

+IpSecGetEncryptIvLength (

+  IN UINT8 AlgorithmId

+  )

+{

+  UINT8 Index;

+

+  for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) {

+    if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) {

+      //

+      // The BlockSize is same with IvSize.

+      //

+      return mIpsecEncryptAlgorithmList[Index].IvLength;

+    }

+  }

+

+  return (UINTN) -1;

+}

+

+/**

+  Get the ICV size of Authenticaion alogrithm. The ICV size is based on the algorithm used.

+

+  @param[in]  AuthAlgorithmId          The Authentication algorithm ID.

+

+  @return The value of ICV size.

+

+**/

+UINTN

+IpSecGetIcvLength (

+  IN UINT8  AuthAlgorithmId

+  )

+{

+  UINT8 Index;

+  for (Index = 0; Index < IPSEC_AUTH_ALGORITHM_LIST_SIZE; Index++) {

+    if (AuthAlgorithmId == mIpsecAuthAlgorithmList[Index].AlgorithmId) {

+      return mIpsecAuthAlgorithmList[Index].IcvLength;

+    }

+  }

+  return (UINTN) -1;

+}

+

+/**

+  Generate a random data for IV. If the IvSize is zero, not needed to create

+  IV and return EFI_SUCCESS.

+

+  @param[in]  IvBuffer  The pointer of the IV buffer.

+  @param[in]  IvSize    The IV size.

+

+  @retval     EFI_SUCCESS  Create a random data for IV.

+

+**/

+EFI_STATUS

+IpSecGenerateIv (

+  IN UINT8                           *IvBuffer,

+  IN UINTN                           IvSize

+  )

+{

+  if (IvSize != 0) {

+    //

+    //TODO: return CryptGenerateRandom (IvBuffer, IvSize);

+    //

+    return EFI_SUCCESS;

+  }

+  return EFI_SUCCESS;

+}

diff --git a/NetworkPkg/IpSecDxe/IpSecCryptIo.h b/NetworkPkg/IpSecDxe/IpSecCryptIo.h
new file mode 100644
index 0000000..d883a2e
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecCryptIo.h
@@ -0,0 +1,322 @@
+/** @file

+  Definition related to the Security operation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _EFI_IPSEC_CRYPTIO_H_

+#define _EFI_IPSEC_CRYPTIO_H_

+

+#include <Protocol/IpSecConfig.h>

+#include <Library/DebugLib.h>

+

+#define IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE 2

+#define IPSEC_AUTH_ALGORITHM_LIST_SIZE    3

+

+/**

+  Prototype of Hash GetContextSize.

+

+  Retrieves the size, in bytes, of the context buffer required.

+

+  @return  The size, in bytes, of the context buffer required.

+

+**/

+typedef

+UINTN

+(EFIAPI *CPL_HASH_GETCONTEXTSIZE) (

+  VOID

+  );

+

+/**

+  Prototype of Hash Operation Initiating.

+

+  Initialization with a new context.

+

+

+  @param[in,out]  Context  Input Context.

+

+  @retval TRUE  Initialization Successfully.

+

+**/

+typedef

+EFI_STATUS

+(EFIAPI *CPL_HASH_INIT) (

+  IN OUT  VOID     *Context

+  );

+

+/**

+  Prototype of HASH update.

+  Hash update operation. Continue an Hash message digest operation, processing

+  another message block, and updating the Hash context.

+

+  If Context is NULL, then ASSERT().

+  If Data is NULL, then ASSERT().

+

+  @param[in,out]  Context     The Specified Context.

+  @param[in,out]  Data        The Input Data to hash.

+  @param[in]      DataLength  The length, in bytes, of Data.

+

+  @retval TRUE   Update data successfully.

+  @retval FALSE  The Context has been finalized.

+

+**/

+typedef

+BOOLEAN

+(EFIAPI *CPL_HASH_UPDATE) (

+  IN OUT       VOID  *Context,

+  IN     CONST VOID  *Data,

+  IN           UINTN DataLength

+  );

+

+/**

+  Prototype of Hash finallization.

+  Terminate a Hash message digest operation and output the message digest.

+

+  If Context is NULL, then ASSERT().

+  If HashValue is NULL, then ASSERT().

+

+  @param[in,out]  Context     The specified Context.

+  @param[out]     HashValue   Pointer to a 16-byte message digest output buffer.

+

+  @retval TRUE  Finalized successfully.

+

+**/

+typedef

+BOOLEAN

+(EFIAPI *CPL_HASH_FINAL) (

+  IN OUT  VOID   *Context,

+     OUT  UINT8  *HashValue

+  );

+

+/**

+  Prototype of Cipher GetContextSize.

+

+  Retrieves the size, in bytes, of the context buffer required.

+

+  @return  The size, in bytes, of the context buffer required.

+

+**/

+typedef

+UINTN

+(EFIAPI *CPL_CIPHER_GETCONTEXTSIZE) (

+  VOID

+  );

+

+/**

+  Prototype of Cipher initiation.

+  Intializes the user-supplied key as the specifed context (key materials) for both

+  encryption and decryption operations.

+

+  If Context is NULL, then ASSERT().

+  If Key is NULL, then generate random key for usage.

+

+  @param[in,out]  Context      The specified Context.

+  @param[in]      Key          User-supplied TDES key (64/128/192 bits).

+  @param[in]      KeyBits      Key length in bits.

+

+  @retval TRUE  TDES Initialization was successful.

+

+**/

+typedef

+BOOLEAN

+(EFIAPI *CPL_CIPHER_INIT) (

+  IN OUT        VOID   *Context,

+  IN      CONST UINT8  *Key,

+  IN      CONST UINTN  KeyBits

+  );

+

+

+/**

+  Prototype of Cipher encryption.

+  Encrypts plaintext message with the specified cipher.

+

+  If Context is NULL, then ASSERT().

+  if InData is NULL, then ASSERT().

+  If Size of input data is not multiple of Cipher algorithm related block size,

+  then ASSERT().

+

+  @param[in]      Context      The specified Context.

+  @param[in]      InData       The input plaintext data to be encrypted.

+  @param[out]     OutData      The resultant encrypted ciphertext.

+  @param[in]      DataLength   Length of input data in bytes.

+

+  @retval TRUE  Encryption successful.

+

+**/

+typedef

+BOOLEAN

+(EFIAPI *CPL_CIPHER_ENCRYPT) (

+  IN            VOID   *Context,

+  IN      CONST UINT8  *InData,

+      OUT       UINT8  *OutData,

+  IN      CONST UINTN  DataLength

+  );

+

+

+/**

+  Prototype of Cipher decryption.

+  Decrypts cipher message with specified cipher.

+

+  If Context is NULL, then ASSERT().

+  if InData is NULL, then ASSERT().

+  If Size of input data is not a multiple of a certaion block size , then ASSERT().

+

+  @param[in]      Context      The specified Context.

+  @param[in]      InData       The input ciphertext data to be decrypted.

+  @param[out]     OutData      The resultant decrypted plaintext.

+  @param[in]      DataLength   Length of input data in bytes.

+

+  @retval TRUE  Decryption successful.

+

+**/

+typedef

+BOOLEAN

+(EFIAPI *CPL_CIPHER_DECRYPT) (

+  IN     CONST VOID   *Context,

+  IN     CONST UINT8  *InData,

+     OUT       UINT8  *OutData,

+  IN     CONST UINTN  DataLength

+  );

+

+//

+// The struct used to store the informatino and operation of  Cipher algorithm.

+//

+typedef struct _ENCRYPT_ALGORITHM {

+//

+// The ID of the Algorithm

+//

+UINT8                     AlgorithmId;

+//

+// The Key length of the Algorithm

+//

+UINTN                     KeyLength;

+//

+// Iv Size of the Algorithm

+//

+UINTN                     IvLength;

+//

+// The Block Size of the Algorithm

+//

+UINTN                     BlockSize;

+//

+// The Function pointer of GetContextSize.

+//

+CPL_CIPHER_GETCONTEXTSIZE CipherGetContextSize;

+//

+// The Function pointer of Cipher intitiaion.

+//

+CPL_CIPHER_INIT           CipherInitiate;

+//

+// The Function pointer of Cipher Encryption.

+//

+CPL_CIPHER_ENCRYPT        CipherEncrypt;

+//

+// The Function pointer of Cipher Decrption.

+//

+CPL_CIPHER_DECRYPT        CipherDecrypt;

+} ENCRYPT_ALGORITHM;

+

+//

+// The struct used to store the informatino and operation of  Autahentication algorithm.

+//

+typedef struct _AUTH_ALGORITHM {

+  //

+  // ID of the Algorithm

+  //

+  UINT8                    AlgorithmId;

+  //

+  // The Key length of the Algorithm

+  //

+  UINTN                    KeyLength;

+  //

+  // The ICV length of the Algorithm

+  //

+  UINTN                    IcvLength;

+  //

+  // The block size of the Algorithm

+  //

+  UINTN                    BlockSize;

+  //

+  // The function pointer of GetContextSize.

+  //

+  CPL_HASH_GETCONTEXTSIZE  HashGetContextSize;

+  //

+  // The function pointer of Initiatoion

+  //

+  CPL_HASH_INIT            HashInitiate;

+  //

+  // The function pointer of Hash Update.

+  //

+  CPL_HASH_UPDATE          HashUpdate;

+  //

+  // The fucntion pointer of Hash Final

+  //

+  CPL_HASH_FINAL           HashFinal;

+} AUTH_ALGORITHM;

+

+/**

+  Get the IV size of encrypt alogrithm. IV size is different from different algorithm.

+

+  @param[in]  AlgorithmId          The encrypt algorithm ID.

+

+  @return The value of IV size.

+

+**/

+UINTN

+IpSecGetEncryptIvLength (

+  IN UINT8 AlgorithmId

+  );

+

+/**

+  Get the block size of encrypt alogrithm. Block size is different from different algorithm.

+

+  @param[in]  AlgorithmId          The encrypt algorithm ID.

+

+  @return The value of block size.

+

+**/

+UINTN

+IpSecGetEncryptBlockSize (

+  IN UINT8   AlgorithmId

+  );

+

+/**

+  Get the ICV size of Authenticaion alogrithm. ICV size is different from different algorithm.

+

+  @param[in]  AuthAlgorithmId          The Authentication algorithm ID.

+

+  @return The value of ICV size.

+

+**/

+UINTN

+IpSecGetIcvLength (

+  IN UINT8  AuthAlgorithmId

+  );

+

+/**

+  Generate a random data for IV. If the IvSize is zero, not needed to create

+  IV and return EFI_SUCCESS.

+

+  @param[in]  IvBuffer  The pointer of the IV buffer.

+  @param[in]  IvSize    The IV size.

+

+  @retval     EFI_SUCCESS  Create random data for IV.

+

+**/

+EFI_STATUS

+IpSecGenerateIv (

+  IN UINT8                           *IvBuffer,

+  IN UINTN                           IvSize

+  );

+

+#endif

+

diff --git a/NetworkPkg/IpSecDxe/IpSecDebug.c b/NetworkPkg/IpSecDxe/IpSecDebug.c
new file mode 100644
index 0000000..8a5811b
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecDebug.c
@@ -0,0 +1,172 @@
+/** @file

+  Interface of IPsec printing debug information.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecImpl.h"

+#include "IpSecDebug.h"

+

+//

+// The print title for IKEv1 variety phase.

+//

+CHAR8 *mStateStr[] = {

+  "IKEv1_MAIN_1",

+  "IKEv1_MAIN_2",

+  "IKEv1_MAIN_3",

+  "IKEv1_MAIN_ESTABLISHED",

+  "IKEv1_QUICK_1",

+  "IKEv1_QUICK_2",

+  "IKEv1_QUICK_ESTABLISHED"

+};

+//

+// The print title for IKEv1 variety Exchagne.

+//

+CHAR8 *mExchangeStr[] = {

+  "IKEv1 Main Exchange",

+  "IKEv1 Info Exchange",

+  "IKEv1 Quick Exchange",

+  "IKEv1 Unknown Exchange"

+};

+

+//

+// The print title for IKEv1 variety Payload.

+//

+CHAR8 *mPayloadStr[] = {

+  "IKEv1 None Payload",

+  "IKEv1 SA Payload",

+  "IKEv1 Proposal Payload",

+  "IKEv1 Transform Payload",

+  "IKEv1 KE Payload",

+  "IKEv1 ID Payload",

+  "IKEv1 Certificate Payload",

+  "IKEv1 Certificate Request Payload",

+  "IKEv1 Hash Payload",

+  "IKEv1 Signature Payload",

+  "IKEv1 Nonce Payload",

+  "IKEv1 Notify Payload",

+  "IKEv1 Delete Payload",

+  "IKEv1 Vendor Payload"

+};

+

+/**

+  Print the IP address.

+

+  @param[in]  Level     Debug print error level. Pass to DEBUG().

+  @param[in]  Ip        Point to a specified IP address.

+  @param[in]  IpVersion The IP Version.

+

+**/

+VOID

+IpSecDumpAddress (

+  IN UINTN               Level,

+  IN EFI_IP_ADDRESS      *Ip,

+  IN UINT8               IpVersion

+  )

+{

+  if (IpVersion == IP_VERSION_6) {

+    DEBUG (

+      (Level,

+      "%x%x:%x%x:%x%x:%x%x",

+      Ip->v6.Addr[0],

+      Ip->v6.Addr[1],

+      Ip->v6.Addr[2],

+      Ip->v6.Addr[3],

+      Ip->v6.Addr[4],

+      Ip->v6.Addr[5],

+      Ip->v6.Addr[6],

+      Ip->v6.Addr[7])

+      );

+    DEBUG (

+      (Level,

+      ":%x%x:%x%x:%x%x:%x%x\n",

+      Ip->v6.Addr[8],

+      Ip->v6.Addr[9],

+      Ip->v6.Addr[10],

+      Ip->v6.Addr[11],

+      Ip->v6.Addr[12],

+      Ip->v6.Addr[13],

+      Ip->v6.Addr[14],

+      Ip->v6.Addr[15])

+      );

+  } else {

+    DEBUG (

+      (Level,

+      "%d.%d.%d.%d\n",

+      Ip->v4.Addr[0],

+      Ip->v4.Addr[1],

+      Ip->v4.Addr[2],

+      Ip->v4.Addr[3])

+      );

+  }

+

+}

+

+/**

+  Print IKEv1 Current states.

+

+  @param[in]  Previous    The Previous state of IKEv1.

+  @param[in]  Current     The current state of IKEv1.

+

+**/

+VOID

+IpSecDumpState (

+  IN UINT32              Previous,

+  IN UINT32              Current

+  )

+{

+  if (Previous == Current) {

+    DEBUG ((DEBUG_INFO, "\n****Current state is %a\n", mStateStr[Previous]));

+  } else {

+    DEBUG ((DEBUG_INFO, "\n****Change state from %a to %a\n", mStateStr[Previous], mStateStr[Current]));

+  }

+

+}

+

+/**

+  Print the buffer in form of Hex.

+

+  @param[in]  Title       The strings to be printed before the data of the buffer.

+  @param[in]  Data        Points to buffer to be printed.

+  @param[in]  DataSize    The size of the buffer to be printed.

+

+**/

+VOID

+IpSecDumpBuf (

+  IN CHAR8                 *Title,

+  IN UINT8                 *Data,

+  IN UINTN                 DataSize

+  )

+{

+  UINTN Index;

+  UINTN DataIndex;

+  UINTN BytesRemaining;

+  UINTN BytesToPrint;

+

+  DataIndex       = 0;

+  BytesRemaining  = DataSize;

+

+  DEBUG ((DEBUG_INFO, "==%a %d bytes==\n", Title, DataSize));

+

+  while (BytesRemaining > 0) {

+

+    BytesToPrint = (BytesRemaining > IPSEC_DEBUG_BYTE_PER_LINE) ? IPSEC_DEBUG_BYTE_PER_LINE : BytesRemaining;

+

+    for (Index = 0; Index < BytesToPrint; Index++) {

+      DEBUG ((DEBUG_INFO, " 0x%02x,", Data[DataIndex++]));

+    }

+

+    DEBUG ((DEBUG_INFO, "\n"));

+    BytesRemaining -= BytesToPrint;

+  }

+

+}

diff --git a/NetworkPkg/IpSecDxe/IpSecDebug.h b/NetworkPkg/IpSecDxe/IpSecDebug.h
new file mode 100644
index 0000000..0e6e681
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecDebug.h
@@ -0,0 +1,102 @@
+/** @file

+  The definition of functions and MACROs used for IPsec debug information print.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _EFI_IPSEC_DEBUG_H_

+#define _EFI_IPSEC_DEBUG_H_

+

+#include <Library/DebugLib.h>

+

+#define IPSEC_DUMP_ADDRESS(Level, Ip, Version)           IpSecDumpAddress (Level, Ip, Version)

+#define IPSEC_DUMP_STATE(Previous, Current)              IpSecDumpState (Previous, Current)

+#define IPSEC_DUMP_PACKET(Packet, Direction, IpVersion)  IpSecDumpPacket (Packet, Direction, IpVersion)

+#define IPSEC_DUMP_PAYLOAD(IkePayload)                   IpSecDumpPayload (IkePayload)

+#define IPSEC_DUMP_BUF(Title, Data, DataSize)            IpSecDumpBuf (Title, Data, DataSize)

+

+#define IPSEC_DEBUG_BYTE_PER_LINE                       8

+

+

+/**

+  Print the IP address.

+

+  @param[in]  Level     Debug print error level. Pass to DEBUG().

+  @param[in]  Ip        Point to specified IP address.

+  @param[in]  IpVersion The IP Version.

+

+**/

+VOID

+IpSecDumpAddress (

+  IN UINTN               Level,

+  IN EFI_IP_ADDRESS      *Ip,

+  IN UINT8               IpVersion

+  );

+

+/**

+  Print IKEv1 Current states.

+

+  @param[in]  Previous    The Previous state of IKEv1.

+  @param[in]  Current     The current state of IKEv1.

+

+**/

+VOID

+IpSecDumpState (

+  IN UINT32              Previous,

+  IN UINT32              Current

+  );

+

+/**

+  Print the Ike Packet.

+

+  @param[in]  Packet      Point to IKE packet to be printed.

+  @param[in]  Direction   Point to the IKE packet is inbound or outbound.

+  @param[in]  IpVersion   Specified IP Version.

+

+**/

+/*

+VOID

+IpSecDumpPacket (

+  IN IKE_PACKET            *Packet,

+  IN EFI_IPSEC_TRAFFIC_DIR Direction,

+  IN UINT8                 IpVersion

+  );

+*/

+

+/**

+  Print the IKE Paylolad.

+

+  @param[in]  IkePayload  Points to the payload to be printed.

+

+**/

+/*

+VOID

+IpSecDumpPayload (

+  IN IKE_PAYLOAD           *IkePayload

+  );

+*/

+/**

+  Print the buffer in form of Hex.

+

+  @param[in]  Title       The strings to be printed before the data of the buffer.

+  @param[in]  Data        Points to the buffer to be printed.

+  @param[in]  DataSize    The size of the buffer to be printed.

+

+**/

+VOID

+IpSecDumpBuf (

+  IN CHAR8                 *Title,

+  IN UINT8                 *Data,

+  IN UINTN                 DataSize

+  );

+

+#endif

diff --git a/NetworkPkg/IpSecDxe/IpSecDriver.c b/NetworkPkg/IpSecDxe/IpSecDriver.c
new file mode 100644
index 0000000..b38f2a9
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecDriver.c
@@ -0,0 +1,282 @@
+/** @file

+  Driver Binding Protocol for IPsec Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include <Library/UdpIoLib.h>

+#include "IpSecConfigImpl.h"

+#include "IpSecDebug.h"

+

+/**

+  Test to see if this driver supports ControllerHandle.

+

+  @param[in]  This                 Protocol instance pointer.

+  @param[in]  ControllerHandle     Handle of device to test.

+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child

+                                   device to start.

+

+  @retval EFI_SUCCES           This driver supports this device.

+  @retval EFI_ALREADY_STARTED  This driver is already running on this device.

+  @retval other                This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecDriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL

+  )

+{

+  //

+  //TODO: Add Udp4Protocol and Udp6Protocol testing.

+  //

+  return EFI_UNSUPPORTED;

+}

+

+/**

+  Start this driver on ControllerHandle.

+

+  @param[in]  This                 Protocol instance pointer.

+  @param[in]  ControllerHandle     Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child

+                                   device to start.

+

+  @retval EFI_SUCCES           This driver is added to ControllerHandle

+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle

+  @retval EFI_DEVICE_ERROR     The device could not be started due to a device error.

+                               Currently not implemented.

+  @retval other                This driver does not support this device

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecDriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  )

+{

+  //

+  //TODO: Add Udp4Io and Udp6Io creation for the IKE.

+  //

+  return EFI_SUCCESS;

+}

+

+/**

+  Stop this driver on ControllerHandle.

+

+  @param[in]  This                 Protocol instance pointer.

+  @param[in]  ControllerHandle     Handle of a device to stop the driver on.

+  @param[in]  NumberOfChildren     Number of Handles in ChildHandleBuffer. If the number of

+                                   children is zero, stop the entire bus driver.

+  @param[in]  ChildHandleBuffer    List of Child Handles to Stop.

+

+  @retval EFI_SUCCES           This driver removed ControllerHandle.

+  @retval other                This driver was not removed from this device.

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecDriverBindingStop (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN UINTN                        NumberOfChildren,

+  IN EFI_HANDLE                   *ChildHandleBuffer

+  )

+{

+  //

+  //TODO: Add UdpIo4 and UdpIo6 destruction when the Udp driver unload or stop.

+  //

+  return EFI_UNSUPPORTED;

+}

+

+EFI_DRIVER_BINDING_PROTOCOL gIpSecDriverBinding = {

+  IpSecDriverBindingSupported,

+  IpSecDriverBindingStart,

+  IpSecDriverBindingStop,

+  0xa,

+  NULL,

+  NULL

+};

+

+/**

+  This is a callback function when the mIpSecInstance.DisabledEvent is signaled.

+

+  @param[in]  Event        Event whose notification function is being invoked.

+  @param[in]  Context      Pointer to the notification function's context.

+

+**/

+VOID

+EFIAPI

+IpSecCleanupAllSa (

+  IN  EFI_EVENT     Event,

+  IN  VOID          *Context

+  )

+{

+  IPSEC_PRIVATE_DATA  *Private;

+  UINT8               Value;

+  EFI_STATUS          Status;

+

+  Private = (IPSEC_PRIVATE_DATA *) Context;

+

+  //

+  // Set the Status Variable

+  //

+  Value  = IPSEC_STATUS_DISABLED;

+  Status = gRT->SetVariable (

+                  IPSECCONFIG_STATUS_NAME,

+                  &gEfiIpSecConfigProtocolGuid,

+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,

+                  sizeof (Value),

+                  &Value

+                  );

+  if (!EFI_ERROR (Status)) {

+    Private->IpSec.DisabledFlag = TRUE;

+  }

+

+}

+

+/**

+  This is the declaration of an EFI image entry point. This entry point is

+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including

+  both device drivers and bus drivers.

+

+  The entry point for IPsec driver which installs the driver binding,

+  component name protocol, IPsec Config protcolon, and IPsec protocol in

+  its ImageHandle.

+

+  @param[in] ImageHandle        The firmware allocated handle for the UEFI image.

+  @param[in] SystemTable        A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The operation completed successfully.

+  @retval EFI_ALREADY_STARTED   The IPsec driver has been already loaded.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.

+  @retval Others                The operation is failed.

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecDriverEntryPoint (

+  IN EFI_HANDLE              ImageHandle,

+  IN EFI_SYSTEM_TABLE        *SystemTable

+  )

+{

+  EFI_STATUS          Status;

+  IPSEC_PRIVATE_DATA  *Private;

+  EFI_IPSEC_PROTOCOL  *IpSec;

+

+  //

+  // Check whether ipsec protocol has already been installed.

+  //

+  Status = gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &IpSec);

+

+  if (!EFI_ERROR (Status)) {

+    DEBUG ((DEBUG_WARN, "_ModuleEntryPoint: IpSec has been already loaded\n"));

+    Status = EFI_ALREADY_STARTED;

+    goto ON_EXIT;

+  }

+

+  Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, (VOID **) &mDpc);

+

+  if (EFI_ERROR (Status)) {

+    DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to locate EfiDpcProtocol\n"));

+    goto ON_EXIT;

+  }

+

+  Private = AllocateZeroPool (sizeof (IPSEC_PRIVATE_DATA));

+

+  if (Private == NULL) {

+    DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to allocate private data\n"));

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+  //

+  // Create disable event to cleanup all sa when ipsec disabled by user.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_CALLBACK,

+                  IpSecCleanupAllSa,

+                  Private,

+                  &mIpSecInstance.DisabledEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to create disable event\n"));

+    goto ON_FREE_PRIVATE;

+  }

+

+  Private->Signature    = IPSEC_PRIVATE_DATA_SIGNATURE;

+  Private->ImageHandle  = ImageHandle;

+  CopyMem (&Private->IpSec, &mIpSecInstance, sizeof (EFI_IPSEC_PROTOCOL));

+

+  //

+  // Initilize Private's members. Thess members is used for IKE.

+  //

+  InitializeListHead (&Private->Udp4List);

+  InitializeListHead (&Private->Udp6List);

+  InitializeListHead (&Private->Ikev1SessionList);

+  InitializeListHead (&Private->Ikev1EstablishedList);

+  InitializeListHead (&Private->Ikev2SessionList);

+  InitializeListHead (&Private->Ikev2EstablishedList);

+

+  //

+  // Initialize the ipsec config data and restore it from variable.

+  //

+  Status = IpSecConfigInitialize (Private);

+  if (EFI_ERROR (Status)) {

+    DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to initialize IpSecConfig\n"));

+    goto ON_CLOSE_EVENT;

+  }

+  //

+  // Install ipsec protocol which is used by ip driver to process ipsec header.

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &Private->Handle,

+                  &gEfiIpSecProtocolGuid,

+                  &Private->IpSec,

+                  NULL

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_UNINSTALL_CONFIG;

+  }

+

+  Status = EfiLibInstallDriverBindingComponentName2 (

+             ImageHandle,

+             SystemTable,

+             &gIpSecDriverBinding,

+             ImageHandle,

+             &gIpSecComponentName,

+             &gIpSecComponentName2

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_UNINSTALL_CONFIG;

+  }

+

+  return Status;

+

+ON_UNINSTALL_CONFIG:

+  gBS->UninstallProtocolInterface (

+        Private->Handle,

+        &gEfiIpSecConfigProtocolGuid,

+        &Private->IpSecConfig

+        );

+ON_CLOSE_EVENT:

+  gBS->CloseEvent (mIpSecInstance.DisabledEvent);

+  mIpSecInstance.DisabledEvent = NULL;

+ON_FREE_PRIVATE:

+  FreePool (Private);

+ON_EXIT:

+  return Status;

+}

+

diff --git a/NetworkPkg/IpSecDxe/IpSecDxe.inf b/NetworkPkg/IpSecDxe/IpSecDxe.inf
new file mode 100644
index 0000000..250ef1c
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecDxe.inf
@@ -0,0 +1,63 @@
+## @file

+#  Component description file for IpSec module.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = IpSecDxe

+  FILE_GUID                      = EE8367C0-A1D6-4565-8F89-EF628547B722

+  MODULE_TYPE                    = UEFI_DRIVER

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = IpSecDriverEntryPoint

+

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC

+#

+

+[Sources]

+  IpSecConfigImpl.c

+  IpSecConfigImpl.h

+  IpSecCryptIo.h

+  IpSecCryptIo.c

+  IpSecDebug.h

+  ComponentName.c

+  IpSecImpl.c

+  IpSecDebug.c

+  IpSecSaEngine.c

+  IpSecDriver.c

+  IpSecImpl.h

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+

+[LibraryClasses]

+  MemoryAllocationLib

+  BaseLib

+  UefiLib

+  UefiBootServicesTableLib

+  UefiRuntimeServicesTableLib

+  UefiDriverEntryPoint

+  BaseMemoryLib

+  DebugLib

+  PrintLib

+  DpcLib

+  NetLib

+

+[Protocols]

+  gEfiIp4ConfigProtocolGuid                     # PROTOCOL ALWAYS_CONSUMED

+  gEfiIpSecConfigProtocolGuid                   # PROTOCOL ALWAYS_PRODUCED

+  gEfiIpSecProtocolGuid                         # PROTOCOL ALWAYS_PRODUCED

diff --git a/NetworkPkg/IpSecDxe/IpSecImpl.c b/NetworkPkg/IpSecDxe/IpSecImpl.c
new file mode 100644
index 0000000..15884ae
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecImpl.c
@@ -0,0 +1,856 @@
+/** @file

+  The implementation of IPsec Protocol

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecConfigImpl.h"

+

+EFI_IPSEC_PROTOCOL  mIpSecInstance = { IpSecProcess, NULL, TRUE };

+

+extern LIST_ENTRY   mConfigData[IPsecConfigDataTypeMaximum];

+

+/**

+  Check if the specified Address is the Valid Address Range.

+

+  This function checks if the bytes after prefixed length are all Zero in this

+  Address. This Address is supposed to point to a range address,  meaning it only

+  gives the correct prefixed address.

+

+  @param[in]  IpVersion         The IP version.

+  @param[in]  Address           Points to EFI_IP_ADDRESS to be checked.

+  @param[in]  PrefixLength      The PrefixeLength of this address.

+

+  @retval     TRUE      The address is a vaild address range.

+  @retval     FALSE     The address is not a vaild address range.

+

+**/

+BOOLEAN

+IpSecValidAddressRange (

+  IN UINT8                     IpVersion,

+  IN EFI_IP_ADDRESS            *Address,

+  IN UINT8                     PrefixLength

+  )

+{

+  UINT8           Div;

+  UINT8           Mod;

+  UINT8           Mask;

+  UINT8           AddrLen;

+  UINT8           *Addr;

+  EFI_IP_ADDRESS  ZeroAddr;

+

+  if (PrefixLength == 0) {

+    return TRUE;

+  }

+

+  AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128);

+

+  if (AddrLen <= PrefixLength) {

+    return FALSE;

+  }

+

+  Div   = (UINT8) (PrefixLength / 8);

+  Mod   = (UINT8) (PrefixLength % 8);

+  Addr  = (UINT8 *) Address;

+  ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));

+

+  //

+  // Check whether the mod part of host scope is zero or not.

+  //

+  if (Mod > 0) {

+    Mask = (UINT8) (0xFF << (8 - Mod));

+

+    if ((Addr[Div] | Mask) != Mask) {

+      return FALSE;

+    }

+

+    Div++;

+  }

+  //

+  // Check whether the div part of host scope is zero or not.

+  //

+  if (CompareMem (

+        &Addr[Div],

+        &ZeroAddr,

+        sizeof (EFI_IP_ADDRESS) - Div

+        ) != 0) {

+    return FALSE;

+  }

+

+  return TRUE;

+}

+

+/**

+  Extrct the Address Range from a Address.

+

+  This function keep the prefix address and zero other part address.

+

+  @param[in]  Address           Point to a specified address.

+  @param[in]  PrefixLength      The prefix length.

+  @param[out] Range             Contain the return Address Range.

+

+**/

+VOID

+IpSecExtractAddressRange (

+  IN EFI_IP_ADDRESS            *Address,

+  IN UINT8                     PrefixLength,

+  OUT EFI_IP_ADDRESS           *Range

+  )

+{

+  UINT8 Div;

+  UINT8 Mod;

+  UINT8 Mask;

+  UINT8 *Addr;

+

+  if (PrefixLength == 0) {

+    return ;

+  }

+

+  Div   = (UINT8) (PrefixLength / 8);

+  Mod   = (UINT8) (PrefixLength % 8);

+  Addr  = (UINT8 *) Range;

+

+  CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS));

+

+  //

+  // Zero the mod part of host scope.

+  //

+  if (Mod > 0) {

+    Mask      = (UINT8) (0xFF << (8 - Mod));

+    Addr[Div] = (UINT8) (Addr[Div] & Mask);

+    Div++;

+  }

+  //

+  // Zero the div part of host scope.

+  //

+  ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div);

+

+}

+

+/**

+  Checks if the IP Address in the address range of AddressInfos specified.

+

+  @param[in]  IpVersion         The IP version.

+  @param[in]  IpAddr            Point to EFI_IP_ADDRESS to be check.

+  @param[in]  AddressInfo       A list of EFI_IP_ADDRESS_INFO that is used to check

+                                the IP Address is matched.

+  @param[in]  AddressCount      The total numbers of the AddressInfo.

+

+  @retval   TRUE    If the Specified IP Address is in the range of the AddressInfos specified.

+  @retval   FALSE   If the Specified IP Address is not in the range of the AddressInfos specified.

+

+**/

+BOOLEAN

+IpSecMatchIpAddress (

+  IN UINT8                     IpVersion,

+  IN EFI_IP_ADDRESS            *IpAddr,

+  IN EFI_IP_ADDRESS_INFO       *AddressInfo,

+  IN UINT32                    AddressCount

+  )

+{

+  EFI_IP_ADDRESS  Range;

+  UINT32          Index;

+  BOOLEAN         IsMatch;

+

+  IsMatch = FALSE;

+

+  for (Index = 0; Index < AddressCount; Index++) {

+    //

+    // Check whether the target address is in the address range

+    // if it's a valid range of address.

+    //

+    if (IpSecValidAddressRange (

+          IpVersion,

+          &AddressInfo[Index].Address,

+          AddressInfo[Index].PrefixLength

+          )) {

+      //

+      // Get the range of the target address belongs to.

+      //

+      ZeroMem (&Range, sizeof (EFI_IP_ADDRESS));

+      IpSecExtractAddressRange (

+        IpAddr,

+        AddressInfo[Index].PrefixLength,

+        &Range

+        );

+

+      if (CompareMem (

+            &Range,

+            &AddressInfo[Index].Address,

+            sizeof (EFI_IP_ADDRESS)

+            ) == 0) {

+        //

+        // The target address is in the address range.

+        //

+        IsMatch = TRUE;

+        break;

+      }

+    }

+

+    if (CompareMem (

+          IpAddr,

+          &AddressInfo[Index].Address,

+          sizeof (EFI_IP_ADDRESS)

+          ) == 0) {

+      //

+      // The target address is exact same as the address.

+      //

+      IsMatch = TRUE;

+      break;

+    }

+  }

+

+  return IsMatch;

+}

+

+/**

+  Check if the specified Protocol and Prot is supported by the specified SPD Entry.

+

+  This function is the subfunction of IPsecLookUpSpdEntry() that is used to

+  check if the sent/received IKE packet has the related SPD entry support.

+

+  @param[in]  Protocol          The Protocol to be checked.

+  @param[in]  IpPayload         Point to IP Payload to be check.

+  @param[in]  SpdProtocol       The Protocol supported by SPD.

+  @param[in]  SpdLocalPort      The Local Port in SPD.

+  @param[in]  SpdRemotePort     The Remote Port in SPD.

+  @param[in]  IsOutbound        Flag to indicate the is for IKE Packet sending or recieving.

+

+  @retval     TRUE      The Protocol and Port are supported by the SPD Entry.

+  @retval     FALSE     The Protocol and Port are not supported by the SPD Entry.

+

+**/

+BOOLEAN

+IpSecMatchNextLayerProtocol (

+  IN UINT8                     Protocol,

+  IN UINT8                     *IpPayload,

+  IN UINT16                    SpdProtocol,

+  IN UINT16                    SpdLocalPort,

+  IN UINT16                    SpdRemotePort,

+  IN BOOLEAN                   IsOutbound

+  )

+{

+  BOOLEAN IsMatch;

+

+  if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) {

+    return TRUE;

+  }

+

+  IsMatch = FALSE;

+

+  if (SpdProtocol == Protocol) {

+    switch (Protocol) {

+    case EFI_IP_PROTO_UDP:

+    case EFI_IP_PROTO_TCP:

+      //

+      // For udp and tcp, (0, 0) means no need to check local and remote

+      // port. The payload is passed from upper level, which means it should

+      // be in network order.

+      //

+      IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);

+      IsMatch = (BOOLEAN) (IsMatch ||

+                           (IsOutbound &&

+                           (BOOLEAN)(

+                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort &&

+                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort

+                              )

+                            ));

+

+      IsMatch = (BOOLEAN) (IsMatch ||

+                           (!IsOutbound &&

+                           (BOOLEAN)(

+                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort &&

+                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort

+                              )

+                           ));

+      break;

+

+    case EFI_IP_PROTO_ICMP:

+      //

+      // For icmpv4, type code is replaced with local port and remote port,

+      // and (0, 0) means no need to check.

+      //

+      IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);

+      IsMatch = (BOOLEAN) (IsMatch ||

+                           (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&

+                                      ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort

+                                      )

+                           );

+      break;

+

+    case IP6_ICMP:

+      //

+      // For icmpv6, type code is replaced with local port and remote port,

+      // and (0, 0) means no need to check.

+      //

+      IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);

+

+      IsMatch = (BOOLEAN) (IsMatch ||

+                           (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&

+                                      ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort

+                                      )

+                          );

+      break;

+

+    default:

+      IsMatch = TRUE;

+      break;

+    }

+  }

+

+  return IsMatch;

+}

+

+/**

+  Find the SAD through a specified SPD's SAD list.

+

+  @param[in]  SadList           SAD list related to a specified SPD entry.

+  @param[in]  DestAddress       The destination address used to find the SAD entry.

+

+  @return  The pointer to a certain SAD entry.

+

+**/

+IPSEC_SAD_ENTRY *

+IpSecLookupSadBySpd (

+  IN LIST_ENTRY                 *SadList,

+  IN EFI_IP_ADDRESS             *DestAddress

+  )

+{

+  LIST_ENTRY      *Entry;

+  IPSEC_SAD_ENTRY *SadEntry;

+

+  for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {

+

+    SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);

+    //

+    // Find the right sad entry which contains the appointed dest address.

+    //

+    if (CompareMem (

+          &SadEntry->Id->DestAddress,

+          DestAddress,

+          sizeof (EFI_IP_ADDRESS)

+          ) == 0) {

+      return SadEntry;

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  Find the SAD through whole SAD list.

+

+  @param[in]  Spi               The SPI used to search the SAD entry.

+  @param[in]  DestAddress       The destination used to search the SAD entry.

+

+  @return  the pointer to a certain SAD entry.

+

+**/

+IPSEC_SAD_ENTRY *

+IpSecLookupSadBySpi (

+  IN UINT32                   Spi,

+  IN EFI_IP_ADDRESS           *DestAddress

+  )

+{

+  LIST_ENTRY      *Entry;

+  LIST_ENTRY      *SadList;

+  IPSEC_SAD_ENTRY *SadEntry;

+

+  SadList = &mConfigData[IPsecConfigDataTypeSad];

+

+  for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {

+

+    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);

+    //

+    // Find the right sad entry which contain the appointed spi and dest addr.

+    //

+    if (SadEntry->Id->Spi == Spi && CompareMem (

+                                      &SadEntry->Id->DestAddress,

+                                      DestAddress,

+                                      sizeof (EFI_IP_ADDRESS)

+                                      ) == 0) {

+

+      return SadEntry;

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  Look up if there is existing SAD entry for specified IP packet sending.

+

+  This function is called by the IPsecProcess when there is some IP packet needed to

+  send out. This function checks if there is an existing SAD entry that can be serviced

+  to this IP packet sending. If no existing SAD entry could be used, this

+  function will invoke an IPsec Key Exchange Negotiation.

+

+  @param[in]  Private           Points to private data.

+  @param[in]  NicHandle         Points to a NIC handle.

+  @param[in]  IpVersion         The version of IP.

+  @param[in]  IpHead            The IP Header of packet to be sent out.

+  @param[in]  IpPayload         The IP Payload to be sent out.

+  @param[in]  OldLastHead       The Last protocol of the IP packet.

+  @param[in]  SpdEntry          Points to a related SPD entry.

+  @param[out] SadEntry          Contains the Point of a related SAD entry.

+

+  @retval EFI_DEVICE_ERROR  One of following conditions is TRUE:

+                            - If don't find related UDP service.

+                            - Sequence Number is used up.

+                            - Extension Sequence Number is used up.

+  @retval EFI_DEVICE_ERROR  GC_TODO: Add description for return value.

+  @retval EFI_NOT_READY     No existing SAD entry could be used.

+  @retval EFI_SUCCESS       Find the related SAD entry.

+

+**/

+EFI_STATUS

+IpSecLookupSadEntry (

+  IN IPSEC_PRIVATE_DATA      *Private,

+  IN EFI_HANDLE              NicHandle,

+  IN UINT8                   IpVersion,

+  IN VOID                    *IpHead,

+  IN UINT8                   *IpPayload,

+  IN UINT8                   OldLastHead,

+  IN IPSEC_SPD_ENTRY         *SpdEntry,

+  OUT IPSEC_SAD_ENTRY        **SadEntry

+  )

+{

+  IPSEC_SAD_ENTRY *Entry;

+  IPSEC_SAD_DATA  *Data;

+  EFI_IP_ADDRESS  DestIp;

+  UINT32          SeqNum32;

+

+  *SadEntry   = NULL;

+  //

+  // Parse the destination address from ip header.

+  //

+  ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));

+  if (IpVersion == IP_VERSION_4) {

+    CopyMem (

+      &DestIp,

+      &((IP4_HEAD *) IpHead)->Dst,

+      sizeof (IP4_ADDR)

+      );

+  } else {

+    CopyMem (

+      &DestIp,

+      &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,

+      sizeof (EFI_IP_ADDRESS)

+      );

+  }

+  //

+  // Find the sad entry in the spd.sas list according to the dest address.

+  //

+  Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp);

+

+  if (Entry == NULL) {

+

+    if (OldLastHead != IP6_ICMP ||

+        (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST)

+        ) {

+      //

+      // TODO: Start ike negotiation process except the request packet of ping.

+      //

+      //IkeNegotiate (UdpService, SpdEntry, &DestIp);

+    }

+

+    return EFI_NOT_READY;

+  }

+

+  Data = Entry->Data;

+

+  if (!Data->ManualSet) {

+    if (Data->ESNEnabled) {

+      //

+      // Validate the 64bit sn number if 64bit sn enabled.

+      //

+      if (Data->SequenceNumber + 1 < Data->SequenceNumber) {

+        //

+        // TODO: Re-negotiate SA

+        //

+        return EFI_DEVICE_ERROR;

+      }

+    } else {

+      //

+      // Validate the 32bit sn number if 64bit sn disabled.

+      //

+      SeqNum32 = (UINT32) Data->SequenceNumber;

+      if (SeqNum32 + 1 < SeqNum32) {

+        //

+        // TODO: Re-negotiate SA

+        //

+        return EFI_DEVICE_ERROR;

+      }

+    }

+  }

+

+  *SadEntry = Entry;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Find a PAD entry according to a remote IP address.

+

+  @param[in]  IpVersion         The version of IP.

+  @param[in]  IpAddr            Points to remote IP address.

+

+  @return the pointer of related PAD entry.

+

+**/

+IPSEC_PAD_ENTRY *

+IpSecLookupPadEntry (

+  IN UINT8                   IpVersion,

+  IN EFI_IP_ADDRESS          *IpAddr

+  )

+{

+  LIST_ENTRY          *PadList;

+  LIST_ENTRY          *Entry;

+  EFI_IP_ADDRESS_INFO *IpAddrInfo;

+  IPSEC_PAD_ENTRY     *PadEntry;

+

+  PadList = &mConfigData[IPsecConfigDataTypePad];

+

+  for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) {

+

+    PadEntry    = IPSEC_PAD_ENTRY_FROM_LIST (Entry);

+    IpAddrInfo  = &PadEntry->Id->Id.IpAddress;

+    //

+    // Find the right pad entry which contain the appointed dest addr.

+    //

+    if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) {

+      return PadEntry;

+    }

+  }

+

+  return NULL;

+}

+

+/**

+  Check if the specified IP packet can be serviced by this SPD entry.

+

+  @param[in]  SpdEntry          Point to SPD entry.

+  @param[in]  IpVersion         Version of IP.

+  @param[in]  IpHead            Point to IP header.

+  @param[in]  IpPayload         Point to IP payload.

+  @param[in]  Protocol          The Last protocol of IP packet.

+  @param[in]  IsOutbound        Traffic direction.

+

+  @retval EFI_IPSEC_ACTION  The support action of SPD entry.

+  @retval -1                If the input packet header doesn't match the SpdEntry.

+

+**/

+EFI_IPSEC_ACTION

+IpSecLookupSpdEntry (

+  IN IPSEC_SPD_ENTRY         *SpdEntry,

+  IN UINT8                   IpVersion,

+  IN VOID                    *IpHead,

+  IN UINT8                   *IpPayload,

+  IN UINT8                   Protocol,

+  IN BOOLEAN                 IsOutbound

+  )

+{

+  EFI_IPSEC_SPD_SELECTOR  *SpdSel;

+  IP4_HEAD                *Ip4;

+  EFI_IP6_HEADER          *Ip6;

+  EFI_IP_ADDRESS          SrcAddr;

+  EFI_IP_ADDRESS          DstAddr;

+  BOOLEAN                 SpdMatch;

+

+  ASSERT (SpdEntry != NULL);

+  SpdSel  = SpdEntry->Selector;

+  Ip4     = (IP4_HEAD *) IpHead;

+  Ip6     = (EFI_IP6_HEADER *) IpHead;

+

+  ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS));

+  ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS));

+

+  //

+  // Parse the source and destination address from ip header.

+  //

+  if (IpVersion == IP_VERSION_4) {

+    CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR));

+    CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR));

+  } else {

+    CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS));

+    CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));

+  }

+  //

+  // Check the local and remote addresses for outbound traffic

+  //

+  SpdMatch = (BOOLEAN)(IsOutbound &&

+                       IpSecMatchIpAddress (

+                         IpVersion,

+                         &SrcAddr,

+                         SpdSel->LocalAddress,

+                         SpdSel->LocalAddressCount

+                         ) &&

+                       IpSecMatchIpAddress (

+                         IpVersion,

+                         &DstAddr,

+                         SpdSel->RemoteAddress,

+                         SpdSel->RemoteAddressCount

+                         )

+                       );

+

+  //

+  // Check the local and remote addresses for inbound traffic

+  //

+  SpdMatch = (BOOLEAN) (SpdMatch ||

+                        (!IsOutbound &&

+                        IpSecMatchIpAddress (

+                          IpVersion,

+                          &DstAddr,

+                          SpdSel->LocalAddress,

+                          SpdSel->LocalAddressCount

+                          ) &&

+                        IpSecMatchIpAddress (

+                          IpVersion,

+                          &SrcAddr,

+                          SpdSel->RemoteAddress,

+                          SpdSel->RemoteAddressCount

+                          )

+                        ));

+

+  //

+  // Check the next layer protocol and local and remote ports.

+  //

+  SpdMatch = (BOOLEAN) (SpdMatch &&

+                        IpSecMatchNextLayerProtocol (

+                          Protocol,

+                          IpPayload,

+                          SpdSel->NextLayerProtocol,

+                          SpdSel->LocalPort,

+                          SpdSel->RemotePort,

+                          IsOutbound

+                          )

+                        );

+

+  if (SpdMatch) {

+    //

+    // Find the right spd entry if match the 5 key elements.

+    //

+    return SpdEntry->Data->Action;

+  }

+

+  return (EFI_IPSEC_ACTION) - 1;

+}

+

+/**

+  Handles IPsec packet processing for inbound and outbound IP packets.

+

+  The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.

+  The behavior is that it can perform one of the following actions:

+  bypass the packet, discard the packet, or protect the packet.

+

+  @param[in]      This             Pointer to the EFI_IPSEC_PROTOCOL instance.

+  @param[in]      NicHandle        Instance of the network interface.

+  @param[in]      IpVersion        IPV4 or IPV6.

+  @param[in, out] IpHead           Pointer to the IP Header.

+  @param[in]      LastHead         The protocol of the next layer to be processed by IPsec.

+  @param[in]      OptionsBuffer    Pointer to the options buffer.

+  @param[in]      OptionsLength    Length of the options buffer.

+  @param[in, out] FragmentTable    Pointer to a list of fragments.

+  @param[in]      FragmentCount    Number of fragments.

+  @param[in]      TrafficDirection Traffic direction.

+  @param[out]     RecycleSignal    Event for recycling of resources.

+

+  @retval EFI_SUCCESS              The packet was bypassed and all buffers remain the same.

+  @retval EFI_SUCCESS              The packet was protected.

+  @retval EFI_ACCESS_DENIED        The packet was discarded.

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecProcess (

+  IN     EFI_IPSEC_PROTOCOL              *This,

+  IN     EFI_HANDLE                      NicHandle,

+  IN     UINT8                           IpVersion,

+  IN OUT VOID                            *IpHead,

+  IN     UINT8                           *LastHead,

+  IN     VOID                            *OptionsBuffer,

+  IN     UINT32                          OptionsLength,

+  IN OUT EFI_IPSEC_FRAGMENT_DATA         **FragmentTable,

+  IN     UINT32                          *FragmentCount,

+  IN     EFI_IPSEC_TRAFFIC_DIR           TrafficDirection,

+     OUT EFI_EVENT                       *RecycleSignal

+  )

+{

+  IPSEC_PRIVATE_DATA  *Private;

+  IPSEC_SPD_ENTRY     *SpdEntry;

+  IPSEC_SAD_ENTRY     *SadEntry;

+  LIST_ENTRY          *SpdList;

+  LIST_ENTRY          *Entry;

+  EFI_IPSEC_ACTION    Action;

+  EFI_STATUS          Status;

+  UINT8               *IpPayload;

+  UINT8               OldLastHead;

+  BOOLEAN             IsOutbound;

+

+  Private         = IPSEC_PRIVATE_DATA_FROM_IPSEC (This);

+  IpPayload       = (*FragmentTable)[0].FragmentBuffer;

+  IsOutbound      = (BOOLEAN) ((TrafficDirection == EfiIPsecOutBound) ? TRUE : FALSE);

+  OldLastHead     = *LastHead;

+  *RecycleSignal  = NULL;

+

+  if (!IsOutbound) {

+    //

+    // For inbound traffic, process the ipsec header of the packet.

+    //

+    Status = IpSecProtectInboundPacket (

+              IpVersion,

+              IpHead,

+              LastHead,

+              OptionsBuffer,

+              OptionsLength,

+              FragmentTable,

+              FragmentCount,

+              &SpdEntry,

+              RecycleSignal

+              );

+

+    if (Status == EFI_ACCESS_DENIED) {

+      //

+      // The packet is denied to access.

+      //

+      goto ON_EXIT;

+    }

+

+    if (Status == EFI_SUCCESS) {

+      //

+      // Check the spd entry if the packet is accessible.

+      //

+      if (SpdEntry == NULL) {

+        Status = EFI_ACCESS_DENIED;

+        goto ON_EXIT;

+      }

+      Action = IpSecLookupSpdEntry (

+                SpdEntry,

+                IpVersion,

+                IpHead,

+                IpPayload,

+                *LastHead,

+                IsOutbound

+                );

+

+      if (Action != EfiIPsecActionProtect) {

+        //

+        // Discard the packet if the spd entry is not protect.

+        //

+        gBS->SignalEvent (*RecycleSignal);

+        *RecycleSignal  = NULL;

+        Status          = EFI_ACCESS_DENIED;

+      }

+

+      goto ON_EXIT;

+    }

+  }

+

+  Status  = EFI_ACCESS_DENIED;

+  SpdList = &mConfigData[IPsecConfigDataTypeSpd];

+

+  for (Entry = SpdList->ForwardLink; Entry != SpdList; Entry = Entry->ForwardLink) {

+    //

+    // For outbound and non-ipsec Inbound traffic: check the spd entry.

+    //

+    SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);

+    Action = IpSecLookupSpdEntry (

+              SpdEntry,

+              IpVersion,

+              IpHead,

+              IpPayload,

+              OldLastHead,

+              IsOutbound

+              );

+

+    switch (Action) {

+

+    case EfiIPsecActionProtect:

+

+      if (IsOutbound) {

+        //

+        // For outbound traffic, lookup the sad entry.

+        //

+        Status = IpSecLookupSadEntry (

+                  Private,

+                  NicHandle,

+                  IpVersion,

+                  IpHead,

+                  IpPayload,

+                  OldLastHead,

+                  SpdEntry,

+                  &SadEntry

+                  );

+

+        if (SadEntry != NULL) {

+          //

+          // Process the packet by the found sad entry.

+          //

+          Status = IpSecProtectOutboundPacket (

+                    IpVersion,

+                    IpHead,

+                    LastHead,

+                    OptionsBuffer,

+                    OptionsLength,

+                    FragmentTable,

+                    FragmentCount,

+                    SadEntry,

+                    RecycleSignal

+                    );

+

+        } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {

+          //

+          // TODO: if no need return not ready to upper layer, change here.

+          //

+          Status = EFI_SUCCESS;

+        }

+      } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {

+        //

+        // For inbound icmpv6 traffic except ping request, accept the packet

+        // although no sad entry associated with protect spd entry.

+        //

+        IpSecLookupSadEntry (

+          Private,

+          NicHandle,

+          IpVersion,

+          IpHead,

+          IpPayload,

+          OldLastHead,

+          SpdEntry,

+          &SadEntry

+          );

+        if (SadEntry == NULL) {

+          Status = EFI_SUCCESS;

+        }

+      }

+

+      goto ON_EXIT;

+

+    case EfiIPsecActionBypass:

+      Status = EFI_SUCCESS;

+      goto ON_EXIT;

+

+    case EfiIPsecActionDiscard:

+      goto ON_EXIT;

+

+    default:

+      //

+      // Discard the packet if no spd entry match.

+      //

+      break;

+    }

+  }

+

+ON_EXIT:

+  return Status;

+}

+

diff --git a/NetworkPkg/IpSecDxe/IpSecImpl.h b/NetworkPkg/IpSecDxe/IpSecImpl.h
new file mode 100644
index 0000000..644c658
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecImpl.h
@@ -0,0 +1,313 @@
+/** @file

+  The definitions related to IPsec protocol implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _IP_SEC_IMPL_H_

+#define _IP_SEC_IMPL_H_

+

+#include <Uefi.h>

+#include <Library/UefiLib.h>

+#include <Library/NetLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Protocol/IpSec.h>

+#include <Protocol/IpSecConfig.h>

+#include <Protocol/Dpc.h>

+#include <Protocol/ComponentName.h>

+#include <Protocol/ComponentName2.h>

+

+typedef struct _IPSEC_PRIVATE_DATA IPSEC_PRIVATE_DATA;

+typedef struct _IPSEC_SPD_ENTRY IPSEC_SPD_ENTRY;

+typedef struct _IPSEC_PAD_ENTRY IPSEC_PAD_ENTRY;

+typedef struct _IPSEC_SPD_DATA IPSEC_SPD_DATA;

+

+#define IPSEC_PRIVATE_DATA_SIGNATURE        SIGNATURE_32 ('I', 'P', 'S', 'E')

+

+#define IPSEC_PRIVATE_DATA_FROM_IPSEC(a)    CR (a, IPSEC_PRIVATE_DATA, IpSec, IPSEC_PRIVATE_DATA_SIGNATURE)

+#define IPSEC_PRIVATE_DATA_FROM_UDP4LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp4List, IPSEC_PRIVATE_DATA_SIGNATURE)

+#define IPSEC_PRIVATE_DATA_FROM_UDP6LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp6List, IPSEC_PRIVATE_DATA_SIGNATURE)

+#define IPSEC_UDP_SERVICE_FROM_LIST(a)      BASE_CR (a, IKE_UDP_SERVICE, List)

+#define IPSEC_SPD_ENTRY_FROM_LIST(a)        BASE_CR (a, IPSEC_SPD_ENTRY, List)

+#define IPSEC_SAD_ENTRY_FROM_LIST(a)        BASE_CR (a, IPSEC_SAD_ENTRY, List)

+#define IPSEC_PAD_ENTRY_FROM_LIST(a)        BASE_CR (a, IPSEC_PAD_ENTRY, List)

+#define IPSEC_SAD_ENTRY_FROM_SPD(a)         BASE_CR (a, IPSEC_SAD_ENTRY, BySpd)

+

+#define IPSEC_STATUS_DISABLED       0

+#define IPSEC_STATUS_ENABLED        1

+#define IPSEC_ESP_PROTOCOL          50

+#define IPSEC_AH_PROTOCOL           51

+#define IPSEC_DEFAULT_VARIABLE_SIZE 0x100

+

+//

+// Internal Structure Definition

+//

+#pragma pack(1)

+typedef struct _EFI_AH_HEADER {

+  UINT8   NextHeader;

+  UINT8   PayloadLen;

+  UINT16  Reserved;

+  UINT32  Spi;

+  UINT32  SequenceNumber;

+} EFI_AH_HEADER;

+

+typedef struct _EFI_ESP_HEADER {

+  UINT32  Spi;

+  UINT32  SequenceNumber;

+} EFI_ESP_HEADER;

+

+typedef struct _EFI_ESP_TAIL {

+  UINT8 PaddingLength;

+  UINT8 NextHeader;

+} EFI_ESP_TAIL;

+#pragma pack()

+

+struct _IPSEC_SPD_DATA {

+  CHAR16                    Name[100];

+  UINT32                    PackageFlag;

+  EFI_IPSEC_ACTION          Action;

+  EFI_IPSEC_PROCESS_POLICY  *ProcessingPolicy;

+  LIST_ENTRY                Sas;

+};

+

+struct _IPSEC_SPD_ENTRY {

+  EFI_IPSEC_SPD_SELECTOR  *Selector;

+  IPSEC_SPD_DATA          *Data;

+  LIST_ENTRY              List;

+};

+

+typedef struct _IPSEC_SAD_DATA {

+  EFI_IPSEC_MODE        Mode;

+  UINT64                SequenceNumber;

+  UINT8                 AntiReplayWindowSize;

+  UINT64                AntiReplayBitmap[4];  // bitmap for received packet

+  EFI_IPSEC_ALGO_INFO   AlgoInfo;

+  EFI_IPSEC_SA_LIFETIME SaLifetime;

+  UINT32                PathMTU;

+  IPSEC_SPD_ENTRY       *SpdEntry;

+  BOOLEAN               ESNEnabled;           // Extended (64-bit) SN enabled

+  BOOLEAN               ManualSet;

+} IPSEC_SAD_DATA;

+

+typedef struct _IPSEC_SAD_ENTRY {

+  EFI_IPSEC_SA_ID  *Id;

+  IPSEC_SAD_DATA  *Data;

+  LIST_ENTRY      List;

+  LIST_ENTRY      BySpd;                      // Linked on IPSEC_SPD_DATA.Sas

+} IPSEC_SAD_ENTRY;

+

+struct _IPSEC_PAD_ENTRY {

+  EFI_IPSEC_PAD_ID    *Id;

+  EFI_IPSEC_PAD_DATA  *Data;

+  LIST_ENTRY          List;

+};

+

+typedef struct _IPSEC_RECYCLE_CONTEXT {

+  EFI_IPSEC_FRAGMENT_DATA *FragmentTable;

+  UINT8                   *PayloadBuffer;

+} IPSEC_RECYCLE_CONTEXT;

+

+struct _IPSEC_PRIVATE_DATA {

+  UINT32                    Signature;

+  EFI_HANDLE                Handle;           // Virtual handle to install private prtocol

+  EFI_HANDLE                ImageHandle;

+  EFI_IPSEC_PROTOCOL        IpSec;

+  EFI_IPSEC_CONFIG_PROTOCOL IpSecConfig;

+  BOOLEAN                   SetBySelf;

+  LIST_ENTRY                Udp4List;

+  UINTN                     Udp4Num;

+  LIST_ENTRY                Udp6List;

+  UINTN                     Udp6Num;

+  LIST_ENTRY                Ikev1SessionList;

+  LIST_ENTRY                Ikev1EstablishedList;

+  LIST_ENTRY                Ikev2SessionList;

+  LIST_ENTRY                Ikev2EstablishedList;

+  BOOLEAN                   IsIPsecDisabling;

+};

+

+/**

+  This function processes the inbound traffic with IPsec.

+

+  It checks the received packet security property, trims the ESP/AH header, and then

+  returns without an IPsec protected IP Header and FragmentTable.

+

+  @param[in]      IpVersion          The version of IP.

+  @param[in, out] IpHead             Points to IP header containing the ESP/AH header

+                                     to be trimed on input, and without ESP/AH header

+                                     on return.

+  @param[in]      LastHead           The Last Header in IP header on return.

+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.

+  @param[in]      OptionsLength      Length of the options buffer. It is optional.

+  @param[in, out] FragmentTable      Pointer to a list of fragments in the form of IPsec

+                                     protected on input, and without IPsec protected

+                                     on return.

+  @param[in]      FragmentCount      Number of fragments.

+  @param[out]     SpdEntry           Pointer to contain the address of SPD entry on return.

+  @param[out]     RecycleEvent       Event for recycling of resources.

+

+  @retval EFI_SUCCESS              The operation is successful.

+  @retval EFI_UNSUPPORTED          If the IPSEC protocol is not supported.

+

+**/

+EFI_STATUS

+IpSecProtectInboundPacket (

+  IN     UINT8                       IpVersion,

+  IN OUT VOID                        *IpHead,

+  IN     UINT8                       *LastHead,

+  IN     VOID                        *OptionsBuffer, OPTIONAL

+  IN     UINT32                      OptionsLength,  OPTIONAL

+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,

+  IN     UINT32                      *FragmentCount,

+     OUT IPSEC_SPD_ENTRY             **SpdEntry,

+     OUT EFI_EVENT                   *RecycleEvent

+  );

+

+

+/**

+  This fucntion processes the output traffic with IPsec.

+

+  It protected the sending packet by encrypting it payload and inserting ESP/AH header

+  in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable.

+

+  @param[in]      IpVersion          The version of IP.

+  @param[in, out] IpHead             Point to IP header containing the orginal IP header

+                                     to be processed on input, and inserted ESP/AH header

+                                     on return.

+  @param[in]      LastHead           The Last Header in IP header.

+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.

+  @param[in]      OptionsLength      Length of the options buffer. It is optional.

+  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by

+                                     IPsec on input, and with IPsec protected

+                                     on return.

+  @param[in]      FragmentCount      Number of fragments.

+  @param[in]      SadEntry           Related SAD entry.

+  @param[out]     RecycleEvent       Event for recycling of resources.

+

+  @retval EFI_SUCCESS              The operation is successful.

+  @retval EFI_UNSUPPORTED          If the IPSEC protocol is not supported.

+

+**/

+EFI_STATUS

+IpSecProtectOutboundPacket (

+  IN     UINT8                       IpVersion,

+  IN OUT VOID                        *IpHead,

+  IN     UINT8                       *LastHead,

+  IN     VOID                        *OptionsBuffer, OPTIONAL

+  IN     UINT32                      OptionsLength,  OPTIONAL

+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,

+  IN     UINT32                      *FragmentCount,

+  IN     IPSEC_SAD_ENTRY             *SadEntry,

+     OUT EFI_EVENT                   *RecycleEvent

+  );

+

+/**

+  Check if the IP Address in the address range of AddressInfos specified.

+

+  @param[in]  IpVersion         The IP version.

+  @param[in]  IpAddr            Points to EFI_IP_ADDRESS to be check.

+  @param[in]  AddressInfo       A list of EFI_IP_ADDRESS_INFO that is used to check

+                                the IP Address is matched.

+  @param[in]  AddressCount      The total numbers of the AddressInfo.

+

+  @retval   TRUE    If the Specified IP Address is in the range of the AddressInfos specified.

+  @retval   FALSE   If the Specified IP Address is not in the range of the AddressInfos specified.

+

+**/

+BOOLEAN

+IpSecMatchIpAddress (

+  IN UINT8                                  IpVersion,

+  IN EFI_IP_ADDRESS                         *IpAddr,

+  IN EFI_IP_ADDRESS_INFO                    *AddressInfo,

+  IN UINT32                                 AddressCount

+  );

+

+/**

+  Find a PAD entry according to remote IP address.

+

+  @param[in]  IpVersion         The version of IP.

+  @param[in]  IpAddr            Point to remote IP address.

+

+  @return The pointer of related PAD entry.

+

+**/

+IPSEC_PAD_ENTRY *

+IpSecLookupPadEntry (

+  IN UINT8                                  IpVersion,

+  IN EFI_IP_ADDRESS                         *IpAddr

+  );

+

+/**

+  Find the SAD through whole SAD list.

+

+  @param[in]  Spi               The SPI used to search the SAD entry.

+  @param[in]  DestAddress       The destination used to search the SAD entry.

+

+  @return  The pointer to a certain SAD entry.

+

+**/

+IPSEC_SAD_ENTRY *

+IpSecLookupSadBySpi (

+  IN UINT32                                 Spi,

+  IN EFI_IP_ADDRESS                         *DestAddress

+  )

+;

+

+/**

+  Handles IPsec packet processing for inbound and outbound IP packets.

+

+  The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.

+  The behavior is that it can perform one of the following actions:

+  bypass the packet, discard the packet, or protect the packet.

+

+  @param[in]      This             Pointer to the EFI_IPSEC_PROTOCOL instance.

+  @param[in]      NicHandle        Instance of the network interface.

+  @param[in]      IpVersion        IPV4 or IPV6.

+  @param[in, out] IpHead           Pointer to the IP Header.

+  @param[in]      LastHead         The protocol of the next layer to be processed by IPsec.

+  @param[in]      OptionsBuffer    Pointer to the options buffer.

+  @param[in]      OptionsLength    Length of the options buffer.

+  @param[in, out] FragmentTable    Pointer to a list of fragments.

+  @param[in]      FragmentCount    Number of fragments.

+  @param[in]      TrafficDirection Traffic direction.

+  @param[out]     RecycleSignal    Event for recycling of resources.

+

+  @retval EFI_SUCCESS              The packet was bypassed and all buffers remain the same.

+  @retval EFI_SUCCESS              The packet was protected.

+  @retval EFI_ACCESS_DENIED        The packet was discarded.

+

+**/

+EFI_STATUS

+EFIAPI

+IpSecProcess (

+  IN     EFI_IPSEC_PROTOCOL              *This,

+  IN     EFI_HANDLE                      NicHandle,

+  IN     UINT8                           IpVersion,

+  IN OUT VOID                            *IpHead,

+  IN     UINT8                           *LastHead,

+  IN     VOID                            *OptionsBuffer,

+  IN     UINT32                          OptionsLength,

+  IN OUT EFI_IPSEC_FRAGMENT_DATA         **FragmentTable,

+  IN     UINT32                          *FragmentCount,

+  IN     EFI_IPSEC_TRAFFIC_DIR           TrafficDirection,

+     OUT EFI_EVENT                       *RecycleSignal

+  );

+

+extern EFI_DPC_PROTOCOL    *mDpc;

+extern EFI_IPSEC_PROTOCOL  mIpSecInstance;

+

+extern EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2;

+extern EFI_COMPONENT_NAME_PROTOCOL  gIpSecComponentName;

+

+

+#endif

diff --git a/NetworkPkg/IpSecDxe/IpSecSaEngine.c b/NetworkPkg/IpSecDxe/IpSecSaEngine.c
new file mode 100644
index 0000000..8abf4d6
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecSaEngine.c
@@ -0,0 +1,934 @@
+/** @file

+  IPsec inbound and outbound traffic processing.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "IpSecImpl.h"

+#include "IpSecDebug.h"

+#include "IpSecCryptIo.h"

+

+extern LIST_ENTRY     mConfigData[IPsecConfigDataTypeMaximum];

+

+/**

+  The call back function of NetbufFromExt.

+

+  @param[in]  Arg            The argument passed from the caller.

+

+**/

+VOID

+EFIAPI

+IpSecOnRecyclePacket (

+  IN VOID                            *Arg

+  )

+{

+}

+

+/**

+  This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP

+  is released.

+

+  @param[in]  Event              The related event.

+  @param[in]  Context            The data passed by the caller.

+

+**/

+VOID

+EFIAPI

+IpSecRecycleCallback (

+  IN EFI_EVENT                       Event,

+  IN VOID                            *Context

+  )

+{

+  IPSEC_RECYCLE_CONTEXT *RecycleContext;

+

+  RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context;

+

+  if (RecycleContext->FragmentTable != NULL) {

+    FreePool (RecycleContext->FragmentTable);

+  }

+

+  if (RecycleContext->PayloadBuffer != NULL) {

+    FreePool (RecycleContext->PayloadBuffer);

+  }

+

+  FreePool (RecycleContext);

+  gBS->CloseEvent (Event);

+

+}

+

+/**

+  Calculate the extension header of IP. The return length only doesn't contain

+  the fixed IP header length.

+

+  @param[in]  IpHead             Points to an IP head to be calculated.

+  @param[in]  LastHead           Points to the last header of the IP header.

+

+  @return The length of the extension header.

+

+**/

+UINT16

+IpSecGetPlainExtHeadSize (

+  IN VOID                             *IpHead,

+  IN UINT8                            *LastHead

+  )

+{

+  UINT16  Size;

+

+  Size = (UINT16) (LastHead - (UINT8 *) IpHead);

+

+  if (Size > sizeof (EFI_IP6_HEADER)) {

+    //

+    // * (LastHead+1) point the last header's length but not include the first

+    // 8 octers, so this formluation add 8 at the end.

+    //

+    Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8);

+  } else {

+    Size = 0;

+  }

+

+  return Size;

+}

+

+/**

+  Authenticate the IpSec Payload and store the result in the IcvBuffer.

+

+  @param[in]      BufferToAuth    The buffer to be Authenticated.

+  @param[in]      AuthSize        The size of the buffer to be Authenticated.

+  @param[in, out] IcvBuffer       The buffer to store the ICV.

+  @param[in]      IcvSize         The size of ICV.

+  @param[in]      Key             The Key passed to the CryptLib to generate a

+                                  CRYPT_HANDLE.

+  @param[in]      AuthAlgId       The Authentication Algorithm ID.

+

+  @retval EFI_UNSUPPORTED     If the AuthAlg is not in the support list.

+  @retval EFI_SUCCESS         Authenticated the payload successfully.

+  @retval otherwise           Authentication of the payload failed.

+**/

+EFI_STATUS

+IpSecAuthPayload (

+  IN     UINT8                           *BufferToAuth,

+  IN     UINTN                           AuthSize,

+  IN OUT UINT8                           *IcvBuffer,

+  IN     UINTN                           IcvSize,

+  IN     VOID                            *Key,

+  IN     UINT8                           AuthAlgId

+  )

+{

+  switch (AuthAlgId) {

+    case EFI_IPSEC_AALG_NONE :

+    case EFI_IPSEC_AALG_NULL :

+        return EFI_SUCCESS;

+

+    default:

+        return EFI_UNSUPPORTED;

+  }

+}

+

+/**

+  Verify if the Authentication payload is correct.

+

+  @param[in]  EspBuffer          Points to the ESP wrapped buffer.

+  @param[in]  EspSize            The size of the ESP wrapped buffer.

+  @param[in]  SadEntry           The related SAD entry to store the authentication

+                                 algorithm key.

+  @param[in]  IcvSize            The length of ICV.

+

+  @retval EFI_SUCCESS        The authentication data is correct.

+  @retval EFI_ACCESS_DENIED  The authentication data is not correct.

+

+**/

+EFI_STATUS

+IpSecEspAuthVerifyPayload (

+  IN UINT8                           *EspBuffer,

+  IN UINTN                           EspSize,

+  IN IPSEC_SAD_ENTRY                 *SadEntry,

+  IN UINTN                           *IcvSize

+  )

+{

+  EFI_STATUS  Status;

+  UINTN       AuthSize;

+  UINT8       IcvBuffer[12];

+

+  //

+  // Calculate the size of authentication payload.

+  //

+  *IcvSize  = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);

+  AuthSize  = EspSize - *IcvSize;

+

+  //

+  // Calculate the icv buffer and size of the payload.

+  //

+  Status = IpSecAuthPayload (

+             EspBuffer,

+             AuthSize,

+             IcvBuffer,

+             *IcvSize,

+             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,

+             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+  //

+  // Compare the calculated icv and the appended original icv.

+  //

+  if (CompareMem (EspBuffer + AuthSize, IcvBuffer, *IcvSize) == 0) {

+    return EFI_SUCCESS;

+  }

+

+  DEBUG ((DEBUG_ERROR, "Error auth verify payload\n"));

+  return EFI_ACCESS_DENIED;

+}

+

+/**

+  ESP Decrypt the payload.

+

+  @param[in, out] PayloadBuffer      Pointer to the buffer containing the ESP wrapped;

+                                     to be decrypted on input, and plaintext on return. The

+                                     number of bytes of data to be decrypted is

+                                     specified by EncryptSize.

+  @param[in]      EncryptSize        The size of the PayloadBuffer as input.

+  @param[in]      SadEntry           The related SAD entry.

+  @param[in]      IvSize             The size of IV.

+  @param[out]     PlainPayloadSize   Contains the return value of decrypted size.

+  @param[out]     PaddingSize        Contains the return value of Padding size.

+  @param[out]     NextHeader         Contains the return value of the last protocol header

+                                     of the IP packet.

+

+  @retval EFI_UNSUPPORTED    The Algorithm pointed to by the SAD entry is not supported.

+  @retval EFI_SUCCESS        The operation completed successfully.

+

+**/

+EFI_STATUS

+IpSecEspDecryptPayload (

+  IN OUT UINT8                       *PayloadBuffer,

+  IN     UINTN                       EncryptSize,

+  IN     IPSEC_SAD_ENTRY             *SadEntry,

+  IN     UINTN                       *IvSize,

+     OUT UINTN                       *PlainPayloadSize,

+     OUT UINTN                       *PaddingSize,

+     OUT UINT8                       *NextHeader

+  )

+{

+  EFI_ESP_TAIL *EspTail;

+

+  switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {

+    case EFI_IPSEC_EALG_NULL:

+      EspTail            = (EFI_ESP_TAIL *) (PayloadBuffer + EncryptSize - sizeof (EFI_ESP_TAIL));

+      *PaddingSize       = EspTail->PaddingLength;

+      *NextHeader        = EspTail->NextHeader;

+      *PlainPayloadSize  = EncryptSize - EspTail->PaddingLength - sizeof (EFI_ESP_TAIL);

+      break;

+

+    case EFI_IPSEC_EALG_3DESCBC:

+    case EFI_IPSEC_EALG_AESCBC:

+      //

+      // TODO: support these algorithm

+      //

+      return EFI_UNSUPPORTED;

+    default :

+      return EFI_UNSUPPORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  ESP Encrypt the payload.

+

+  @param[in, out] BufferToEncrypt Pointer to the buffer containing plaintext to be

+                                  encrypted on input, and ciphertext on return. The

+                                  number of bytes of data to be encrypted is

+                                  specified by EncryptSize.

+  @param[in, out] EncryptSize     The size of the plaintext on input, and the size of the

+                                  ciphertext on return.

+  @param[in]      IvBuffer        Points to IV data.

+  @param[in]      IvSize          Size of IV.

+  @param[in]      SadEntry        Related SAD entry.

+

+  @retval EFI_UNSUPPORTED    The Algorithm pointed by SAD entry is not supported.

+  @retval EFI_SUCCESS        The operation completed successfully.

+

+**/

+EFI_STATUS

+IpSecEspEncryptPayload (

+  IN OUT UINT8                       *BufferToEncrypt,

+  IN OUT UINTN                       EncryptSize,

+  IN     UINT8                       *IvBuffer,

+  IN     UINTN                       IvSize,

+  IN     IPSEC_SAD_ENTRY             *SadEntry

+  )

+{

+  switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {

+    case EFI_IPSEC_EALG_NULL:

+      return EFI_SUCCESS;

+

+    case EFI_IPSEC_EALG_3DESCBC:

+    case EFI_IPSEC_EALG_AESCBC:

+      //

+      // TODO: support these algorithms

+      //

+      return EFI_UNSUPPORTED;

+    default :

+      return EFI_UNSUPPORTED;

+

+  }

+}

+

+/**

+  The actual entry to relative function processes the inbound traffic of ESP header.

+

+  This function is the subfunction of IpSecProtectInboundPacket(). It checks the

+  received packet security property and trim the ESP header and then returns without

+  an IPsec protected IP Header and FramgmentTable.

+

+  @param[in]      IpVersion          The version of IP.

+  @param[in, out] IpHead             Points to the IP header containing the ESP header

+                                     to be trimed on input, and without ESP header

+                                     on return.

+  @param[out]     LastHead           The Last Header in IP header on return.

+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.

+  @param[in]      OptionsLength      Length of the options buffer. It is optional.

+  @param[in, out] FragmentTable      Pointer to a list of fragments in the form of IPsec

+                                     protected on input, and without IPsec protected

+                                     on return.

+  @param[in]      FragmentCount      The number of fragments.

+  @param[out]     SpdEntry           Pointer to contain the address of SPD entry on return.

+  @param[out]     RecycleEvent       The event for recycling of resources.

+

+  @retval EFI_SUCCESS              The operation was successful.

+  @retval EFI_ACCESS_DENIED        One or more following conditions is TRUE:

+                                   - ESP header was not found.

+                                   - The related SAD entry was not found.

+                                   - The related SAD entry does not support the ESP protocol.

+  @retval EFI_OUT_OF_RESOURCES     The required system resource can't be allocated.

+

+**/

+EFI_STATUS

+IpSecEspInboundPacket (

+  IN     UINT8                       IpVersion,

+  IN OUT VOID                        *IpHead,

+     OUT UINT8                       *LastHead,

+  IN     VOID                        *OptionsBuffer, OPTIONAL

+  IN     UINT32                      OptionsLength,  OPTIONAL

+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,

+  IN     UINT32                      *FragmentCount,

+     OUT IPSEC_SPD_ENTRY             **SpdEntry,

+     OUT EFI_EVENT                   *RecycleEvent

+  )

+{

+  EFI_STATUS            Status;

+  NET_BUF               *Payload;

+  UINTN                 EspSize;

+  UINTN                 IvSize;

+  UINTN                 PlainPayloadSize;

+  UINTN                 PaddingSize;

+  UINTN                 IcvSize;

+  UINT8                 *ProcessBuffer;

+  EFI_IP_ADDRESS        DestIp;

+  EFI_ESP_HEADER        *EspHeader;

+  EFI_ESP_TAIL          *EspTail;

+  EFI_IPSEC_SA_ID       *SaId;

+  IPSEC_SAD_DATA        *SadData;

+  IPSEC_SAD_ENTRY       *SadEntry;

+  IPSEC_RECYCLE_CONTEXT *RecycleContext;

+  UINT32                Spi;

+  UINT8                 NextHeader;

+  UINT16                IpSecHeadSize;

+

+  Status            = EFI_SUCCESS;

+  Payload           = NULL;

+  ProcessBuffer     = NULL;

+  RecycleContext    = NULL;

+  *RecycleEvent     = NULL;

+  PlainPayloadSize  = 0;

+  NextHeader        = 0;

+  //

+  // Build netbuf from fragment table first.

+  //

+  Payload = NetbufFromExt (

+              (NET_FRAGMENT *) *FragmentTable,

+              *FragmentCount,

+              0,

+              sizeof (EFI_ESP_HEADER),

+              IpSecOnRecyclePacket,

+              NULL

+              );

+  if (Payload == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+  //

+  // Get the esp size and eso header from netbuf.

+  //

+  EspSize   = Payload->TotalSize;

+  EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);

+  if (EspHeader == NULL) {

+    Status = EFI_ACCESS_DENIED;

+    goto ON_EXIT;

+  }

+  //

+  // Parse destination address from ip header.

+  //

+  ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));

+  if (IpVersion == IP_VERSION_4) {

+    CopyMem (

+      &DestIp,

+      &((IP4_HEAD *) IpHead)->Dst,

+      sizeof (IP4_ADDR)

+      );

+  } else {

+    CopyMem (

+      &DestIp,

+      &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,

+      sizeof (EFI_IPv6_ADDRESS)

+      );

+  }

+  //

+  // Lookup sad entry according to the spi and dest address.

+  //

+  Spi       = NTOHL (EspHeader->Spi);

+  SadEntry  = IpSecLookupSadBySpi (Spi, &DestIp);

+  if (SadEntry == NULL) {

+    Status = EFI_ACCESS_DENIED;

+    goto ON_EXIT;

+  }

+

+  SaId    = SadEntry->Id;

+  SadData = SadEntry->Data;

+

+  //

+  // Only support esp protocol currently.

+  //

+  if (SaId->Proto != EfiIPsecESP) {

+    Status = EFI_ACCESS_DENIED;

+    goto ON_EXIT;

+  }

+

+  if (!SadData->ManualSet) {

+    //

+    // TODO: Check sa lifetime and sequence number

+    //

+  }

+  //

+  // Allocate buffer for decryption and authentication by esp.

+  //

+  ProcessBuffer = AllocateZeroPool (EspSize);

+  if (ProcessBuffer == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer);

+

+  //

+  // Authenticate the esp wrapped buffer by the sad entry if has auth key.

+  //

+  IcvSize = 0;

+  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {

+    Status = IpSecEspAuthVerifyPayload (

+               ProcessBuffer,

+               EspSize,

+               SadEntry,

+               &IcvSize

+               );

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+  }

+  //

+  // Decrypt the payload by the sad entry if has decrypt key.

+  //

+  IvSize = 0;

+  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {

+    Status = IpSecEspDecryptPayload (

+               ProcessBuffer + sizeof (EFI_ESP_HEADER),

+               EspSize - sizeof (EFI_ESP_HEADER) - IcvSize,

+               SadEntry,

+               &IvSize,

+               &PlainPayloadSize,

+               &PaddingSize,

+               &NextHeader

+               );

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+  } else {

+    EspTail           = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));

+    PaddingSize       = EspTail->PaddingLength;

+    NextHeader        = EspTail->NextHeader;

+    PlainPayloadSize  = EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize - sizeof (EFI_ESP_TAIL) - PaddingSize;

+  }

+  //

+  // TODO: handle anti-replay window

+  //

+  //

+  // Decryption and authentication with esp has been done, so it's time to

+  // reload the new packet, create recycle event and fixup ip header.

+  //

+  RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));

+  if (RecycleContext == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  IpSecRecycleCallback,

+                  RecycleContext,

+                  RecycleEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // TODO: Who take responsible to handle the original fragment table?

+  //

+  *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));

+  if (*FragmentTable == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  RecycleContext->PayloadBuffer       = ProcessBuffer;

+  RecycleContext->FragmentTable       = *FragmentTable;

+  (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;

+  (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;

+  *FragmentCount                      = 1;

+

+  //

+  // Update the total length field in ip header since processed by esp.

+  //

+  if (IpVersion == IP_VERSION_4) {

+    ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + PlainPayloadSize));

+  } else {

+    IpSecHeadSize                              = IpSecGetPlainExtHeadSize (IpHead, LastHead);

+    ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize));

+  }

+  //

+  // Update the next layer field in ip header since esp header inserted.

+  //

+  *LastHead = NextHeader;

+

+  //

+  // Update the spd association of the sad entry.

+  //

+  *SpdEntry = SadData->SpdEntry;

+

+ON_EXIT:

+  if (Payload != NULL) {

+    NetbufFree (Payload);

+  }

+

+  if (EFI_ERROR (Status)) {

+    if (ProcessBuffer != NULL) {

+      FreePool (ProcessBuffer);

+    }

+

+    if (RecycleContext != NULL) {

+      FreePool (RecycleContext);

+    }

+

+    if (*RecycleEvent != NULL) {

+      gBS->CloseEvent (*RecycleEvent);

+    }

+  }

+

+  return Status;

+}

+

+/**

+  The actual entry to the relative function processes the output traffic using the ESP protocol.

+

+  This function is the subfunction of IpSecProtectOutboundPacket(). It protected

+  the sending packet by encrypting its payload and inserting ESP header in the orginal

+  IP header, then return the IpHeader and IPsec protected Fragmentable.

+

+  @param[in]      IpVersion          The version of IP.

+  @param[in, out] IpHead             Points to IP header containing the orginal IP header

+                                     to be processed on input, and inserted ESP header

+                                     on return.

+  @param[in]      LastHead           The Last Header in IP header.

+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.

+  @param[in]      OptionsLength      Length of the options buffer. It is optional.

+  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by

+                                     IPsec on input, and with IPsec protected

+                                     on return.

+  @param[in]      FragmentCount      The number of fragments.

+  @param[in]      SadEntry           The related SAD entry.

+  @param[out]     RecycleEvent       The event for recycling of resources.

+

+  @retval EFI_SUCCESS              The operation was successful.

+  @retval EFI_OUT_OF_RESOURCES     The required system resources can't be allocated.

+

+**/

+EFI_STATUS

+IpSecEspOutboundPacket (

+  IN UINT8                           IpVersion,

+  IN OUT VOID                        *IpHead,

+  IN     UINT8                       *LastHead,

+  IN     VOID                        *OptionsBuffer, OPTIONAL

+  IN     UINT32                      OptionsLength,  OPTIONAL

+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,

+  IN     UINT32                      *FragmentCount,

+  IN     IPSEC_SAD_ENTRY             *SadEntry,

+     OUT EFI_EVENT                   *RecycleEvent

+  )

+{

+  EFI_STATUS            Status;

+  UINTN                 Index;

+  EFI_IPSEC_SA_ID       *SaId;

+  IPSEC_SAD_DATA        *SadData;

+  IPSEC_RECYCLE_CONTEXT *RecycleContext;

+  UINT8                 *ProcessBuffer;

+  UINTN                 BytesCopied;

+  INTN                  EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4

+  UINTN                 EspSize;         // Total size of esp wrapped ip payload

+  UINTN                 IvSize;          // Size of IV, optional, might be 0

+  UINTN                 PlainPayloadSize;// Original IP payload size

+  UINTN                 PaddingSize;     // Size of padding

+  UINTN                 EncryptSize;     // Size of data to be encrypted, start after IV and

+                                         // stop before ICV

+  UINTN                 IcvSize;         // Size of ICV, optional, might be 0

+  UINT8                 *RestOfPayload;  // Start of Payload after IV

+  UINT8                 *Padding;        // Start address of padding

+  EFI_ESP_HEADER        *EspHeader;      // Start address of ESP frame

+  EFI_ESP_TAIL          *EspTail;        // Address behind padding

+

+  Status          = EFI_ACCESS_DENIED;

+  SaId            = SadEntry->Id;

+  SadData         = SadEntry->Data;

+  ProcessBuffer   = NULL;

+  RecycleContext  = NULL;

+  *RecycleEvent   = NULL;

+

+  if (!SadData->ManualSet &&

+      SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL &&

+      SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL

+      ) {

+    //

+    // Invalid manual sad entry configuration.

+    //

+    goto ON_EXIT;

+  }

+  //

+  // Calculate enctrypt block size, need iv by default and 4 bytes alignment.

+  //

+  EncryptBlockSize  = 4;

+

+  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {

+    EncryptBlockSize  = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);

+

+    if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) {

+      goto ON_EXIT;

+    }

+  }

+  //

+  // Calculate the plain payload size accroding to the fragment table.

+  //

+  PlainPayloadSize = 0;

+  for (Index = 0; Index < *FragmentCount; Index++) {

+    PlainPayloadSize += (*FragmentTable)[Index].FragmentLength;

+  }

+  //

+  // Calculate icv size, optional by default and 4 bytes alignment.

+  //

+  IcvSize = 0;

+  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {

+    IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);

+    if (IcvSize % 4 != 0) {

+      goto ON_EXIT;

+    }

+  }

+  //

+  // Calcuate the total size of esp wrapped ip payload.

+  //

+  IvSize        = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);

+  EncryptSize   = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize;

+  PaddingSize   = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL);

+  EspSize       = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize;

+

+  ProcessBuffer = AllocateZeroPool (EspSize);

+  if (ProcessBuffer == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+  //

+  // Calculate esp header and esp tail including header, payload and padding.

+  //

+  EspHeader     = (EFI_ESP_HEADER *) ProcessBuffer;

+  RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize;

+  Padding       = RestOfPayload + PlainPayloadSize;

+  EspTail       = (EFI_ESP_TAIL *) (Padding + PaddingSize);

+

+  //

+  // Fill the sn and spi fields in esp header.

+  //

+  EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1);

+  EspHeader->Spi            = HTONL (SaId->Spi);

+

+  //

+  // Copy the rest of payload (after iv) from the original fragment buffer.

+  //

+  BytesCopied = 0;

+  for (Index = 0; Index < *FragmentCount; Index++) {

+    CopyMem (

+      (RestOfPayload + BytesCopied),

+      (*FragmentTable)[Index].FragmentBuffer,

+      (*FragmentTable)[Index].FragmentLength

+      );

+    BytesCopied += (*FragmentTable)[Index].FragmentLength;

+  }

+  //

+  // Fill the padding buffer by natural number sequence.

+  //

+  for (Index = 0; Index < PaddingSize; Index++) {

+    Padding[Index] = (UINT8) (Index + 1);

+  }

+  //

+  // Fill the padding length and next header fields in esp tail.

+  //

+  EspTail->PaddingLength  = (UINT8) PaddingSize;

+  EspTail->NextHeader     = *LastHead;

+

+  //

+  // Generate iv at random by crypt library.

+  //

+  Status = IpSecGenerateIv (

+             (UINT8 *) (EspHeader + 1),

+             IvSize

+             );

+

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // Encrypt the payload (after iv) by the sad entry if has encrypt key.

+  //

+  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {

+    Status = IpSecEspEncryptPayload (

+               RestOfPayload,

+               EncryptSize,

+               (UINT8 *) (EspHeader + 1),

+               IvSize,

+               SadEntry

+               );

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+  }

+  //

+  // Authenticate the esp wrapped buffer by the sad entry if has auth key.

+  //

+  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {

+    Status = IpSecAuthPayload (

+               ProcessBuffer,

+               EspSize - IcvSize,

+               ProcessBuffer + EspSize - IcvSize,

+               IcvSize,

+               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,

+               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId

+               );

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+  }

+  //

+  // Encryption and authentication with esp has been done, so it's time to

+  // reload the new packet, create recycle event and fixup ip header.

+  //

+  RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));

+  if (RecycleContext == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  IpSecRecycleCallback,

+                  RecycleContext,

+                  RecycleEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // TODO: Who take responsible to handle the original fragment table?

+  //

+  *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));

+  if (*FragmentTable == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  RecycleContext->FragmentTable       = *FragmentTable;

+  RecycleContext->PayloadBuffer       = ProcessBuffer;

+  (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer;

+  (*FragmentTable)[0].FragmentLength  = (UINT32) EspSize;

+  *FragmentCount                      = 1;

+

+  //

+  // Update the total length field in ip header since processed by esp.

+  //

+  if (IpVersion == IP_VERSION_4) {

+    ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + EspSize));

+  } else {

+    ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);

+  }

+  //

+  // Update the next layer field in ip header since esp header inserted.

+  //

+  *LastHead = IPSEC_ESP_PROTOCOL;

+

+  //

+  // Increase the sn number in sad entry according to rfc4303.

+  //

+  SadData->SequenceNumber++;

+

+ON_EXIT:

+  if (EFI_ERROR (Status)) {

+    if (ProcessBuffer != NULL) {

+      FreePool (ProcessBuffer);

+    }

+

+    if (RecycleContext != NULL) {

+      FreePool (RecycleContext);

+    }

+

+    if (*RecycleEvent != NULL) {

+      gBS->CloseEvent (*RecycleEvent);

+    }

+  }

+

+  return Status;

+}

+

+/**

+  This function processes the inbound traffic with IPsec.

+

+  It checks the received packet security property, trims the ESP/AH header, and then

+  returns without an IPsec protected IP Header and FragmentTable.

+

+  @param[in]      IpVersion          The version of IP.

+  @param[in, out] IpHead             Points to IP header containing the ESP/AH header

+                                     to be trimed on input, and without ESP/AH header

+                                     on return.

+  @param[in]      LastHead           The Last Header in IP header on return.

+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.

+  @param[in]      OptionsLength      Length of the options buffer. It is optional.

+  @param[in, out] FragmentTable      Pointer to a list of fragments in form of IPsec

+                                     protected on input, and without IPsec protected

+                                     on return.

+  @param[in]      FragmentCount      The number of fragments.

+  @param[out]     SpdEntry           Pointer to contain the address of SPD entry on return.

+  @param[out]     RecycleEvent       The event for recycling of resources.

+

+  @retval EFI_SUCCESS              The operation was successful.

+  @retval EFI_UNSUPPORTED          The IPSEC protocol is not supported.

+

+**/

+EFI_STATUS

+IpSecProtectInboundPacket (

+  IN     UINT8                       IpVersion,

+  IN OUT VOID                        *IpHead,

+  IN     UINT8                       *LastHead,

+  IN     VOID                        *OptionsBuffer, OPTIONAL

+  IN     UINT32                      OptionsLength,  OPTIONAL

+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,

+  IN     UINT32                      *FragmentCount,

+     OUT IPSEC_SPD_ENTRY             **SpdEntry,

+     OUT EFI_EVENT                   *RecycleEvent

+  )

+{

+  if (*LastHead == IPSEC_ESP_PROTOCOL) {

+    //

+    // Process the esp ipsec header of the inbound traffic.

+    //

+    return IpSecEspInboundPacket (

+             IpVersion,

+             IpHead,

+             LastHead,

+             OptionsBuffer,

+             OptionsLength,

+             FragmentTable,

+             FragmentCount,

+             SpdEntry,

+             RecycleEvent

+             );

+  }

+  //

+  // The other protocols are not supported.

+  //

+  return EFI_UNSUPPORTED;

+}

+

+/**

+  This function processes the output traffic with IPsec.

+

+  It protected the sending packet by encrypting it payload and inserting ESP/AH header

+  in the orginal IP header, then returns the IpHeader and IPsec protected Fragmentable.

+

+  @param[in]      IpVersion          The version of IP.

+  @param[in, out] IpHead             Points to IP header containing the orginal IP header

+                                     to be processed on input, and inserted ESP/AH header

+                                     on return.

+  @param[in]      LastHead           The Last Header in the IP header.

+  @param[in]      OptionsBuffer      Pointer to the options buffer. It is optional.

+  @param[in]      OptionsLength      Length of the options buffer. It is optional.

+  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by

+                                     IPsec on input, and with IPsec protected

+                                     on return.

+  @param[in]      FragmentCount      The number of fragments.

+  @param[in]      SadEntry           The related SAD entry.

+  @param[out]     RecycleEvent       The event for recycling of resources.

+

+  @retval EFI_SUCCESS              The operation was successful.

+  @retval EFI_UNSUPPORTED          If the IPSEC protocol is not supported.

+

+**/

+EFI_STATUS

+IpSecProtectOutboundPacket (

+  IN     UINT8                       IpVersion,

+  IN OUT VOID                        *IpHead,

+  IN     UINT8                       *LastHead,

+  IN     VOID                        *OptionsBuffer, OPTIONAL

+  IN     UINT32                      OptionsLength,  OPTIONAL

+  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,

+  IN     UINT32                      *FragmentCount,

+  IN     IPSEC_SAD_ENTRY             *SadEntry,

+     OUT EFI_EVENT                   *RecycleEvent

+  )

+{

+  if (SadEntry->Id->Proto == EfiIPsecESP) {

+    //

+    // Process the esp ipsec header of the outbound traffic.

+    //

+    return IpSecEspOutboundPacket (

+             IpVersion,

+             IpHead,

+             LastHead,

+             OptionsBuffer,

+             OptionsLength,

+             FragmentTable,

+             FragmentCount,

+             SadEntry,

+             RecycleEvent

+             );

+  }

+  //

+  // The other protocols are not supported.

+  //

+  return EFI_UNSUPPORTED;

+}

diff --git a/NetworkPkg/Mtftp6Dxe/ComponentName.c b/NetworkPkg/Mtftp6Dxe/ComponentName.c
new file mode 100644
index 0000000..0f91b64
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/ComponentName.c
@@ -0,0 +1,308 @@
+/** @file

+  UEFI Component Name(2) protocol implementation for Mtftp6 driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Mtftp6Impl.h"

+

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for bus drivers

+                                attempting to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that attempts to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,

+  IN  EFI_HANDLE                                      ControllerHandle,

+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,

+  IN  CHAR8                                           *Language,

+  OUT CHAR16                                          **ControllerName

+  );

+

+//

+// EFI Component Name Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL     gMtftp6ComponentName = {

+  Mtftp6ComponentNameGetDriverName,

+  Mtftp6ComponentNameGetControllerName,

+  "eng"

+};

+

+//

+// EFI Component Name 2 Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL    gMtftp6ComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp6ComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp6ComponentNameGetControllerName,

+  "en"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE        mMtftp6DriverNameTable[] = {

+  {

+    "eng;en",

+    L"MTFTP6 Network Service Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+          Language,

+          This->SupportedLanguages,

+          mMtftp6DriverNameTable,

+          DriverName,

+          (BOOLEAN)(This == &gMtftp6ComponentName)

+          );

+}

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                attempting to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that attempts to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,

+  IN  EFI_HANDLE                                      ControllerHandle,

+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,

+  IN  CHAR8                                           *Language,

+  OUT CHAR16                                          **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c
new file mode 100644
index 0000000..d448c78
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c
@@ -0,0 +1,703 @@
+/** @file

+  Driver Binding functions and Service Binding functions

+  implementation for Mtftp6 Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Mtftp6Impl.h"

+

+

+EFI_DRIVER_BINDING_PROTOCOL   gMtftp6DriverBinding = {

+  Mtftp6DriverBindingSupported,

+  Mtftp6DriverBindingStart,

+  Mtftp6DriverBindingStop,

+  0xa,

+  NULL,

+  NULL

+};

+

+EFI_SERVICE_BINDING_PROTOCOL  gMtftp6ServiceBindingTemplate = {

+  Mtftp6ServiceBindingCreateChild,

+  Mtftp6ServiceBindingDestroyChild

+};

+

+

+/**

+  Destory the MTFTP6 service. The MTFTP6 service may be partly initialized,

+  or partly destroyed. If a resource is destroyed, it is marked as such in

+  case the destroy failed and is called again later.

+

+  @param[in]  Service            The MTFTP6 service to be destroyed.

+

+**/

+VOID

+Mtftp6DestroyService (

+  IN MTFTP6_SERVICE     *Service

+  )

+{

+  //

+  // Make sure all children instances have been already destoryed.

+  //

+  ASSERT (Service->ChildrenNum == 0);

+

+  if (Service->DummyUdpIo != NULL) {

+    UdpIoFreeIo (Service->DummyUdpIo);

+  }

+

+  if (Service->Timer != NULL) {

+    gBS->CloseEvent (Service->Timer);

+  }

+

+  FreePool (Service);

+}

+

+

+/**

+  Create then initialize a MTFTP6 service binding instance.

+

+  @param[in]  Controller             The controller to install the MTFTP6 service

+                                     binding on.

+  @param[in]  Image                  The driver binding image of the MTFTP6 driver.

+  @param[out] Service                The variable to receive the created service

+                                     binding instance.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to create the instance

+  @retval EFI_DEVICE_ERROR       Failed to create a NULL UDP port to keep connection with UDP.

+  @retval EFI_SUCCESS            The service instance is created for the controller.

+

+**/

+EFI_STATUS

+Mtftp6CreateService (

+  IN  EFI_HANDLE            Controller,

+  IN  EFI_HANDLE            Image,

+  OUT MTFTP6_SERVICE        **Service

+  )

+{

+  MTFTP6_SERVICE            *Mtftp6Srv;

+  EFI_STATUS                Status;

+

+  ASSERT (Service != NULL);

+

+  *Service  = NULL;

+  Mtftp6Srv = AllocateZeroPool (sizeof (MTFTP6_SERVICE));

+

+  if (Mtftp6Srv == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Mtftp6Srv->Signature      = MTFTP6_SERVICE_SIGNATURE;

+  Mtftp6Srv->Controller     = Controller;

+  Mtftp6Srv->Image          = Image;

+  Mtftp6Srv->InDestory      = FALSE;

+  Mtftp6Srv->ChildrenNum    = 0;

+

+  CopyMem (

+    &Mtftp6Srv->ServiceBinding,

+    &gMtftp6ServiceBindingTemplate,

+    sizeof (EFI_SERVICE_BINDING_PROTOCOL)

+    );

+

+  InitializeListHead (&Mtftp6Srv->Children);

+

+  //

+  // Create a internal timer for all instances.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL | EVT_TIMER,

+                  TPL_CALLBACK,

+                  Mtftp6OnTimerTick,

+                  Mtftp6Srv,

+                  &Mtftp6Srv->Timer

+                  );

+

+  if (EFI_ERROR (Status)) {

+    FreePool (Mtftp6Srv);

+    return Status;

+  }

+

+  //

+  // Create a dummy Udp6Io to build parent-child relationship between Udp6 driver

+  // and Mtftp6 driver.

+  //

+  Mtftp6Srv->DummyUdpIo = UdpIoCreateIo (

+                            Controller,

+                            Image,

+                            Mtftp6ConfigDummyUdpIo,

+                            UDP_IO_UDP6_VERSION,

+                            NULL

+                            );

+

+  if (Mtftp6Srv->DummyUdpIo == NULL) {

+    gBS->CloseEvent (Mtftp6Srv->Timer);

+    FreePool (Mtftp6Srv);

+    return EFI_DEVICE_ERROR;

+  }

+

+  *Service = Mtftp6Srv;

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Destroy the MTFTP6 instance and recycle the resources.

+

+  @param[in]  Instance        The pointer to the MTFTP6 instance.

+

+**/

+VOID

+Mtftp6DestroyInstance (

+  IN MTFTP6_INSTANCE         *Instance

+  )

+{

+  LIST_ENTRY                 *Entry;

+  LIST_ENTRY                 *Next;

+  MTFTP6_BLOCK_RANGE         *Block;

+

+  if (Instance->Config != NULL) {

+    FreePool (Instance->Config);

+  }

+

+  if (Instance->Token != NULL && Instance->Token->Event != NULL) {

+    gBS->SignalEvent (Instance->Token->Event);

+  }

+

+  if (Instance->LastPacket != NULL) {

+    NetbufFree (Instance->LastPacket);

+  }

+

+  if (Instance->UdpIo!= NULL) {

+    UdpIoFreeIo (Instance->UdpIo);

+  }

+

+  if (Instance->McastUdpIo != NULL) {

+    UdpIoFreeIo (Instance->McastUdpIo);

+  }

+

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {

+    Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);

+    RemoveEntryList (Entry);

+    FreePool (Block);

+  }

+

+  FreePool (Instance);

+}

+

+

+/**

+  Create the MTFTP6 instance and initialize it.

+

+  @param[in]  Service              The pointer to the MTFTP6 service.

+  @param[out] Instance             The pointer to the MTFTP6 instance.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.

+  @retval EFI_SUCCESS            The MTFTP6 instance is created.

+

+**/

+EFI_STATUS

+Mtftp6CreateInstance (

+  IN  MTFTP6_SERVICE         *Service,

+  OUT MTFTP6_INSTANCE        **Instance

+  )

+{

+  MTFTP6_INSTANCE            *Mtftp6Ins;

+

+  *Instance = NULL;

+  Mtftp6Ins = AllocateZeroPool (sizeof (MTFTP6_INSTANCE));

+

+  if (Mtftp6Ins == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Mtftp6Ins->Signature = MTFTP6_INSTANCE_SIGNATURE;

+  Mtftp6Ins->InDestory = FALSE;

+  Mtftp6Ins->Service   = Service;

+

+  CopyMem (

+    &Mtftp6Ins->Mtftp6,

+    &gMtftp6ProtocolTemplate,

+    sizeof (EFI_MTFTP6_PROTOCOL)

+    );

+

+  InitializeListHead (&Mtftp6Ins->Link);

+  InitializeListHead (&Mtftp6Ins->BlkList);

+

+  *Instance = Mtftp6Ins;

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  This is the declaration of an EFI image entry point. This entry point is

+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including

+  both device drivers and bus drivers.

+

+  Entry point of the MTFTP6 driver to install various protocols.

+

+  @param[in]  ImageHandle           The firmware allocated handle for the UEFI image.

+  @param[in]  SystemTable           The pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The operation completed successfully.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6DriverEntryPoint (

+  IN EFI_HANDLE             ImageHandle,

+  IN EFI_SYSTEM_TABLE       *SystemTable

+  )

+{

+  return EfiLibInstallDriverBindingComponentName2 (

+           ImageHandle,

+           SystemTable,

+           &gMtftp6DriverBinding,

+           ImageHandle,

+           &gMtftp6ComponentName,

+           &gMtftp6ComponentName2

+           );

+}

+

+

+/**

+  Test to see if this driver supports ControllerHandle. This service

+  is called by the EFI boot service ConnectController(). In

+  order to make drivers as small as possible, there are calling

+  restrictions for this service. ConnectController() must

+  follow these calling restrictions. If any other agent wishes to call

+  Supported(), it must also follow these calling restrictions.

+

+  @param[in]  This                Protocol instance pointer.

+  @param[in]  ControllerHandle    Handle of device to test

+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child.

+                                  device to start.

+

+  @retval EFI_SUCCESS         This driver supports this device.

+  @retval Others              This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6DriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,

+  IN EFI_HANDLE                     Controller,

+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath

+  )

+{

+  return gBS->OpenProtocol (

+                Controller,

+                &gEfiUdp6ServiceBindingProtocolGuid,

+                NULL,

+                This->DriverBindingHandle,

+                Controller,

+                EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                );

+}

+

+

+/**

+  Start this driver on ControllerHandle. This service is called by the

+  EFI boot service ConnectController(). In order to make

+  drivers as small as possible, there are calling restrictions for

+  this service. ConnectController() must follow these

+  calling restrictions. If any other agent wishes to call Start() it

+  must also follow these calling restrictions.

+

+  @param[in]  This                 Protocol instance pointer.

+  @param[in]  ControllerHandle     Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child

+                                   device to start.

+

+  @retval EFI_SUCCESS          This driver is added to ControllerHandle.

+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.

+  @retval Others               This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6DriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  )

+{

+  MTFTP6_SERVICE            *Service;

+  EFI_STATUS                Status;

+

+  //

+  // Directly return if driver is already running on this Nic handle.

+  //

+  Status = gBS->OpenProtocol (

+                  Controller,

+                  &gEfiMtftp6ServiceBindingProtocolGuid,

+                  NULL,

+                  This->DriverBindingHandle,

+                  Controller,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+

+  if (!EFI_ERROR (Status)) {

+    return EFI_ALREADY_STARTED;

+  }

+

+  //

+  // Create Mtftp6 service for this Nic handle

+  //

+  Status = Mtftp6CreateService (

+             Controller,

+             This->DriverBindingHandle,

+             &Service

+             );

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  ASSERT (Service != NULL);

+

+  //

+  // Start the internal timer to track the packet retransmission.

+  //

+  Status = gBS->SetTimer (

+                  Service->Timer,

+                  TimerPeriodic,

+                  TICKS_PER_SECOND

+                  );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Install the Mtftp6 service on the Nic handle.

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &Controller,

+                  &gEfiMtftp6ServiceBindingProtocolGuid,

+                  &Service->ServiceBinding,

+                  NULL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  Mtftp6DestroyService (Service);

+  return Status;

+}

+

+

+/**

+  Stop this driver on ControllerHandle. This service is called by the

+  EFI boot service DisconnectController(). In order to

+  make drivers as small as possible, there are a few calling

+  restrictions for this service. DisconnectController()

+  must follow these calling restrictions. If any other agent wishes

+  to call Stop() it must also follow these calling restrictions.

+

+  @param[in]  This              Protocol instance pointer.

+  @param[in]  ControllerHandle  Handle of device to stop driver on

+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of

+                                children is zero, stop the entire bus driver.

+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS       This driver is removed ControllerHandle.

+  @retval EFI_DEVICE_ERROR  An unexpected error.

+  @retval Others            This driver was not removed from this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6DriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL *This,

+  IN  EFI_HANDLE                  Controller,

+  IN  UINTN                       NumberOfChildren,

+  IN  EFI_HANDLE                  *ChildHandleBuffer

+  )

+{

+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;

+  MTFTP6_SERVICE                *Service;

+  MTFTP6_INSTANCE               *Instance;

+  EFI_HANDLE                    NicHandle;

+  EFI_STATUS                    Status;

+  EFI_TPL                       OldTpl;

+

+  //

+  // Locate the Nic handle to retrieve the Mtftp6 private data.

+  //

+  NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp6ProtocolGuid);

+

+  if (NicHandle == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  NicHandle,

+                  &gEfiMtftp6ServiceBindingProtocolGuid,

+                  (VOID **) &ServiceBinding,

+                  This->DriverBindingHandle,

+                  NicHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  Service = MTFTP6_SERVICE_FROM_THIS (ServiceBinding);

+

+  if (Service->InDestory) {

+    return EFI_SUCCESS;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (NumberOfChildren == 0) {

+    //

+    // Destory the Mtftp6 service if there is no Mtftp6 child instance left.

+    //

+    Service->InDestory = TRUE;

+

+    gBS->UninstallProtocolInterface (

+           NicHandle,

+           &gEfiMtftp6ServiceBindingProtocolGuid,

+           ServiceBinding

+           );

+

+    Mtftp6DestroyService (Service);

+

+  } else {

+    //

+    // Destory the Mtftp6 child instance one by one.

+    //

+    while (!IsListEmpty (&Service->Children)) {

+      Instance = NET_LIST_HEAD (&Service->Children, MTFTP6_INSTANCE, Link);

+      Mtftp6ServiceBindingDestroyChild (ServiceBinding, Instance->Handle);

+    }

+

+    if (Service->ChildrenNum != 0) {

+      Status = EFI_DEVICE_ERROR;

+    }

+  }

+

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+

+/**

+  Creates a child handle and installs a protocol.

+

+  The CreateChild() function installs a protocol on ChildHandle.

+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.

+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.

+

+  @param[in]      This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,

+                              then a new handle is created. If it is a pointer to an existing

+                              UEFI handle, then the protocol is added to the existing UEFI handle.

+

+  @retval EFI_SUCCES            The protocol was added to ChildHandle.

+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.

+  @retval Others                The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ServiceBindingCreateChild (

+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN OUT EFI_HANDLE                    *ChildHandle

+  )

+{

+  MTFTP6_SERVICE            *Service;

+  MTFTP6_INSTANCE           *Instance;

+  EFI_STATUS                Status;

+  EFI_TPL                   OldTpl;

+  VOID                      *Udp6;

+

+  if (This == NULL || ChildHandle == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Service = MTFTP6_SERVICE_FROM_THIS (This);

+

+  Status = Mtftp6CreateInstance (Service, &Instance);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  ASSERT (Instance != NULL);

+

+  //

+  // Install the Mtftp6 protocol on the new child handle.

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  ChildHandle,

+                  &gEfiMtftp6ProtocolGuid,

+                  &Instance->Mtftp6,

+                  NULL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Instance->Handle = *ChildHandle;

+

+  //

+  // Open the Udp6 protocol by child.

+  //

+  Status = gBS->OpenProtocol (

+                  Service->DummyUdpIo->UdpHandle,

+                  &gEfiUdp6ProtocolGuid,

+                  (VOID **) &Udp6,

+                  gMtftp6DriverBinding.DriverBindingHandle,

+                  Instance->Handle,

+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                  );

+

+  if (EFI_ERROR (Status)) {

+    gBS->UninstallMultipleProtocolInterfaces (

+           Instance->Handle,

+           &gEfiMtftp6ProtocolGuid,

+           &Instance->Mtftp6,

+           NULL

+           );

+

+    goto ON_ERROR;

+  }

+

+  //

+  // Add the new Mtftp6 instance into the children list of Mtftp6 service.

+  //

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  InsertTailList (&Service->Children, &Instance->Link);

+  Service->ChildrenNum++;

+

+  gBS->RestoreTPL (OldTpl);

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  Mtftp6DestroyInstance (Instance);

+  return Status;

+}

+

+

+/**

+  Destroys a child handle with a protocol installed on it.

+

+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol

+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the

+  last protocol on ChildHandle, then ChildHandle is destroyed.

+

+  @param[in]  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in]  ChildHandle Handle of the child to destroy.

+

+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.

+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.

+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.

+  @retval Others                The child handle was not destroyed

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL *This,

+  IN EFI_HANDLE                   ChildHandle

+  )

+{

+  MTFTP6_SERVICE            *Service;

+  MTFTP6_INSTANCE           *Instance;

+  EFI_MTFTP6_PROTOCOL       *Mtftp6;

+  EFI_STATUS                Status;

+  EFI_TPL                   OldTpl;

+

+  if (This == NULL || ChildHandle == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Locate the Nic handle to retrieve the Mtftp6 private data.

+  //

+  Status = gBS->OpenProtocol (

+                  ChildHandle,

+                  &gEfiMtftp6ProtocolGuid,

+                  (VOID **) &Mtftp6,

+                  gMtftp6DriverBinding.DriverBindingHandle,

+                  ChildHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Instance = MTFTP6_INSTANCE_FROM_THIS (Mtftp6);

+  Service  = MTFTP6_SERVICE_FROM_THIS (This);

+

+  if (Instance->Service != Service) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Check whether the instance already in destory state.

+  //

+  if (Instance->InDestory) {

+    return EFI_SUCCESS;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  Instance->InDestory = TRUE;

+

+  gBS->CloseProtocol (

+         Service->DummyUdpIo->UdpHandle,

+         &gEfiUdp6ProtocolGuid,

+         gMtftp6DriverBinding.DriverBindingHandle,

+         ChildHandle

+         );

+

+  //

+  // Uninstall the MTFTP6 protocol first to enable a top down destruction.

+  //

+  Status = gBS->UninstallProtocolInterface (

+                  ChildHandle,

+                  &gEfiMtftp6ProtocolGuid,

+                  Mtftp6

+                  );

+

+  if (EFI_ERROR (Status)) {

+    Instance->InDestory = FALSE;

+    gBS->RestoreTPL (OldTpl);

+    return Status;

+  }

+

+  //

+  // Remove the Mtftp6 instance from the children list of Mtftp6 service.

+  //

+  RemoveEntryList (&Instance->Link);

+  Service->ChildrenNum --;

+

+  Mtftp6DestroyInstance (Instance);

+

+  gBS->RestoreTPL (OldTpl);

+

+  return EFI_SUCCESS;

+}

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h
new file mode 100644
index 0000000..94f73a8
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h
@@ -0,0 +1,151 @@
+/** @file

+  Driver Binding functions and Service Binding functions

+  declaration for Mtftp6 Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_MTFTP6_DRIVER_H__

+#define __EFI_MTFTP6_DRIVER_H__

+

+#include <Protocol/ServiceBinding.h>

+

+extern EFI_COMPONENT_NAME_PROTOCOL  gMtftp6ComponentName;

+extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2;

+

+/**

+  Test to see if this driver supports ControllerHandle. This service

+  is called by the EFI boot service ConnectController(). In

+  order to make drivers as small as possible, there are a few calling

+  restrictions for this service. ConnectController() must

+  follow these calling restrictions. If any other agent wishes to call

+  Supported(), it must also follow these calling restrictions.

+

+  @param[in]  This                Protocol instance pointer.

+  @param[in]  ControllerHandle    Handle of device to test.

+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child

+                                  device to start.

+

+  @retval EFI_SUCCESS         This driver supports this device.

+  @retval Others              This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6DriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+/**

+  Start this driver on ControllerHandle. This service is called by the

+  EFI boot service ConnectController(). In order to make

+  drivers as small as possible, there are calling restrictions for

+  this service. ConnectController() must follow these

+  calling restrictions. If any other agent wishes to call Start() it

+  must also follow these calling restrictions.

+

+  @param[in]  This                 Protocol instance pointer.

+  @param[in]  ControllerHandle     Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child

+                                   device to start.

+

+  @retval EFI_SUCCESS          This driver is added to ControllerHandle.

+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.

+  @retval Others               This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6DriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   Controller,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath

+  );

+

+/**

+  Stop this driver on ControllerHandle. This service is called by the

+  EFI boot service DisconnectController(). In order to

+  make drivers as small as possible, there are calling

+  restrictions for this service. DisconnectController()

+  must follow these calling restrictions. If any other agent wishes

+  to call Stop(), it must also follow these calling restrictions.

+

+  @param[in]  This              Protocol instance pointer.

+  @param[in]  ControllerHandle  Handle of device to stop driver on

+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of

+                                children is zero, stop the entire bus driver.

+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS       This driver is removed ControllerHandle.

+  @retval EFI_DEVICE_ERROR  An unexpected error.

+  @retval Others            This driver was not removed from this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6DriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL *This,

+  IN  EFI_HANDLE                  Controller,

+  IN  UINTN                       NumberOfChildren,

+  IN  EFI_HANDLE                  *ChildHandleBuffer

+  );

+

+/**

+  Creates a child handle and installs a protocol.

+

+  The CreateChild() function installs a protocol on ChildHandle.

+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.

+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.

+

+  @param[in]      This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,

+                              then a new handle is created. If it is a pointer to an existing

+                              UEFI handle, then the protocol is added to the existing UEFI handle.

+

+  @retval EFI_SUCCES            The protocol was added to ChildHandle.

+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.

+  @retval Others                The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ServiceBindingCreateChild (

+  IN     EFI_SERVICE_BINDING_PROTOCOL *This,

+  IN OUT EFI_HANDLE                   *ChildHandle

+  );

+

+/**

+  Destroys a child handle with a protocol installed on it.

+

+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol

+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the

+  last protocol on ChildHandle, then ChildHandle is destroyed.

+

+  @param[in]  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in]  ChildHandle Handle of the child to destroy.

+

+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.

+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.

+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.

+  @retval Others                The child handle was not destroyed

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL *This,

+  IN EFI_HANDLE                   ChildHandle

+  );

+

+#endif

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf b/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf
new file mode 100644
index 0000000..ecf1f7c
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf
@@ -0,0 +1,69 @@
+## @file

+#  Component description file for Mtftp6 module.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = Mtftp6Dxe

+  FILE_GUID                      = 99F03B99-98D8-49dd-A8D3-3219D0FFE41E

+  MODULE_TYPE                    = UEFI_DRIVER

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = Mtftp6DriverEntryPoint

+  UNLOAD_IMAGE                   = NetLibDefaultUnload

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC

+#

+#  DRIVER_BINDING                =  gMtftp6DriverBinding

+#  COMPONENT_NAME                =  gMtftp6ComponentName

+#  COMPONENT_NAME2               =  gMtftp6ComponentName2

+#

+

+[Sources]

+  Mtftp6Driver.c

+  Mtftp6Driver.h

+  Mtftp6Impl.c

+  Mtftp6Impl.h

+  Mtftp6Option.c

+  Mtftp6Option.h

+  Mtftp6Support.h

+  Mtftp6Support.c

+  Mtftp6Rrq.c

+  Mtftp6Wrq.c

+  ComponentName.c

+

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+

+

+[LibraryClasses]

+  UefiLib

+  BaseLib

+  UefiBootServicesTableLib

+  UefiRuntimeServicesTableLib

+  UefiDriverEntryPoint

+  DebugLib

+  NetLib

+  UdpIoLib

+

+

+[Protocols]

+  gEfiUdp6ServiceBindingProtocolGuid

+  gEfiUdp6ProtocolGuid

+  gEfiMtftp6ServiceBindingProtocolGuid

+  gEfiMtftp6ProtocolGuid

+

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c
new file mode 100644
index 0000000..fcbbf11
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c
@@ -0,0 +1,634 @@
+/** @file

+  This EFI_MTFTP6_PROTOCOL interface implementation.

+

+  It supports the following RFCs:

+   RFC1350 - THE TFTP PROTOCOL (REVISION 2)

+   RFC2090 - TFTP Multicast Option

+   RFC2347 - TFTP Option Extension

+   RFC2348 - TFTP Blocksize Option

+   RFC2349 - TFTP Timeout Interval and Transfer Size Options

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Mtftp6Impl.h"

+

+EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate = {

+  EfiMtftp6GetModeData,

+  EfiMtftp6Configure,

+  EfiMtftp6GetInfo,

+  EfiMtftp6ParseOptions,

+  EfiMtftp6ReadFile,

+  EfiMtftp6WriteFile,

+  EfiMtftp6ReadDirectory,

+  EfiMtftp6Poll

+  };

+

+/**

+  Returns the current operating mode data for the MTFTP6 instance.

+

+  The GetModeData() function returns the current operating mode and

+  cached data packet for the MTFTP6 instance.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[out] ModeData           The buffer in which the EFI MTFTPv6 Protocol driver mode

+                                 data is returned.

+

+  @retval  EFI_SUCCESS           The configuration data was returned successfully.

+  @retval  EFI_OUT_OF_RESOURCES  The required mode data could not be allocated.

+  @retval  EFI_INVALID_PARAMETER This is NULL or ModeData is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6GetModeData (

+  IN  EFI_MTFTP6_PROTOCOL    *This,

+  OUT EFI_MTFTP6_MODE_DATA   *ModeData

+  )

+{

+  MTFTP6_INSTANCE  *Instance;

+  EFI_TPL          OldTpl;

+

+  if (This == NULL || ModeData == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  OldTpl   = gBS->RaiseTPL (TPL_CALLBACK);

+  Instance = MTFTP6_INSTANCE_FROM_THIS (This);

+

+  //

+  // Copy back the configure data if the instance already configured.

+  //

+  if (Instance->Config != NULL) {

+    CopyMem (

+      &ModeData->ConfigData,

+      Instance->Config,

+      sizeof (EFI_MTFTP6_CONFIG_DATA)

+      );

+  } else {

+    ZeroMem (

+      &ModeData->ConfigData,

+      sizeof (EFI_MTFTP6_CONFIG_DATA)

+      );

+  }

+

+  //

+  // Set the current support options in mode data.

+  //

+  ModeData->SupportedOptionCount = MTFTP6_SUPPORTED_OPTIONS_NUM;

+  ModeData->SupportedOptions     = (UINT8 **) mMtftp6SupportedOptions;

+

+  gBS->RestoreTPL (OldTpl);

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Initializes, changes, or resets the default operational setting for

+  this EFI MTFTPv6 Protocol driver instance.

+

+  The Configure() function is used to set and change the configuration

+  data for this EFI MTFTPv6 Protocol driver instance. The configuration

+  data can be reset to startup defaults by calling Configure() with

+  MtftpConfigData set to NULL. Whenever the instance is reset, any

+  pending operation is aborted. By changing the EFI MTFTPv6 Protocol

+  driver instance configuration data, the client can connect to

+  different MTFTPv6 servers. The configuration parameters in

+  MtftpConfigData are used as the default parameters in later MTFTPv6

+  operations and can be overridden in later operations.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  MtftpConfigData    Pointer to the configuration data structure.

+

+  @retval  EFI_SUCCESS           The EFI MTFTPv6 Protocol instance was configured successfully.

+  @retval  EFI_INVALID_PARAMETER One or more following conditions are TRUE:

+                                 - This is NULL.

+                                 - MtftpConfigData.StationIp is neither zero nor one

+                                   of the configured IP addresses in the underlying IPv6 driver.

+                                 - MtftpCofigData.ServerIp is not a valid IPv6 unicast address.

+                                 Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_ACCESS_DENIED     - The configuration could not be changed at this time because there

+                                   is some MTFTP background operation in progress.

+                                 - MtftpCofigData.LocalPort is already in use.

+                                 Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source

+                                 address for this instance, but no source address was available for use.

+  @retval  EFI_OUT_OF_RESOURCES  The EFI MTFTPv6 Protocol driver instance data could not be

+                                 allocated.

+                                 Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred. The EFI

+                                 MTFTPv6 Protocol driver instance is not configured.

+                                 Note: It is not defined in the UEFI 2.3 Specification.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6Configure (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData     OPTIONAL

+  )

+{

+  MTFTP6_SERVICE            *Service;

+  MTFTP6_INSTANCE           *Instance;

+  EFI_UDP6_PROTOCOL         *Udp6;

+  EFI_UDP6_CONFIG_DATA      Udp6Cfg;

+  EFI_STATUS                Status;

+  EFI_TPL                   OldTpl;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (MtftpConfigData != NULL && !NetIp6IsValidUnicast (&MtftpConfigData->ServerIp)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  OldTpl   = gBS->RaiseTPL (TPL_CALLBACK);

+  Instance = MTFTP6_INSTANCE_FROM_THIS (This);

+  Service  = Instance->Service;

+  Status   = EFI_SUCCESS;

+

+  if (MtftpConfigData == NULL) {

+    //

+    // Configure the instance as NULL to abort the current session.

+    //

+    Mtftp6OperationClean (Instance, EFI_ABORTED);

+    FreePool (Instance->Config);

+    Instance->Config = NULL;

+  } else {

+    //

+    // It's not allowed to configure one instance twice without configure null.

+    //

+    if (Instance->Config != NULL) {

+      Status = EFI_ACCESS_DENIED;

+      goto ON_EXIT;

+    }

+    //

+    // Allocate the configure buffer of the instance and store the user's data.

+    //

+    Instance->Config = AllocateZeroPool (sizeof (EFI_MTFTP6_CONFIG_DATA));

+

+    if (Instance->Config == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_EXIT;

+    }

+

+    CopyMem (Instance->Config, MtftpConfigData, sizeof (EFI_MTFTP6_CONFIG_DATA));

+

+    //

+    // Don't configure the udpio here because each operation might override

+    // the configuration, so delay udpio configuration in each operation.

+    //

+    Instance->UdpIo = UdpIoCreateIo (

+                        Service->Controller,

+                        Service->Image,

+                        Mtftp6ConfigDummyUdpIo,

+                        UDP_IO_UDP6_VERSION,

+                        NULL

+                        );

+

+    if (Instance->UdpIo == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_EXIT;

+    }

+

+    //

+    // Continue to configure the downside Udp6 instance by user's data.

+    //

+    ZeroMem (&Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));

+

+    Udp6Cfg.AcceptPromiscuous  = FALSE;

+    Udp6Cfg.AcceptAnyPort      = FALSE;

+    Udp6Cfg.AllowDuplicatePort = FALSE;

+    Udp6Cfg.TrafficClass       = 0;

+    Udp6Cfg.HopLimit           = 128;

+    Udp6Cfg.ReceiveTimeout     = 0;

+    Udp6Cfg.TransmitTimeout    = 0;

+    Udp6Cfg.StationPort        = Instance->Config->LocalPort;

+    Udp6Cfg.RemotePort         = Instance->Config->InitialServerPort;

+

+    CopyMem (

+      &Udp6Cfg.StationAddress,

+      &Instance->Config->StationIp,

+      sizeof(EFI_IPv6_ADDRESS)

+      );

+

+    CopyMem (

+      &Udp6Cfg.RemoteAddress,

+      &Instance->Config->ServerIp,

+      sizeof (EFI_IPv6_ADDRESS)

+      );

+

+    Udp6   = Instance->UdpIo->Protocol.Udp6;

+    Status = Udp6->Configure (Udp6, &Udp6Cfg);

+

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+  }

+

+ON_EXIT:

+  if (EFI_ERROR (Status)) {

+    if (Instance->Config != NULL) {

+      FreePool (Instance->Config);

+      Instance->Config = NULL;

+    }

+    if (Instance->UdpIo != NULL) {

+      UdpIoFreeIo (Instance->UdpIo);

+      Instance->UdpIo = NULL;

+    }

+  }

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+

+/**

+  Get the information of the download from the server.

+

+  The GetInfo() function assembles an MTFTPv6 request packet

+  with options, sends it to the MTFTPv6 server, and may return

+  an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries

+  occur only if no response packets are received from the MTFTPv6

+  server before the timeout expires.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  OverrideData       Data that is used to override the existing parameters. If NULL, the

+                                 default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure()

+                                 function are used.

+  @param[in]  Filename           Pointer to ASCIIZ file name string.

+  @param[in]  ModeStr            Pointer to ASCIIZ mode string. If NULL, octet will be used.

+  @param[in]  OptionCount        Number of option/value string pairs in OptionList.

+  @param[in]  OptionList         Pointer to array of option/value string pairs. Ignored if

+                                 OptionCount is zero.

+  @param[out] PacketLength       The number of bytes in the returned packet.

+  @param[out] Packet             The pointer to the received packet. This buffer must be freed by

+                                 the caller.

+

+  @retval  EFI_SUCCESS              An MTFTPv6 OACK packet was received and is in the Packet.

+                                    Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_INVALID_PARAMETER    One or more of the following conditions is TRUE:

+                                    - This is NULL.

+                                    - Filename is NULL.

+                                    - OptionCount is not zero and OptionList is NULL.

+                                    - One or more options in OptionList have wrong format.

+                                    - PacketLength is NULL.

+                                    - OverrideData.ServerIp is not valid unicast IPv6 addresses.

+  @retval  EFI_UNSUPPORTED          One or more options in the OptionList are unsupported by

+                                    this implementation.

+  @retval  EFI_NOT_STARTED          The EFI MTFTPv6 Protocol driver has not been started.

+  @retval  EFI_NO_MAPPING           The underlying IPv6 driver was responsible for choosing a source

+                                    address for this instance, but no source address was available for use.

+  @retval  EFI_ACCESS_DENIED        The previous operation has not completed yet.

+  @retval  EFI_OUT_OF_RESOURCES     Required system resources could not be allocated.

+  @retval  EFI_TFTP_ERROR           An MTFTPv6 ERROR packet was received and is in the Packet.

+  @retval  EFI_NETWORK_UNREACHABLE  An ICMP network unreachable error packet was received and the Packet is set to NULL.

+                                    Note: It is not defined in UEFI 2.3 Specification.

+  @retval  EFI_HOST_UNREACHABLE     An ICMP host unreachable error packet was received and the Packet is set to NULL.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received and the Packet is set to NULL.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_PORT_UNREACHABLE     An ICMP port unreachable error packet was received and the Packet is set to NULL.

+  @retval  EFI_ICMP_ERROR           Some other ICMP ERROR packet was received and the Packet is set to NULL.

+                                    Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_PROTOCOL_ERROR       An unexpected MTFTPv6 packet was received and is in the Packet.

+  @retval  EFI_TIMEOUT              No responses were received from the MTFTPv6 server.

+  @retval  EFI_DEVICE_ERROR         An unexpected network error or system error occurred.

+  @retval  EFI_NO_MEDIA             There was a media error.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6GetInfo (

+  IN  EFI_MTFTP6_PROTOCOL      *This,

+  IN  EFI_MTFTP6_OVERRIDE_DATA *OverrideData         OPTIONAL,

+  IN  UINT8                    *Filename,

+  IN  UINT8                    *ModeStr              OPTIONAL,

+  IN  UINT8                    OptionCount,

+  IN  EFI_MTFTP6_OPTION        *OptionList           OPTIONAL,

+  OUT UINT32                   *PacketLength,

+  OUT EFI_MTFTP6_PACKET        **Packet              OPTIONAL

+  )

+{

+  EFI_STATUS                Status;

+  EFI_MTFTP6_TOKEN          Token;

+  MTFTP6_GETINFO_CONTEXT    Context;

+

+  if (This == NULL ||

+      Filename == NULL ||

+      PacketLength == NULL ||

+      (OptionCount != 0 && OptionList == NULL) ||

+      (OverrideData != NULL && !NetIp6IsValidUnicast (&OverrideData->ServerIp))

+      ) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Packet != NULL) {

+    *Packet = NULL;

+  }

+

+  *PacketLength         = 0;

+

+  Context.Packet        = Packet;

+  Context.PacketLen     = PacketLength;

+  Context.Status        = EFI_SUCCESS;

+

+  //

+  // Fill fields of the Token for GetInfo operation.

+  //

+  Token.Status          = EFI_SUCCESS;

+  Token.Event           = NULL;

+  Token.OverrideData    = OverrideData;

+  Token.Filename        = Filename;

+  Token.ModeStr         = ModeStr;

+  Token.OptionCount     = OptionCount;

+  Token.OptionList      = OptionList;

+  Token.BufferSize      = 0;

+  Token.Buffer          = NULL;

+  Token.Context         = &Context;

+  Token.CheckPacket     = Mtftp6CheckPacket;

+  Token.TimeoutCallback = NULL;

+  Token.PacketNeeded    = NULL;

+

+  //

+  // Start the GetInfo operation by issue the Token.

+  //

+  Status = Mtftp6OperationStart (This, &Token, EFI_MTFTP6_OPCODE_RRQ);

+

+  if (Status == EFI_ABORTED) {

+    //

+    // Return the status if failed to issue.

+    //

+    return Context.Status;

+  }

+

+  return Status;

+}

+

+

+/**

+  Parse the options in an MTFTPv6 OACK packet.

+

+  The ParseOptions() function parses the option fields in an MTFTPv6 OACK

+  packet and returns the number of options that were found, and optionally,

+  a list of pointers to the options in the packet. If one or more of the

+  option fields are not valid, then EFI_PROTOCOL_ERROR is returned and

+  *OptionCount and *OptionList stop at the last valid option.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  PacketLen          Length of the OACK packet to be parsed.

+  @param[in]  Packet             Pointer to the OACK packet to be parsed.

+  @param[out] OptionCount        Pointer to the number of options in the following OptionList.

+  @param[out] OptionList         Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the

+                                 OptionList points to the corresponding MTFTP option buffer

+                                 in the Packet. Call the EFI Boot Service FreePool() to

+                                 release the OptionList if the options in this OptionList

+                                 are not needed anymore.

+

+  @retval  EFI_SUCCESS           The OACK packet was valid and the OptionCount and

+                                 OptionList parameters have been updated.

+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:

+                                 - PacketLen is 0.

+                                 - Packet is NULL or Packet is not a valid MTFTPv6 packet.

+                                 - OptionCount is NULL.

+  @retval  EFI_NOT_FOUND         No options were found in the OACK packet.

+  @retval  EFI_OUT_OF_RESOURCES  Storage for the OptionList array can not be allocated.

+  @retval  EFI_PROTOCOL_ERROR    One or more of the option fields is invalid.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6ParseOptions (

+  IN     EFI_MTFTP6_PROTOCOL    *This,

+  IN     UINT32                 PacketLen,

+  IN     EFI_MTFTP6_PACKET      *Packet,

+  OUT    UINT32                 *OptionCount,

+  OUT    EFI_MTFTP6_OPTION      **OptionList          OPTIONAL

+  )

+{

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  return Mtftp6ParseStart (Packet, PacketLen, OptionCount, OptionList);

+}

+

+

+/**

+  Download a file from an MTFTPv6 server.

+

+  The ReadFile() function is used to initialize and start an MTFTPv6 download

+  process, and optionally, wait for completion. When the download operation

+  completes, whether successfully or not, the Token.Status field is updated

+  by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it

+  is not NULL.

+  Data can be downloaded from the MTFTPv6 server into either of the following

+  locations:

+  - A fixed buffer that is pointed to by Token.Buffer

+  - A download service function that is pointed to by Token.CheckPacket.

+  If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket

+  will be called first. If the call is successful, the packet will be stored

+  in Token.Buffer.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to the token structure to provide the parameters that are

+                                 used in this operation.

+

+  @retval  EFI_SUCCESS              The data file has been transferred successfully.

+  @retval  EFI_OUT_OF_RESOURCES     Required system resources could not be allocated.

+  @retval  EFI_BUFFER_TOO_SMALL     BufferSize is not zero but not large enough to hold the

+                                    downloaded data in downloading process.

+                                    Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_ABORTED              Current operation is aborted by user.

+  @retval  EFI_NETWORK_UNREACHABLE  An ICMP network unreachable error packet was received.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_HOST_UNREACHABLE     An ICMP host unreachable error packet was received.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_PORT_UNREACHABLE     An ICMP port unreachable error packet was received.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_ICMP_ERROR           An ICMP ERROR packet was received.

+  @retval  EFI_TIMEOUT              No responses were received from the MTFTPv6 server.

+  @retval  EFI_TFTP_ERROR           An MTFTPv6 ERROR packet was received.

+  @retval  EFI_DEVICE_ERROR         An unexpected network error or system error occurred.

+  @retval  EFI_NO_MEDIA             There was a media error.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6ReadFile (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_TOKEN       *Token

+  )

+{

+  return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_RRQ);

+}

+

+

+/**

+  Send a file to an MTFTPv6 server.

+

+  The WriteFile() function is used to initialize an uploading operation

+  with the given option list and optionally wait for completion. If one

+  or more of the options is not supported by the server, the unsupported

+  options are ignored and a standard TFTP process starts instead. When

+  the upload process completes, whether successfully or not, Token.Event

+  is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status.

+  The caller can supply the data to be uploaded in the following two modes:

+  - Through the user-provided buffer

+  - Through a callback function

+  With the user-provided buffer, the Token.BufferSize field indicates

+  the length of the buffer, and the driver will upload the data in the

+  buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver

+  will call this callback function to get more data from the user to upload.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to the token structure to provide the parameters that are

+                                 used in this operation.

+

+  @retval  EFI_SUCCESS           The upload session has started.

+  @retval  EFI_UNSUPPORTED       The operation is not supported by this implementation.

+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token.Filename is NULL.

+                                 - Token.OptionCount is not zero and Token.OptionList is NULL.

+                                 - One or more options in Token.OptionList have wrong format.

+                                 - Token.Buffer and Token.PacketNeeded are both NULL.

+                                 - Token.OverrideData.ServerIp is not a valid unicast IPv6 address.

+  @retval  EFI_UNSUPPORTED       One or more options in the Token.OptionList are not

+                                 supported by this implementation.

+  @retval  EFI_NOT_STARTED       The EFI MTFTPv6 Protocol driver has not been started.

+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source

+                                 address for this instance, but no source address was available for use.

+  @retval  EFI_ALREADY_STARTED   This Token is already being used in another MTFTPv6 session.

+  @retval  EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval  EFI_ACCESS_DENIED     The previous operation has not completed yet.

+  @retval  EFI_DEVICE_ERROR      An unexpected network error or system error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6WriteFile (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_TOKEN       *Token

+  )

+{

+  return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_WRQ);

+}

+

+

+/**

+  Download a data file directory from an MTFTPv6 server.

+

+  The ReadDirectory() function is used to return a list of files on the

+  MTFTPv6 server that are logically (or operationally) related to

+  Token.Filename. The directory request packet that is sent to the server

+  is built with the option list that was provided by the caller, if present.

+  The file information that the server returns is put into either of

+  the following locations:

+  - A fixed buffer that is pointed to by Token.Buffer.

+  - A download service function that is pointed to by Token.CheckPacket.

+  If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket

+  will be called first. If the call is successful, the packet will be stored

+  in Token.Buffer.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to the token structure to provide the parameters that are

+                                 used in this operation.

+

+  @retval  EFI_SUCCESS           The MTFTPv6 related file "directory" has been downloaded.

+  @retval  EFI_UNSUPPORTED       The EFI MTFTPv6 Protocol driver does not support this function.

+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token.Filename is NULL.

+                                 - Token.OptionCount is not zero and Token.OptionList is NULL.

+                                 - One or more options in Token.OptionList have wrong format.

+                                 - Token.Buffer and Token.CheckPacket are both NULL.

+                                 - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.

+  @retval  EFI_UNSUPPORTED       One or more options in the Token.OptionList are not

+                                 supported by this implementation.

+  @retval  EFI_NOT_STARTED       The EFI MTFTPv6 Protocol driver has not been started.

+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source

+                                 address for this instance, but no source address was available for use.

+  @retval  EFI_ALREADY_STARTED   This Token is already being used in another MTFTPv6 session.

+  @retval  EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval  EFI_ACCESS_DENIED     The previous operation has not completed yet.

+  @retval  EFI_DEVICE_ERROR      An unexpected network error or system error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6ReadDirectory (

+  IN EFI_MTFTP6_PROTOCOL        *This,

+  IN EFI_MTFTP6_TOKEN           *Token

+  )

+{

+  return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_DIR);

+}

+

+

+/**

+  Polls for incoming data packets and processes outgoing data packets.

+

+  The Poll() function can be used by network drivers and applications

+  to increase the rate that data packets are moved between the

+  communications device and the transmit and receive queues. In some

+  systems, the periodic timer event in the managed network driver may

+  not poll the underlying communications device fast enough to transmit

+  and/or receive all data packets without missing incoming packets or

+  dropping outgoing packets. Drivers and applications that are

+  experiencing packet loss should try calling the Poll() function

+  more often.

+

+  @param[in]  This                   The MTFTP6 protocol instance.

+

+

+  @retval  EFI_SUCCESS           Incoming or outgoing data was processed.

+  @retval  EFI_NOT_STARTED       This EFI MTFTPv6 Protocol instance has not been started.

+  @retval  EFI_INVALID_PARAMETER This is NULL.

+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+  @retval  EFI_TIMEOUT           Data was dropped out of the transmit and/or receive queue.

+                                 Consider increasing the polling rate.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6Poll (

+  IN EFI_MTFTP6_PROTOCOL    *This

+  )

+{

+  MTFTP6_INSTANCE           *Instance;

+  EFI_UDP6_PROTOCOL         *Udp6;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = MTFTP6_INSTANCE_FROM_THIS (This);

+

+  //

+  // Check the instance whether configured or in destory.

+  //

+  if (Instance->Config == NULL) {

+    return EFI_NOT_STARTED;

+  } else if (Instance->InDestory) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  Udp6 = Instance->UdpIo->Protocol.Udp6;

+

+  return Udp6->Poll (Udp6);

+}

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h
new file mode 100644
index 0000000..bf924a9
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h
@@ -0,0 +1,469 @@
+/** @file

+  Mtftp6 internal data structure and definition declaration.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_MTFTP6_IMPL_H__

+#define __EFI_MTFTP6_IMPL_H__

+

+#include <Uefi.h>

+

+#include <Protocol/Udp6.h>

+#include <Protocol/Mtftp6.h>

+#include <Protocol/ServiceBinding.h>

+#include <Protocol/DriverBinding.h>

+

+#include <Library/DebugLib.h>

+#include <Library/UefiDriverEntryPoint.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/BaseLib.h>

+#include <Library/NetLib.h>

+

+typedef struct _MTFTP6_SERVICE  MTFTP6_SERVICE;

+typedef struct _MTFTP6_INSTANCE MTFTP6_INSTANCE;

+

+#include "Mtftp6Driver.h"

+#include "Mtftp6Option.h"

+#include "Mtftp6Support.h"

+

+#define MTFTP6_SERVICE_SIGNATURE       SIGNATURE_32 ('M', 'F', '6', 'S')

+#define MTFTP6_INSTANCE_SIGNATURE      SIGNATURE_32 ('M', 'F', '6', 'I')

+

+#define MTFTP6_DEFAULT_SERVER_CMD_PORT 69

+#define MTFTP6_DEFAULT_TIMEOUT         3

+#define MTFTP6_GET_MAPPING_TIMEOUT     3

+#define MTFTP6_DEFAULT_MAX_RETRY       5

+#define MTFTP6_DEFAULT_BLK_SIZE        512

+#define MTFTP6_TICK_PER_SECOND         10000000U

+

+#define MTFTP6_SERVICE_FROM_THIS(a)    CR (a, MTFTP6_SERVICE, ServiceBinding, MTFTP6_SERVICE_SIGNATURE)

+#define MTFTP6_INSTANCE_FROM_THIS(a)   CR (a, MTFTP6_INSTANCE, Mtftp6, MTFTP6_INSTANCE_SIGNATURE)

+

+extern EFI_MTFTP6_PROTOCOL             gMtftp6ProtocolTemplate;

+

+typedef struct _MTFTP6_GETINFO_CONTEXT{

+  EFI_MTFTP6_PACKET             **Packet;

+  UINT32                        *PacketLen;

+  EFI_STATUS                    Status;

+} MTFTP6_GETINFO_CONTEXT;

+

+//

+// Control block for MTFTP6 instance, it's per configuration data.

+//

+struct _MTFTP6_INSTANCE {

+  UINT32                        Signature;

+  EFI_HANDLE                    Handle;

+  LIST_ENTRY                    Link;

+  EFI_MTFTP6_PROTOCOL           Mtftp6;

+  MTFTP6_SERVICE                *Service;

+  EFI_MTFTP6_CONFIG_DATA        *Config;

+

+  EFI_MTFTP6_TOKEN              *Token;

+  MTFTP6_EXT_OPTION_INFO        ExtInfo;

+

+  UINT16                        BlkSize;

+  UINT16                        LastBlk;

+  LIST_ENTRY                    BlkList;

+

+  EFI_IPv6_ADDRESS              ServerIp;

+  UINT16                        ServerCmdPort;

+  UINT16                        ServerDataPort;

+  UDP_IO                        *UdpIo;

+

+  EFI_IPv6_ADDRESS              McastIp;

+  UINT16                        McastPort;

+  UDP_IO                        *McastUdpIo;

+

+  NET_BUF                       *LastPacket;

+  UINT32                        CurRetry;

+  UINT32                        MaxRetry;

+  UINT32                        PacketToLive;

+  UINT32                        Timeout;

+

+  EFI_TPL                       OldTpl;

+  BOOLEAN                       IsTransmitted;

+  BOOLEAN                       IsMaster;

+  BOOLEAN                       InDestory;

+};

+

+//

+// Control block for MTFTP6 service, it's per Nic handle.

+//

+struct _MTFTP6_SERVICE {

+  UINT32                        Signature;

+  EFI_SERVICE_BINDING_PROTOCOL  ServiceBinding;

+  EFI_HANDLE                    Controller;

+  EFI_HANDLE                    Image;

+

+  UINT16                        ChildrenNum;

+  LIST_ENTRY                    Children;

+  //

+  // It is used to be as internal calculagraph for all instances.

+  //

+  EFI_EVENT                     Timer;

+  //

+  // It is used to maintain the parent-child relationship between

+  // mtftp driver and udp driver.

+  //

+  UDP_IO                        *DummyUdpIo;

+  BOOLEAN                       InDestory;

+};

+

+/**

+  Returns the current operating mode data for the MTFTP6 instance.

+

+  The GetModeData() function returns the current operating mode and

+  cached data packet for the MTFTP6 instance.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[out] ModeData           The buffer in which the EFI MTFTPv6 Protocol driver mode

+                                 data is returned.

+

+  @retval  EFI_SUCCESS           The configuration data was returned successfully.

+  @retval  EFI_OUT_OF_RESOURCES  The required mode data could not be allocated.

+  @retval  EFI_INVALID_PARAMETER This is NULL, or ModeData is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6GetModeData (

+  IN  EFI_MTFTP6_PROTOCOL    *This,

+  OUT EFI_MTFTP6_MODE_DATA   *ModeData

+  );

+

+/**

+  Initializes, changes, or resets the default operational setting for

+  this EFI MTFTPv6 Protocol driver instance.

+

+  The Configure() function is used to set and change the configuration

+  data for this EFI MTFTPv6 Protocol driver instance. The configuration

+  data can be reset to startup defaults by calling Configure() with

+  MtftpConfigData set to NULL. Whenever the instance is reset, any

+  pending operation is aborted. By changing the EFI MTFTPv6 Protocol

+  driver instance configuration data, the client can connect to

+  different MTFTPv6 servers. The configuration parameters in

+  MtftpConfigData are used as the default parameters in later MTFTPv6

+  operations and can be overridden in later operations.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  MtftpConfigData    Pointer to the configuration data structure.

+

+  @retval  EFI_SUCCESS           The EFI MTFTPv6 Protocol instance was configured successfully.

+  @retval  EFI_INVALID_PARAMETER One or more following conditions are TRUE:

+                                 - This is NULL.

+                                 - MtftpConfigData.StationIp is neither zero nor one

+                                   of the configured IP addresses in the underlying IPv6 driver.

+                                 - MtftpCofigData.ServerIp is not a valid IPv6 unicast address.

+                                 Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_ACCESS_DENIED     - The configuration could not be changed at this time because there

+                                   is some MTFTP background operation in progress.

+                                 - MtftpCofigData.LocalPort is already in use.

+                                 Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source

+                                 address for this instance, but no source address was available for use.

+  @retval  EFI_OUT_OF_RESOURCES  The EFI MTFTPv6 Protocol driver instance data could not be

+                                 allocated.

+                                 Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred. The EFI

+                                 MTFTPv6 Protocol driver instance is not configured.

+                                 Note: It is not defined in the UEFI 2.3 Specification.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6Configure (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData     OPTIONAL

+  );

+

+/**

+  Get the information of the download from the server.

+

+  The GetInfo() function assembles an MTFTPv6 request packet

+  with options, sends it to the MTFTPv6 server, and may return

+  an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries

+  occur only if no response packets are received from the MTFTPv6

+  server before the timeout expires.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  OverrideData       Data that is used to override the existing parameters. If NULL, the

+                                 default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure()

+                                 function are used.

+  @param[in]  Filename           Pointer to ASCIIZ file name string.

+  @param[in]  ModeStr            Pointer to ASCIIZ mode string. If NULL, octet will be used

+  @param[in]  OptionCount        Number of option/value string pairs in OptionList.

+  @param[in]  OptionList         Pointer to array of option/value string pairs. Ignored if

+                                 OptionCount is zero.

+  @param[out] PacketLength       The number of bytes in the returned packet.

+  @param[out] Packet             The pointer to the received packet. This buffer must be freed by

+                                 the caller.

+

+  @retval  EFI_SUCCESS              An MTFTPv6 OACK packet was received and is in the Packet.

+                                    Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_INVALID_PARAMETER    One or more of the following conditions is TRUE:

+                                    - This is NULL.

+                                    - Filename is NULL.

+                                    - OptionCount is not zero and OptionList is NULL.

+                                    - One or more options in OptionList have wrong format.

+                                    - PacketLength is NULL.

+                                    - OverrideData.ServerIp is not a valid unicast IPv6 address.

+  @retval  EFI_UNSUPPORTED          One or more options in the OptionList are unsupported by

+                                    this implementation.

+  @retval  EFI_NOT_STARTED          The EFI MTFTPv6 Protocol driver has not been started.

+  @retval  EFI_NO_MAPPING           The underlying IPv6 driver was responsible for choosing a source

+                                    address for this instance, but no source address was available for use.

+  @retval  EFI_ACCESS_DENIED        The previous operation has not completed yet.

+  @retval  EFI_OUT_OF_RESOURCES     Required system resources could not be allocated.

+  @retval  EFI_TFTP_ERROR           An MTFTPv6 ERROR packet was received and is in the Packet.

+  @retval  EFI_NETWORK_UNREACHABLE  An ICMP network unreachable error packet was received, and the Packet is set to NULL.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_HOST_UNREACHABLE     An ICMP host unreachable error packet was received, and the Packet is set to NULL.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received, and the Packet is set to NULL.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_PORT_UNREACHABLE     An ICMP port unreachable error packet was received, and the Packet is set to NULL.

+  @retval  EFI_ICMP_ERROR           Some other ICMP ERROR packet was received, and the Packet is set to NULL.

+                                    Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_PROTOCOL_ERROR       An unexpected MTFTPv6 packet was received and is in the Packet.

+  @retval  EFI_TIMEOUT              No responses were received from the MTFTPv6 server.

+  @retval  EFI_DEVICE_ERROR         An unexpected network error or system error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6GetInfo (

+  IN  EFI_MTFTP6_PROTOCOL      *This,

+  IN  EFI_MTFTP6_OVERRIDE_DATA *OverrideData         OPTIONAL,

+  IN  UINT8                    *Filename,

+  IN  UINT8                    *ModeStr              OPTIONAL,

+  IN  UINT8                    OptionCount,

+  IN  EFI_MTFTP6_OPTION        *OptionList           OPTIONAL,

+  OUT UINT32                   *PacketLength,

+  OUT EFI_MTFTP6_PACKET        **Packet              OPTIONAL

+  );

+

+/**

+  Parse the options in an MTFTPv6 OACK packet.

+

+  The ParseOptions() function parses the option fields in an MTFTPv6 OACK

+  packet and returns the number of options that were found, and optionally,

+  a list of pointers to the options in the packet. If one or more of the

+  option fields are not valid, then EFI_PROTOCOL_ERROR is returned and

+  *OptionCount and *OptionList stop at the last valid option.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  PacketLen          Length of the OACK packet to be parsed.

+  @param[in]  Packet             Pointer to the OACK packet to be parsed.

+  @param[out] OptionCount        Pointer to the number of options in the following OptionList.

+  @param[out] OptionList         Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the

+                                 OptionList points to the corresponding MTFTP option buffer

+                                 in the Packet. Call the EFI Boot Service FreePool() to

+                                 release the OptionList if the options in this OptionList

+                                 are not needed any more.

+

+  @retval  EFI_SUCCESS           The OACK packet was valid and the OptionCount, and

+                                 OptionList parameters have been updated.

+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:

+                                 - PacketLen is 0.

+                                 - Packet is NULL or Packet is not a valid MTFTPv6 packet.

+                                 - OptionCount is NULL.

+  @retval  EFI_NOT_FOUND         No options were found in the OACK packet.

+  @retval  EFI_OUT_OF_RESOURCES  Storage for the OptionList array can not be allocated.

+  @retval  EFI_PROTOCOL_ERROR    One or more of the option fields is invalid.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6ParseOptions (

+  IN     EFI_MTFTP6_PROTOCOL    *This,

+  IN     UINT32                 PacketLen,

+  IN     EFI_MTFTP6_PACKET      *Packet,

+  OUT    UINT32                 *OptionCount,

+  OUT    EFI_MTFTP6_OPTION      **OptionList          OPTIONAL

+  );

+

+/**

+  Download a file from an MTFTPv6 server.

+

+  The ReadFile() function is used to initialize and start an MTFTPv6 download

+  process and optionally wait for completion. When the download operation

+  completes, whether successfully or not, the Token.Status field is updated

+  by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it

+  is not NULL.

+  Data can be downloaded from the MTFTPv6 server into either of the following

+  locations:

+  - A fixed buffer that is pointed to by Token.Buffer.

+  - A download service function that is pointed to by Token.CheckPacket.

+  If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket

+  will be called first. If the call is successful, the packet will be stored

+  in Token.Buffer.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to the token structure to provide the parameters that are

+                                 used in this operation.

+

+  @retval  EFI_SUCCESS              The data file has been transferred successfully.

+  @retval  EFI_OUT_OF_RESOURCES     Required system resources could not be allocated.

+  @retval  EFI_BUFFER_TOO_SMALL     BufferSize is not zero but not large enough to hold the

+                                    downloaded data in downloading process.

+                                    Note: It does not match the UEFI 2.3 Specification.

+  @retval  EFI_ABORTED              Current operation is aborted by user.

+  @retval  EFI_NETWORK_UNREACHABLE  An ICMP network unreachable error packet was received.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_HOST_UNREACHABLE     An ICMP host unreachable error packet was received.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_PORT_UNREACHABLE     An ICMP port unreachable error packet was received.

+                                    Note: It is not defined in the UEFI 2.3 Specification.

+  @retval  EFI_ICMP_ERROR           An ICMP ERROR packet was received.

+  @retval  EFI_TIMEOUT              No responses were received from the MTFTPv6 server.

+  @retval  EFI_TFTP_ERROR           An MTFTPv6 ERROR packet was received.

+  @retval  EFI_DEVICE_ERROR         An unexpected network error or system error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6ReadFile (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_TOKEN       *Token

+  );

+

+/**

+  Send a file to an MTFTPv6 server.

+

+  The WriteFile() function is used to initialize an uploading operation

+  with the given option list, and optionally, wait for completion. If one

+  or more of the options is not supported by the server, the unsupported

+  options are ignored and a standard TFTP process starts instead. When

+  the upload process completes, whether successfully or not, Token.Event

+  is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status.

+  The caller can supply the data to be uploaded in the following two modes:

+  - Through the user-provided buffer.

+  - Through a callback function.

+  With the user-provided buffer, the Token.BufferSize field indicates

+  the length of the buffer, and the driver will upload the data in the

+  buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver

+  will call this callback function to get more data from the user to upload.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to the token structure to provide the parameters that are

+                                 used in this operation.

+

+  @retval  EFI_SUCCESS           The upload session has started.

+  @retval  EFI_UNSUPPORTED       The operation is not supported by this implementation.

+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token.Filename is NULL.

+                                 - Token.OptionCount is not zero and Token.OptionList is NULL.

+                                 - One or more options in Token.OptionList have wrong format.

+                                 - Token.Buffer and Token.PacketNeeded are both NULL.

+                                 - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.

+  @retval  EFI_UNSUPPORTED       One or more options in the Token.OptionList are not

+                                 supported by this implementation.

+  @retval  EFI_NOT_STARTED       The EFI MTFTPv6 Protocol driver has not been started.

+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source

+                                 address for this instance, but no source address was available for use.

+  @retval  EFI_ALREADY_STARTED   This Token is already being used in another MTFTPv6 session.

+  @retval  EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval  EFI_ACCESS_DENIED     The previous operation has not completed yet.

+  @retval  EFI_DEVICE_ERROR      An unexpected network error or system error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6WriteFile (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_TOKEN       *Token

+  );

+

+/**

+  Download a data file directory from an MTFTPv6 server.

+

+  The ReadDirectory() function is used to return a list of files on the

+  MTFTPv6 server that are logically (or operationally) related to

+  Token.Filename. The directory request packet that is sent to the server

+  is built with the option list that was provided by caller, if present.

+  The file information that the server returns is put into either of

+  the following locations:

+  - A fixed buffer that is pointed to by Token.Buffer.

+  - A download service function that is pointed to by Token.CheckPacket.

+  If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket

+  will be called first. If the call is successful, the packet will be stored

+  in Token.Buffer.

+

+  @param[in]  This               Pointer to the EFI_MTFTP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to the token structure to provide the parameters that are

+                                 used in this operation.

+

+  @retval  EFI_SUCCESS           The MTFTPv6 related file "directory" has been downloaded.

+  @retval  EFI_UNSUPPORTED       The EFI MTFTPv6 Protocol driver does not support this function.

+  @retval  EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token.Filename is NULL.

+                                 - Token.OptionCount is not zero and Token.OptionList is NULL.

+                                 - One or more options in Token.OptionList have wrong format.

+                                 - Token.Buffer and Token.CheckPacket are both NULL.

+                                 - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.

+  @retval  EFI_UNSUPPORTED       One or more options in the Token.OptionList are not

+                                 supported by this implementation.

+  @retval  EFI_NOT_STARTED       The EFI MTFTPv6 Protocol driver has not been started.

+  @retval  EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source

+                                 address for this instance, but no source address was available for use.

+  @retval  EFI_ALREADY_STARTED   This Token is already being used in another MTFTPv6 session.

+  @retval  EFI_OUT_OF_RESOURCES  Required system resources could not be allocated.

+  @retval  EFI_ACCESS_DENIED     The previous operation has not completed yet.

+  @retval  EFI_DEVICE_ERROR      An unexpected network error or system error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6ReadDirectory (

+  IN EFI_MTFTP6_PROTOCOL        *This,

+  IN EFI_MTFTP6_TOKEN           *Token

+  );

+

+/**

+  Polls for incoming data packets and processes outgoing data packets.

+

+  The Poll() function can be used by network drivers and applications

+  to increase the rate that data packets are moved between the

+  communications device and the transmit and receive queues.In some

+  systems, the periodic timer event in the managed network driver may

+  not poll the underlying communications device fast enough to transmit

+  and/or receive all data packets without missing incoming packets or

+  dropping outgoing packets. Drivers and applications that are

+  experiencing packet loss should try calling the Poll() function

+  more often.

+

+  @param[in]  This                   The MTFTP6 protocol instance.

+

+

+  @retval  EFI_SUCCESS           Incoming or outgoing data was processed.

+  @retval  EFI_NOT_STARTED       This EFI MTFTPv6 Protocol instance has not been started.

+  @retval  EFI_INVALID_PARAMETER This is NULL.

+  @retval  EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+  @retval  EFI_TIMEOUT           Data was dropped out of the transmit and/or receive queue.

+                                 Consider increasing the polling rate.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiMtftp6Poll (

+  IN EFI_MTFTP6_PROTOCOL    *This

+  );

+

+#endif

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c
new file mode 100644
index 0000000..0dcf546
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c
@@ -0,0 +1,416 @@
+/** @file

+  Mtftp6 option parse functions implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Mtftp6Impl.h"

+

+CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM] = {

+  "blksize",

+  "timeout",

+  "tsize",

+  "multicast"

+};

+

+

+/**

+  Parse the NULL terminated ASCII string of multicast option.

+

+  @param[in]  Str           The pointer to the Ascii string of multicast option.

+  @param[in]  ExtInfo       The pointer to the option information to be filled.

+

+  @retval EFI_SUCCESS            Parse the multicast option successfully.

+  @retval EFI_INVALID_PARAMETER  The string is malformatted.

+  @retval EFI_OUT_OF_RESOURCES   Failed to perform the operation due to lack of

+                                 resources.

+

+**/

+EFI_STATUS

+Mtftp6ParseMcastOption (

+  IN UINT8                  *Str,

+  IN MTFTP6_EXT_OPTION_INFO *ExtInfo

+  )

+{

+  EFI_STATUS                Status;

+  UINT32                    Num;

+  CHAR8                     *Ip6Str;

+  CHAR8                     *TempStr;

+

+  //

+  // The multicast option is formated like "addr,port,mc"

+  // The server can also omit the ip and port, use ",,1"

+  //

+  if (*Str == ',') {

+

+    ZeroMem (&ExtInfo->McastIp, sizeof (EFI_IPv6_ADDRESS));

+  } else {

+

+    Ip6Str = (CHAR8 *) AllocateCopyPool (AsciiStrSize ((CHAR8 *) Str), Str);

+    if (Ip6Str == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    //

+    // The IPv6 address locates before comma in the input Str.

+    //

+    TempStr = Ip6Str;

+    while ((*TempStr != '\0') && (*TempStr != ',')) {

+      TempStr++;

+    }

+

+    *TempStr = '\0';

+

+    Status = NetLibAsciiStrToIp6 (Ip6Str, &ExtInfo->McastIp);

+    FreePool (Ip6Str);

+

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    while ((*Str != '\0') && (*Str != ',')) {

+      Str++;

+    }

+  }

+

+  if (*Str != ',') {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Str++;

+

+  //

+  // Convert the port setting. the server can send us a port number or

+  // empty string. such as the port in ",,1"

+  //

+  if (*Str == ',') {

+

+    ExtInfo->McastPort = 0;

+  } else {

+

+    Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str);

+

+    if (Num > 65535) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    ExtInfo->McastPort = (UINT16) Num;

+

+    while (NET_IS_DIGIT (*Str)) {

+      Str++;

+    }

+  }

+

+  if (*Str != ',') {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Str++;

+

+  //

+  // Check the master/slave setting, 1 for master, 0 for slave.

+  //

+  Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str);

+

+  if (Num != 0 && Num != 1) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  ExtInfo->IsMaster = (BOOLEAN) (Num == 1);

+

+  while (NET_IS_DIGIT (*Str)) {

+    Str++;

+  }

+

+  if (*Str != '\0') {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Parse the MTFTP6 extesion options.

+

+  @param[in]  Options       The pointer to the extension options list.

+  @param[in]  Count         The num of the extension options.

+  @param[in]  IsRequest     If FALSE, the extension options is included

+                            by a request packet.

+  @param[in]  ExtInfo       The pointer to the option information to be filled.

+

+  @retval EFI_SUCCESS            Parse the multicast option successfully.

+  @retval EFI_INVALID_PARAMETER  There is one option is malformatted at least.

+  @retval EFI_UNSUPPORTED        There is one option is not supported at least.

+

+**/

+EFI_STATUS

+Mtftp6ParseExtensionOption (

+  IN EFI_MTFTP6_OPTION        *Options,

+  IN UINT32                   Count,

+  IN BOOLEAN                  IsRequest,

+  IN MTFTP6_EXT_OPTION_INFO   *ExtInfo

+  )

+{

+  EFI_STATUS                  Status;

+  EFI_MTFTP6_OPTION           *Opt;

+  UINT32                      Index;

+  UINT32                      Value;

+

+  ExtInfo->BitMap = 0;

+

+  for (Index = 0; Index < Count; Index++) {

+

+    Opt = Options + Index;

+

+    if (Opt->OptionStr == NULL || Opt->ValueStr == NULL) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "blksize") == 0) {

+      //

+      // block size option, valid value is between [8, 65464]

+      //

+      Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);

+

+      if ((Value < 8) || (Value > 65464)) {

+        return EFI_INVALID_PARAMETER;

+      }

+

+      ExtInfo->BlkSize = (UINT16) Value;

+      ExtInfo->BitMap |= MTFTP6_OPT_BLKSIZE_BIT;

+

+    } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "timeout") == 0) {

+      //

+      // timeout option, valid value is between [1, 255]

+      //

+      Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);

+

+      if (Value < 1 || Value > 255) {

+        return EFI_INVALID_PARAMETER;

+      }

+

+      ExtInfo->Timeout = (UINT8) Value;

+      ExtInfo->BitMap |= MTFTP6_OPT_TIMEOUT_BIT;

+

+    } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "tsize") == 0) {

+      //

+      // tsize option, the biggest transfer supported is 4GB with block size option

+      //

+      ExtInfo->Tsize   = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);

+      ExtInfo->BitMap |= MTFTP6_OPT_TSIZE_BIT;

+

+    } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "multicast") == 0) {

+      //

+      // Multicast option, if it is a request, the value must be a zero string,

+      // otherwise, it must be like "addr,port,mc" string, mc indicates master.

+      //

+      if (!IsRequest) {

+

+        Status = Mtftp6ParseMcastOption (Opt->ValueStr, ExtInfo);

+

+        if (EFI_ERROR (Status)) {

+          return Status;

+        }

+      } else if (*(Opt->ValueStr) != '\0') {

+

+        return EFI_INVALID_PARAMETER;

+      }

+

+      ExtInfo->BitMap |= MTFTP6_OPT_MCAST_BIT;

+

+    } else if (IsRequest) {

+      //

+      // If it's a request, unsupported; else if it's a reply, ignore.

+      //

+      return EFI_UNSUPPORTED;

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Go through the packet to fill the options array with the start

+  addresses of each MTFTP option name/value pair.

+

+  @param[in]      Packet                 The packet to be checked.

+  @param[in]      PacketLen              The length of the packet.

+  @param[in, out] Count                  The num of the Options on input.

+                                         The actual one on output.

+  @param[in]      Options                The option array to be filled.

+                                         It is optional.

+

+  @retval EFI_SUCCESS            The packet has been parsed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is malformatted.

+  @retval EFI_BUFFER_TOO_SMALL   The Options array is too small.

+  @retval EFI_PROTOCOL_ERROR     An unexpected MTFTPv6 packet was received.

+

+**/

+EFI_STATUS

+Mtftp6ParsePacketOption (

+  IN     EFI_MTFTP6_PACKET     *Packet,

+  IN     UINT32                PacketLen,

+  IN OUT UINT32                *Count,

+  IN     EFI_MTFTP6_OPTION     *Options          OPTIONAL

+  )

+{

+  UINT8                        *Cur;

+  UINT8                        *Last;

+  UINT8                        Num;

+  UINT8                        *Name;

+  UINT8                        *Value;

+

+  Num   = 0;

+  Cur   = (UINT8 *) Packet + MTFTP6_OPCODE_LEN;

+  Last  = (UINT8 *) Packet + PacketLen - 1;

+

+  //

+  // process option name and value pairs.

+  // The last byte is always zero.

+  //

+  while (Cur < Last) {

+    Name = Cur;

+

+    while (*Cur != 0) {

+      Cur++;

+    }

+

+    if (Cur == Last) {

+      return EFI_PROTOCOL_ERROR;

+    }

+

+    Value = ++Cur;

+

+    while (*Cur != 0) {

+      Cur++;

+    }

+

+    Num++;

+

+    if (Options != NULL && Num <= *Count) {

+      Options[Num - 1].OptionStr  = Name;

+      Options[Num - 1].ValueStr   = Value;

+    }

+

+    Cur++;

+  }

+

+  //

+  // Return buffer too small if the buffer passed-in isn't enough.

+  //

+  if (*Count < Num || Options == NULL) {

+    *Count = Num;

+    return EFI_BUFFER_TOO_SMALL;

+  }

+

+  *Count = Num;

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Go through the packet, generate option list array and fill it

+  by the result of parse options.

+

+  @param[in]      Packet                 The packet to be checked.

+  @param[in]      PacketLen              The length of the packet.

+  @param[in, out] OptionCount            The num of the Options on input.

+                                         The actual one on output.

+  @param[out]     OptionList             The option list array to be generated

+                                         and filled. It is optional.

+

+  @retval EFI_SUCCESS            The packet has been parsed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is malformatted.

+  @retval EFI_PROTOCOL_ERROR     There is one option is malformatted at least.

+  @retval EFI_NOT_FOUND          The packet has no options.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the array.

+  @retval EFI_BUFFER_TOO_SMALL   The size of option list array is too small.

+

+**/

+EFI_STATUS

+Mtftp6ParseStart (

+  IN     EFI_MTFTP6_PACKET      *Packet,

+  IN     UINT32                 PacketLen,

+  IN OUT UINT32                 *OptionCount,

+     OUT EFI_MTFTP6_OPTION      **OptionList          OPTIONAL

+  )

+{

+  EFI_STATUS                    Status;

+

+  if (PacketLen == 0 || Packet == NULL || OptionCount == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  *OptionCount = 0;

+

+  if (OptionList != NULL) {

+    *OptionList = NULL;

+  }

+

+  if (NTOHS (Packet->OpCode) != EFI_MTFTP6_OPCODE_OACK) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // The last byte must be zero to terminate the options.

+  //

+  if (*((UINT8 *) Packet + PacketLen - 1) != 0) {

+    return EFI_PROTOCOL_ERROR;

+  }

+

+  //

+  // Parse packet with NULL buffer for the first time to get the number

+  // of options in the packet.

+  //

+  Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, NULL);

+

+  if (Status != EFI_BUFFER_TOO_SMALL) {

+    return Status;

+  }

+

+  //

+  // Return not found if there is no option parsed.

+  //

+  if (*OptionCount == 0) {

+    return EFI_NOT_FOUND;

+  }

+

+  //

+  // Only need parse out the number of options.

+  //

+  if (OptionList == NULL) {

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Allocate the buffer according to the option number parsed before.

+  //

+  *OptionList = AllocateZeroPool (*OptionCount * sizeof (EFI_MTFTP6_OPTION));

+

+  if (*OptionList == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Parse packet with allocated buffer for the second time to fill the pointer array

+  // of the options in the packet.

+  //

+  Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, *OptionList);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  return EFI_SUCCESS;

+}

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h
new file mode 100644
index 0000000..8e2671f
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h
@@ -0,0 +1,148 @@
+/** @file

+  Mtftp6 option parse functions declaration.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_MTFTP6_OPTION_H__

+#define __EFI_MTFTP6_OPTION_H__

+

+#include <Uefi.h>

+

+#include <Protocol/ServiceBinding.h>

+

+#include <Library/NetLib.h>

+#include <Library/UdpIoLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/UefiRuntimeServicesTableLib.h>

+

+#define MTFTP6_SUPPORTED_OPTIONS_NUM  4

+#define MTFTP6_OPCODE_LEN             2

+#define MTFTP6_ERRCODE_LEN            2

+#define MTFTP6_BLKNO_LEN              2

+#define MTFTP6_DATA_HEAD_LEN          4

+

+//

+// The bit map definition for Mtftp6 extension options.

+//

+#define MTFTP6_OPT_BLKSIZE_BIT        0x01

+#define MTFTP6_OPT_TIMEOUT_BIT        0x02

+#define MTFTP6_OPT_TSIZE_BIT          0x04

+#define MTFTP6_OPT_MCAST_BIT          0x08

+

+extern CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM];

+

+typedef struct {

+  UINT16                    BlkSize;

+  UINT8                     Timeout;

+  UINT32                    Tsize;

+  EFI_IPv6_ADDRESS          McastIp;

+  UINT16                    McastPort;

+  BOOLEAN                   IsMaster;

+  UINT32                    BitMap;

+} MTFTP6_EXT_OPTION_INFO;

+

+/**

+  Parse the Ascii string of multi-cast option.

+

+  @param[in]  Str           The pointer to the Ascii string of multi-cast option.

+  @param[in]  ExtInfo       The pointer to the option information to be filled.

+

+  @retval EFI_SUCCESS            Parse the multicast option successfully.

+  @retval EFI_INVALID_PARAMETER  The string is malformatted.

+

+**/

+EFI_STATUS

+Mtftp6ParseMcastOption (

+  IN UINT8                  *Str,

+  IN MTFTP6_EXT_OPTION_INFO *ExtInfo

+  );

+

+

+/**

+  Parse the MTFTP6 extesion options.

+

+  @param[in]  Options       The pointer to the extension options list.

+  @param[in]  Count         The num of the extension options.

+  @param[in]  IsRequest     If FALSE, the extension options is included

+                            by a request packet.

+  @param[in]  ExtInfo       The pointer to the option information to be filled.

+

+  @retval EFI_SUCCESS            Parse the multi-cast option successfully.

+  @retval EFI_INVALID_PARAMETER  An option is malformatted.

+  @retval EFI_UNSUPPORTED        An option is not supported.

+

+**/

+EFI_STATUS

+Mtftp6ParseExtensionOption (

+  IN EFI_MTFTP6_OPTION        *Options,

+  IN UINT32                   Count,

+  IN BOOLEAN                  IsRequest,

+  IN MTFTP6_EXT_OPTION_INFO   *ExtInfo

+  );

+

+

+/**

+  Go through the packet to fill the options array with the start

+  addresses of each MTFTP option name/value pair.

+

+  @param[in]      Packet                 The packet to be checked.

+  @param[in]      PacketLen              The length of the packet.

+  @param[in, out] Count                  The num of the Options on input.

+                                         The actual one on output.

+  @param[in]      Options                The option array to be filled

+                                         it's optional.

+

+  @retval EFI_SUCCESS            The packet has been parsed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is malformatted

+  @retval EFI_BUFFER_TOO_SMALL   The Options array is too small

+  @retval EFI_PROTOCOL_ERROR     An unexpected MTFTPv6 packet was received.

+

+**/

+EFI_STATUS

+Mtftp6ParsePacketOption (

+  IN     EFI_MTFTP6_PACKET     *Packet,

+  IN     UINT32                PacketLen,

+  IN OUT UINT32                *Count,

+  IN     EFI_MTFTP6_OPTION     *Options          OPTIONAL

+  );

+

+

+/**

+  Go through the packet, generate option list array and fill it

+  by the result of parse options.

+

+  @param[in]      Packet                 The packet to be checked.

+  @param[in]      PacketLen              The length of the packet.

+  @param[in, out] OptionCount            The num of the Options on input.

+                                         The actual one on output.

+  @param[out]     OptionList             The option list array to be generated

+                                         and filled. It is optional.

+

+  @retval EFI_SUCCESS            The packet has been parsed successfully.

+  @retval EFI_INVALID_PARAMETER  The packet is malformatted.

+  @retval EFI_PROTOCOL_ERROR     An option is malformatted.

+  @retval EFI_NOT_FOUND          The packet has no options.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the array.

+  @retval EFI_BUFFER_TOO_SMALL   The size of option list array is too small.

+

+**/

+EFI_STATUS

+Mtftp6ParseStart (

+  IN     EFI_MTFTP6_PACKET      *Packet,

+  IN     UINT32                 PacketLen,

+  IN OUT UINT32                 *OptionCount,

+     OUT EFI_MTFTP6_OPTION      **OptionList          OPTIONAL

+  );

+

+#endif

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c
new file mode 100644
index 0000000..da36432
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c
@@ -0,0 +1,900 @@
+/** @file

+  Mtftp6 Rrq process functions implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Mtftp6Impl.h"

+

+

+/**

+  Build and send a ACK packet for download.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  BlockNum              The block number to be acked.

+

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the packet.

+  @retval EFI_SUCCESS           The ACK has been sent.

+  @retval Others                Failed to send the ACK.

+

+**/

+EFI_STATUS

+Mtftp6RrqSendAck (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 BlockNum

+  )

+{

+  EFI_MTFTP6_PACKET         *Ack;

+  NET_BUF                   *Packet;

+

+  //

+  // Allocate net buffer to create ack packet.

+  //

+  Packet = NetbufAlloc (sizeof (EFI_MTFTP6_ACK_HEADER));

+

+  if (Packet == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Ack = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (

+                                Packet,

+                                sizeof (EFI_MTFTP6_ACK_HEADER),

+                                FALSE

+                                );

+  ASSERT (Ack != NULL);

+

+  Ack->Ack.OpCode    = HTONS (EFI_MTFTP6_OPCODE_ACK);

+  Ack->Ack.Block[0]  = HTONS (BlockNum);

+

+  //

+  // Reset current retry count of the instance.

+  //

+  Instance->CurRetry = 0;

+

+  return Mtftp6TransmitPacket (Instance, Packet);

+}

+

+

+/**

+  Deliver the received data block to the user, which can be saved

+  in the user provide buffer or through the CheckPacket callback.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  Packet                The pointer to the received packet.

+  @param[in]  Len                   The packet length.

+  @param[out] UdpPacket             The net buf of the received packet.

+

+  @retval EFI_SUCCESS           The data was saved successfully.

+  @retval EFI_ABORTED           The user tells to abort by return an error through

+                                CheckPacket.

+  @retval EFI_BUFFER_TOO_SMALL  The user's buffer is too small, and buffer length is

+                                updated to the actual buffer size needed.

+

+**/

+EFI_STATUS

+Mtftp6RrqSaveBlock (

+  IN  MTFTP6_INSTANCE       *Instance,

+  IN  EFI_MTFTP6_PACKET     *Packet,

+  IN  UINT32                Len,

+  OUT NET_BUF               **UdpPacket

+  )

+{

+  EFI_MTFTP6_TOKEN          *Token;

+  EFI_STATUS                Status;

+  UINT16                    Block;

+  UINT64                    Start;

+  UINT32                    DataLen;

+  UINT64                    TotalBlock;

+  BOOLEAN                   Completed;

+

+  Completed = FALSE;

+  Token     = Instance->Token;

+  Block     = NTOHS (Packet->Data.Block);

+  DataLen   = Len - MTFTP6_DATA_HEAD_LEN;

+

+  //

+  // This is the last block, save the block num

+  //

+  if (DataLen < Instance->BlkSize) {

+	Completed = TRUE;

+    Instance->LastBlk = Block;

+    Mtftp6SetLastBlockNum (&Instance->BlkList, Block);

+  }

+

+  //

+  // Remove this block number from the file hole. If Mtftp6RemoveBlockNum

+  // returns EFI_NOT_FOUND, the block has been saved, don't save it again.

+  // Note that : For bigger files, allowing the block counter to roll over

+  // to accept transfers of unlimited size. So TotalBlock is memorised as

+  // continuous block counter.

+  //

+  Status = Mtftp6RemoveBlockNum (&Instance->BlkList, Block, Completed, &TotalBlock);

+

+  if (Status == EFI_NOT_FOUND) {

+    return EFI_SUCCESS;

+  } else if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (Token->CheckPacket != NULL) {

+    //

+    // Callback to the check packet routine with the received packet.

+    //

+    Status = Token->CheckPacket (&Instance->Mtftp6, Token, (UINT16) Len, Packet);

+

+    if (EFI_ERROR (Status)) {

+      //

+      // Free the received packet before send new packet in ReceiveNotify,

+      // since the Udp6Io might need to be reconfigured.

+      //

+      NetbufFree (*UdpPacket);

+      *UdpPacket = NULL;

+      //

+      // Send the Mtftp6 error message if user aborted the current session.

+      //

+      Mtftp6SendError (

+        Instance,

+        EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,

+        (UINT8 *) "User aborted download"

+        );

+

+      return EFI_ABORTED;

+    }

+  }

+

+  if (Token->Buffer != NULL) {

+

+    Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize);

+    if (Start + DataLen <= Token->BufferSize) {

+      CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);

+      //

+      // Update the file size when received the last block

+      //

+      if ((Instance->LastBlk == Block) && Completed) {

+        Token->BufferSize = Start + DataLen;

+      }

+    } else if (Instance->LastBlk != 0) {

+      //

+      // Don't save the data if the buffer is too small, return

+      // EFI_BUFFER_TOO_SMALL if received the last packet. This

+      // will give a accurate file length.

+      //

+      Token->BufferSize = Start + DataLen;

+

+      //

+      // Free the received packet before send new packet in ReceiveNotify,

+      // since the udpio might need to be reconfigured.

+      //

+      NetbufFree (*UdpPacket);

+      *UdpPacket = NULL;

+      //

+      // Send the Mtftp6 error message if no enough buffer.

+      //

+      Mtftp6SendError (

+        Instance,

+        EFI_MTFTP6_ERRORCODE_DISK_FULL,

+        (UINT8 *) "User provided memory block is too small"

+        );

+

+      return EFI_BUFFER_TOO_SMALL;

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Process the received data packets. It will save the block

+  then send back an ACK if it is active.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  Packet                The pointer to the received packet.

+  @param[in]  Len                   The length of the packet.

+  @param[out] UdpPacket             The net buf of received packet.

+  @param[out] IsCompleted           If TRUE, the download has been completed.

+                                    Otherwise, the download has not been completed.

+

+  @retval EFI_SUCCESS           The data packet was successfully processed.

+  @retval EFI_ABORTED           The download was aborted by the user.

+  @retval EFI_BUFFER_TOO_SMALL  The user-provided buffer is too small.

+

+**/

+EFI_STATUS

+Mtftp6RrqHandleData (

+  IN  MTFTP6_INSTANCE       *Instance,

+  IN  EFI_MTFTP6_PACKET     *Packet,

+  IN  UINT32                Len,

+  OUT NET_BUF               **UdpPacket,

+  OUT BOOLEAN               *IsCompleted

+  )

+{

+  EFI_STATUS                Status;

+  UINT16                    BlockNum;

+  INTN                      Expected;

+

+  *IsCompleted = FALSE;

+  BlockNum     = NTOHS (Packet->Data.Block);

+  Expected     = Mtftp6GetNextBlockNum (&Instance->BlkList);

+

+  ASSERT (Expected >= 0);

+

+  //

+  // If we are active and received an unexpected packet, retransmit

+  // the last ACK then restart receiving. If we are passive, save

+  // the block.

+  //

+  if (Instance->IsMaster && (Expected != BlockNum)) {

+    //

+    // Free the received packet before send new packet in ReceiveNotify,

+    // since the udpio might need to be reconfigured.

+    //

+    NetbufFree (*UdpPacket);

+    *UdpPacket = NULL;

+

+    Mtftp6TransmitPacket (Instance, Instance->LastPacket);

+    return EFI_SUCCESS;

+  }

+

+  Status = Mtftp6RrqSaveBlock (Instance, Packet, Len, UdpPacket);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Reset the passive client's timer whenever it received a valid data packet.

+  //

+  if (!Instance->IsMaster) {

+    Instance->PacketToLive = Instance->Timeout * 2;

+  }

+

+  //

+  // Check whether we have received all the blocks. Send the ACK if we

+  // are active (unicast client or master client for multicast download).

+  // If we have received all the blocks, send an ACK even if we are passive

+  // to tell the server that we are done.

+  //

+  Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);

+

+  if (Instance->IsMaster || Expected < 0) {

+    if (Expected < 0) {

+      //

+      // If we are passive client, then the just received Block maybe

+      // isn't the last block. We need to send an ACK to the last block

+      // to inform the server that we are done. If we are active client,

+      // the Block == Instance->LastBlock.

+      //

+      BlockNum     = Instance->LastBlk;

+      *IsCompleted = TRUE;

+

+    } else {

+      BlockNum     = (UINT16) (Expected - 1);

+    }

+    //

+    // Free the received packet before send new packet in ReceiveNotify,

+    // since the udpio might need to be reconfigured.

+    //

+    NetbufFree (*UdpPacket);

+    *UdpPacket = NULL;

+

+    Mtftp6RrqSendAck (Instance, BlockNum);

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Validate whether the options received in the server's OACK packet is valid.

+  The options are valid only if:

+  1. The server doesn't include options not requested by us.

+  2. The server can only use smaller blksize than that is requested.

+  3. The server can only use the same timeout as requested.

+  4. The server doesn't change its multicast channel.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  ReplyInfo             The pointer to options information in reply packet.

+  @param[in]  RequestInfo           The pointer to requested options info.

+

+  @retval     TRUE                  If the option in the OACK is valid.

+  @retval     FALSE                 If the option is invalid.

+

+**/

+BOOLEAN

+Mtftp6RrqOackValid (

+  IN MTFTP6_INSTANCE           *Instance,

+  IN MTFTP6_EXT_OPTION_INFO    *ReplyInfo,

+  IN MTFTP6_EXT_OPTION_INFO    *RequestInfo

+  )

+{

+  //

+  // It is invalid for server to return options we don't request

+  //

+  if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {

+    return FALSE;

+  }

+

+  //

+  // Server can only specify a smaller block size to be used and

+  // return the timeout matches that requested.

+  //

+  if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||

+      (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))

+      ) {

+    return FALSE;

+  }

+

+  //

+  // The server can send ",,master" to client to change its master

+  // setting. But if it use the specific multicast channel, it can't

+  // change the setting.

+  //

+  if (((ReplyInfo->BitMap & MTFTP6_OPT_MCAST_BIT) != 0) && !NetIp6IsUnspecifiedAddr (&Instance->McastIp)) {

+

+    if (!NetIp6IsUnspecifiedAddr (&ReplyInfo->McastIp) && CompareMem (

+                                                            &ReplyInfo->McastIp,

+                                                            &Instance->McastIp,

+                                                            sizeof (EFI_IPv6_ADDRESS)

+                                                            ) != 0) {

+      return FALSE;

+    }

+

+    if ((ReplyInfo->McastPort != 0) && (ReplyInfo->McastPort != Instance->McastPort)) {

+      return FALSE;

+    }

+  }

+

+  return TRUE;

+}

+

+

+/**

+  Configure Udp6Io to receive a packet from a multicast address.

+

+  @param[in]  McastIo               The pointer to the mcast Udp6Io.

+  @param[in]  Context               The pointer to the context.

+

+  @retval EFI_SUCCESS           The mcast Udp6Io was successfully configured.

+  @retval Others                Failed to configure the Udp6Io.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6RrqConfigMcastUdpIo (

+  IN UDP_IO                 *McastIo,

+  IN VOID                   *Context

+  )

+{

+  EFI_STATUS                Status;

+  EFI_UDP6_PROTOCOL         *Udp6;

+  EFI_UDP6_CONFIG_DATA      *Udp6Cfg;

+  EFI_IPv6_ADDRESS          Group;

+  MTFTP6_INSTANCE           *Instance;

+

+  Udp6     = McastIo->Protocol.Udp6;

+  Udp6Cfg  = &(McastIo->Config.Udp6);

+  Instance = (MTFTP6_INSTANCE *) Context;

+

+  //

+  // Set the configure data for the mcast Udp6Io.

+  //

+  ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));

+

+  Udp6Cfg->AcceptPromiscuous  = FALSE;

+  Udp6Cfg->AcceptAnyPort      = FALSE;

+  Udp6Cfg->AllowDuplicatePort = FALSE;

+  Udp6Cfg->TrafficClass       = 0;

+  Udp6Cfg->HopLimit           = 128;

+  Udp6Cfg->ReceiveTimeout     = 0;

+  Udp6Cfg->TransmitTimeout    = 0;

+  Udp6Cfg->StationPort        = Instance->McastPort;

+  Udp6Cfg->RemotePort         = 0;

+

+  CopyMem (

+    &Udp6Cfg->RemoteAddress,

+    &Instance->ServerIp,

+    sizeof (EFI_IPv6_ADDRESS)

+    );

+

+  //

+  // Configure the mcast Udp6Io.

+  //

+  Status = Udp6->Configure (Udp6, Udp6Cfg);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Join the multicast group

+  //

+  CopyMem (&Group, &Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));

+

+  return Udp6->Groups (Udp6, TRUE, &Group);

+}

+

+

+/**

+  Process the OACK packet for Rrq.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  Packet                The pointer to the received packet.

+  @param[in]  Len                   The length of the packet.

+  @param[out] UdpPacket             The net buf of received packet.

+  @param[out] IsCompleted           If TRUE, the download has been completed.

+                                    Otherwise, the download has not been completed.

+

+  @retval EFI_DEVICE_ERROR      Failed to create/start a multicast Udp6 child.

+  @retval EFI_TFTP_ERROR        An error happened during the process.

+  @retval EFI_SUCCESS           The OACK packet successfully processed.

+

+**/

+EFI_STATUS

+Mtftp6RrqHandleOack (

+  IN  MTFTP6_INSTANCE       *Instance,

+  IN  EFI_MTFTP6_PACKET     *Packet,

+  IN  UINT32                Len,

+  OUT NET_BUF               **UdpPacket,

+  OUT BOOLEAN               *IsCompleted

+  )

+{

+  EFI_MTFTP6_OPTION         *Options;

+  UINT32                    Count;

+  MTFTP6_EXT_OPTION_INFO    ExtInfo;

+  EFI_STATUS                Status;

+  INTN                      Expected;

+

+  *IsCompleted = FALSE;

+

+  //

+  // If already started the master download, don't change the

+  // setting. Master download always succeeds.

+  //

+  Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);

+  ASSERT (Expected != -1);

+

+  if (Instance->IsMaster && Expected != 1) {

+    return EFI_SUCCESS;

+  }

+

+  ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));

+

+  //

+  // Parse the options in the packet.

+  //

+  Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Parse the extensive options in the packet.

+  //

+  Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);

+

+  if (EFI_ERROR (Status) || !Mtftp6RrqOackValid (Instance, &ExtInfo, &Instance->ExtInfo)) {

+    //

+    // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.

+    //

+    if (Status != EFI_OUT_OF_RESOURCES) {

+      //

+      // Free the received packet before send new packet in ReceiveNotify,

+      // since the udpio might need to be reconfigured.

+      //

+      NetbufFree (*UdpPacket);

+      *UdpPacket = NULL;

+      //

+      // Send the Mtftp6 error message if invalid packet.

+      //

+      Mtftp6SendError (

+        Instance,

+        EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,

+        (UINT8 *) "Mal-formated OACK packet"

+        );

+    }

+

+    return EFI_TFTP_ERROR;

+  }

+

+  if ((ExtInfo.BitMap & MTFTP6_OPT_MCAST_BIT) != 0) {

+

+    //

+    // Save the multicast info. Always update the Master, only update the

+    // multicast IP address, block size, timeoute at the first time. If IP

+    // address is updated, create a UDP child to receive the multicast.

+    //

+    Instance->IsMaster = ExtInfo.IsMaster;

+

+    if (NetIp6IsUnspecifiedAddr (&Instance->McastIp)) {

+      if (NetIp6IsUnspecifiedAddr (&ExtInfo.McastIp) || ExtInfo.McastPort == 0) {

+        //

+        // Free the received packet before send new packet in ReceiveNotify,

+        // since the udpio might need to be reconfigured.

+        //

+        NetbufFree (*UdpPacket);

+        *UdpPacket = NULL;

+        //

+        // Send the Mtftp6 error message if invalid multi-cast setting.

+        //

+        Mtftp6SendError (

+          Instance,

+          EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,

+          (UINT8 *) "Illegal multicast setting"

+          );

+

+        return EFI_TFTP_ERROR;

+      }

+

+      //

+      // Create a UDP child then start receive the multicast from it.

+      //

+      CopyMem (

+        &Instance->McastIp,

+        &ExtInfo.McastIp,

+        sizeof (EFI_IP_ADDRESS)

+        );

+

+      Instance->McastPort  = ExtInfo.McastPort;

+      Instance->McastUdpIo = UdpIoCreateIo (

+                               Instance->Service->Controller,

+                               Instance->Service->Image,

+                               Mtftp6RrqConfigMcastUdpIo,

+                               UDP_IO_UDP6_VERSION,

+                               Instance

+                               );

+

+      if (Instance->McastUdpIo == NULL) {

+        return EFI_DEVICE_ERROR;

+      }

+

+      Status = UdpIoRecvDatagram (

+                 Instance->McastUdpIo,

+                 Mtftp6RrqInput,

+                 Instance,

+                 0

+                 );

+

+      if (EFI_ERROR (Status)) {

+        //

+        // Free the received packet before send new packet in ReceiveNotify,

+        // since the udpio might need to be reconfigured.

+        //

+        NetbufFree (*UdpPacket);

+        *UdpPacket = NULL;

+        //

+        // Send the Mtftp6 error message if failed to create Udp6Io to receive.

+        //

+        Mtftp6SendError (

+          Instance,

+          EFI_MTFTP6_ERRORCODE_ACCESS_VIOLATION,

+          (UINT8 *) "Failed to create socket to receive multicast packet"

+          );

+

+        return Status;

+      }

+

+      //

+      // Update the parameters used.

+      //

+      if (ExtInfo.BlkSize != 0) {

+        Instance->BlkSize = ExtInfo.BlkSize;

+      }

+

+      if (ExtInfo.Timeout != 0) {

+        Instance->Timeout = ExtInfo.Timeout;

+      }

+    }

+

+  } else {

+

+    Instance->IsMaster = TRUE;

+

+    if (ExtInfo.BlkSize != 0) {

+      Instance->BlkSize = ExtInfo.BlkSize;

+    }

+

+    if (ExtInfo.Timeout != 0) {

+      Instance->Timeout = ExtInfo.Timeout;

+    }

+  }

+

+  //

+  // Free the received packet before send new packet in ReceiveNotify,

+  // since the udpio might need to be reconfigured.

+  //

+  NetbufFree (*UdpPacket);

+  *UdpPacket = NULL;

+  //

+  // Send an ACK to (Expected - 1) which is 0 for unicast download,

+  // or tell the server we want to receive the Expected block.

+  //

+  return Mtftp6RrqSendAck (Instance, (UINT16) (Expected - 1));

+}

+

+

+/**

+  The packet process callback for Mtftp6 download.

+

+  @param[in]  UdpPacket             The pointer to the packet received.

+  @param[in]  UdpEpt                The pointer to the Udp6 access point.

+  @param[in]  IoStatus              The status from Udp6 instance.

+  @param[in]  Context               The pointer to the context.

+

+**/

+VOID

+EFIAPI

+Mtftp6RrqInput (

+  IN NET_BUF                *UdpPacket,

+  IN UDP_END_POINT          *UdpEpt,

+  IN EFI_STATUS             IoStatus,

+  IN VOID                   *Context

+  )

+{

+  MTFTP6_INSTANCE           *Instance;

+  EFI_MTFTP6_PACKET         *Packet;

+  BOOLEAN                   IsCompleted;

+  BOOLEAN                   IsMcast;

+  EFI_STATUS                Status;

+  UINT16                    Opcode;

+  UINT32                    TotalNum;

+  UINT32                    Len;

+

+  Instance = (MTFTP6_INSTANCE *) Context;

+

+  NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);

+

+  Status      = EFI_SUCCESS;

+  Packet      = NULL;

+  IsCompleted = FALSE;

+  IsMcast     = FALSE;

+  TotalNum    = 0;

+

+  //

+  // Return error status if Udp6 instance failed to receive.

+  //

+  if (EFI_ERROR (IoStatus)) {

+    Status = IoStatus;

+    goto ON_EXIT;

+  }

+

+  ASSERT (UdpPacket != NULL);

+

+  if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Find the port this packet is from to restart receive correctly.

+  //

+  if (CompareMem (

+        Ip6Swap128 (&UdpEpt->LocalAddr.v6),

+        &Instance->McastIp,

+        sizeof (EFI_IPv6_ADDRESS)

+        ) == 0) {

+    IsMcast = TRUE;

+  } else {

+    IsMcast = FALSE;

+  }

+

+  //

+  // Client send initial request to server's listening port. Server

+  // will select a UDP port to communicate with the client. The server

+  // is required to use the same port as RemotePort to multicast the

+  // data.

+  //

+  if (UdpEpt->RemotePort != Instance->ServerDataPort) {

+    if (Instance->ServerDataPort != 0) {

+      goto ON_EXIT;

+    } else {

+      //

+      // For the subsequent exchange of requests, reconfigure the udpio as

+      // (serverip, serverport, localip, localport).

+      // Ususally, the client set serverport as 0 to receive and reset it

+      // once the first packet arrives to send ack.

+      //

+      Instance->ServerDataPort = UdpEpt->RemotePort;

+    }

+  }

+

+  //

+  // Copy the MTFTP packet to a continuous buffer if it isn't already so.

+  //

+  Len      = UdpPacket->TotalSize;

+  TotalNum = UdpPacket->BlockOpNum;

+

+  if (TotalNum > 1) {

+    Packet = AllocateZeroPool (Len);

+

+    if (Packet == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_EXIT;

+    }

+

+    NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);

+

+  } else {

+    Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);

+    ASSERT (Packet != NULL);

+  }

+

+  Opcode = NTOHS (Packet->OpCode);

+

+  //

+  // Callback to the user's CheckPacket if provided. Abort the transmission

+  // if CheckPacket returns an EFI_ERROR code.

+  //

+  if ((Instance->Token->CheckPacket != NULL) &&

+      (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)

+      ) {

+

+    Status = Instance->Token->CheckPacket (

+                                &Instance->Mtftp6,

+                                Instance->Token,

+                                (UINT16) Len,

+                                Packet

+                                );

+

+    if (EFI_ERROR (Status)) {

+      //

+      // Send an error message to the server to inform it

+      //

+      if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {

+        //

+        // Free the received packet before send new packet in ReceiveNotify,

+        // since the udpio might need to be reconfigured.

+        //

+        NetbufFree (UdpPacket);

+        UdpPacket = NULL;

+        //

+        // Send the Mtftp6 error message if user aborted the current session.

+        //

+        Mtftp6SendError (

+          Instance,

+          EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,

+          (UINT8 *) "User aborted the transfer"

+          );

+      }

+

+      Status = EFI_ABORTED;

+      goto ON_EXIT;

+    }

+  }

+

+  //

+  // Switch the process routines by the operation code.

+  //

+  switch (Opcode) {

+  case EFI_MTFTP6_OPCODE_DATA:

+    if ((Len > (UINT32) (MTFTP6_DATA_HEAD_LEN + Instance->BlkSize)) || (Len < (UINT32) MTFTP6_DATA_HEAD_LEN)) {

+      goto ON_EXIT;

+    }

+    //

+    // Handle the data packet of Rrq.

+    //

+    Status = Mtftp6RrqHandleData (

+               Instance,

+               Packet,

+               Len,

+               &UdpPacket,

+               &IsCompleted

+               );

+    break;

+

+  case EFI_MTFTP6_OPCODE_OACK:

+    if (IsMcast || Len <= MTFTP6_OPCODE_LEN) {

+      goto ON_EXIT;

+    }

+    //

+    // Handle the Oack packet of Rrq.

+    //

+    Status = Mtftp6RrqHandleOack (

+               Instance,

+               Packet,

+               Len,

+               &UdpPacket,

+               &IsCompleted

+               );

+    break;

+

+  default:

+    //

+    // Drop and return eror if received error message.

+    //

+    Status = EFI_TFTP_ERROR;

+    break;

+  }

+

+ON_EXIT:

+  //

+  // Free the resources, then if !EFI_ERROR (Status), restart the

+  // receive, otherwise end the session.

+  //

+  if (Packet != NULL && TotalNum > 1) {

+    FreePool (Packet);

+  }

+  if (UdpPacket != NULL) {

+    NetbufFree (UdpPacket);

+  }

+  if (!EFI_ERROR (Status) && !IsCompleted) {

+    if (IsMcast) {

+      Status = UdpIoRecvDatagram (

+                 Instance->McastUdpIo,

+                 Mtftp6RrqInput,

+                 Instance,

+                 0

+                 );

+    } else {

+      Status = UdpIoRecvDatagram (

+                 Instance->UdpIo,

+                 Mtftp6RrqInput,

+                 Instance,

+                 0

+                 );

+    }

+  }

+  //

+  // Clean up the current session if failed to continue.

+  //

+  if (EFI_ERROR (Status) || IsCompleted) {

+    Mtftp6OperationClean (Instance, Status);

+  }

+}

+

+

+/**

+  Start the Mtftp6 instance to download. It first initializes some

+  of the internal states, then builds and sends an RRQ reqeuest packet.

+  Finally, it starts receive for the downloading.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  Operation             The operation code of current packet.

+

+  @retval EFI_SUCCESS           The Mtftp6 is started to download.

+  @retval Others                Failed to start to download.

+

+**/

+EFI_STATUS

+Mtftp6RrqStart (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 Operation

+  )

+{

+  EFI_STATUS                Status;

+

+  //

+  // The valid block number range are [1, 0xffff]. For example:

+  // the client sends an RRQ request to the server, the server

+  // transfers the DATA1 block. If option negoitation is ongoing,

+  // the server will send back an OACK, then client will send ACK0.

+  //

+  Status = Mtftp6InitBlockRange (&Instance->BlkList, 1, 0xffff);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = Mtftp6SendRequest (Instance, Operation);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  return UdpIoRecvDatagram (

+           Instance->UdpIo,

+           Mtftp6RrqInput,

+           Instance,

+           0

+           );

+}

+

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c
new file mode 100644
index 0000000..24ce0e8
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c
@@ -0,0 +1,1189 @@
+/** @file

+  Mtftp6 support functions implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Mtftp6Impl.h"

+

+

+/**

+  Allocate a MTFTP block range, then init it to the range of [Start, End].

+

+  @param[in]  Start                  The start block number.

+  @param[in]  End                    The last block number in the range.

+

+  @return Range                      The range of the allocated block buffer.

+

+**/

+MTFTP6_BLOCK_RANGE *

+Mtftp6AllocateRange (

+  IN UINT16                 Start,

+  IN UINT16                 End

+  )

+{

+  MTFTP6_BLOCK_RANGE        *Range;

+

+  Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE));

+

+  if (Range == NULL) {

+    return NULL;

+  }

+

+  InitializeListHead (&Range->Link);

+  Range->Start  = Start;

+  Range->End    = End;

+  Range->Bound  = End;

+

+  return Range;

+}

+

+

+/**

+  Initialize the block range for either RRQ or WRQ. RRQ and WRQ have

+  different requirements for Start and End. For example, during startup,

+  WRQ initializes its whole valid block range to [0, 0xffff]. This

+  is bacause the server will send an ACK0 to inform the user to start the

+  upload. When the client receives an ACK0, it will remove 0 from the range,

+  get the next block number, which is 1, then upload the BLOCK1. For RRQ

+  without option negotiation, the server will directly send the BLOCK1

+  in response to the client's RRQ. When received BLOCK1, the client will

+  remove it from the block range and send an ACK. It also works if there

+  is option negotiation.

+

+  @param[in]  Head                   The block range head to initialize.

+  @param[in]  Start                  The Start block number.

+  @param[in]  End                    The last block number.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for initial block range.

+  @retval EFI_SUCCESS            The initial block range is created.

+

+**/

+EFI_STATUS

+Mtftp6InitBlockRange (

+  IN LIST_ENTRY             *Head,

+  IN UINT16                 Start,

+  IN UINT16                 End

+  )

+{

+  MTFTP6_BLOCK_RANGE        *Range;

+

+  Range = Mtftp6AllocateRange (Start, End);

+

+  if (Range == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  InsertTailList (Head, &Range->Link);

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Get the first valid block number on the range list.

+

+  @param[in]  Head                   The block range head.

+

+  @retval     ==-1                   If the block range is empty.

+  @retval     >-1                    The first valid block number.

+

+**/

+INTN

+Mtftp6GetNextBlockNum (

+  IN LIST_ENTRY              *Head

+  )

+{

+  MTFTP6_BLOCK_RANGE  *Range;

+

+  if (IsListEmpty (Head)) {

+    return -1;

+  }

+

+  Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link);

+  return Range->Start;

+}

+

+

+/**

+  Set the last block number of the block range list. It

+  removes all the blocks after the Last. MTFTP initialize the

+  block range to the maximum possible range, such as [0, 0xffff]

+  for WRQ. When it gets the last block number, it calls

+  this function to set the last block number.

+

+  @param[in]  Head                   The block range list.

+  @param[in]  Last                   The last block number.

+

+**/

+VOID

+Mtftp6SetLastBlockNum (

+  IN LIST_ENTRY             *Head,

+  IN UINT16                 Last

+  )

+{

+  MTFTP6_BLOCK_RANGE        *Range;

+

+  //

+  // Iterate from the tail to head to remove the block number

+  // after the last.

+  //

+  while (!IsListEmpty (Head)) {

+    Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link);

+

+    if (Range->Start > Last) {

+      RemoveEntryList (&Range->Link);

+      FreePool (Range);

+      continue;

+    }

+

+    if (Range->End > Last) {

+      Range->End = Last;

+    }

+    return ;

+  }

+}

+

+

+/**

+  Remove the block number from the block range list.

+

+  @param[in]  Head                   The block range list to remove from.

+  @param[in]  Num                    The block number to remove.

+  @param[in]  Completed              Whether Num is the last block number

+  @param[out] TotalBlock             The continuous block number in all

+

+  @retval EFI_NOT_FOUND          The block number isn't in the block range list.

+  @retval EFI_SUCCESS            The block number has been removed from the list.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.

+

+**/

+EFI_STATUS

+Mtftp6RemoveBlockNum (

+  IN LIST_ENTRY             *Head,

+  IN UINT16                 Num,

+  IN BOOLEAN                Completed,

+  OUT UINT64                *TotalBlock

+  )

+{

+  MTFTP6_BLOCK_RANGE        *Range;

+  MTFTP6_BLOCK_RANGE        *NewRange;

+  LIST_ENTRY                *Entry;

+

+  NET_LIST_FOR_EACH (Entry, Head) {

+

+    //

+    // Each block represents a hole [Start, End] in the file,

+    // skip to the first range with End >= Num

+    //

+    Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);

+

+    if (Range->End < Num) {

+      continue;

+    }

+

+    //

+    // There are three different cases for Start

+    // 1. (Start > Num) && (End >= Num):

+    //    because all the holes before this one has the condition of

+    //    End < Num, so this block number has been removed.

+    //

+    // 2. (Start == Num) && (End >= Num):

+    //    Need to increase the Start by one, and if End == Num, this

+    //    hole has been removed completely, remove it.

+    //

+    // 3. (Start < Num) && (End >= Num):

+    //    if End == Num, only need to decrease the End by one because

+    //    we have (Start < Num) && (Num == End), so (Start <= End - 1).

+    //    if (End > Num), the hold is splited into two holes, with

+    //    [Start, Num - 1] and [Num + 1, End].

+    //

+    if (Range->Start > Num) {

+      return EFI_NOT_FOUND;

+

+    } else if (Range->Start == Num) {

+      Range->Start++;

+

+      //

+      // Note that: RFC 1350 does not mention block counter roll-over,

+      // but several TFTP hosts implement the roll-over be able to accept

+      // transfers of unlimited size. There is no consensus, however, whether

+      // the counter should wrap around to zero or to one. Many implementations

+      // wrap to zero, because this is the simplest to implement. Here we choose

+      // this solution.

+      //

+      *TotalBlock  = Num;

+

+      if (Range->Round > 0) {

+        *TotalBlock += Range->Bound +  MultU64x32 ((UINT64) (Range->Round -1), (UINT32)(Range->Bound + 1)) + 1;

+      }

+

+      if (Range->Start > Range->Bound) {

+        Range->Start = 0;

+        Range->Round ++;

+      }

+

+      if ((Range->Start > Range->End) || Completed) {

+        RemoveEntryList (&Range->Link);

+        FreePool (Range);

+      }

+

+      return EFI_SUCCESS;

+

+    } else {

+      if (Range->End == Num) {

+        Range->End--;

+      } else {

+        NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);

+

+        if (NewRange == NULL) {

+          return EFI_OUT_OF_RESOURCES;

+        }

+

+        Range->End = Num - 1;

+        NetListInsertAfter (&Range->Link, &NewRange->Link);

+      }

+

+      return EFI_SUCCESS;

+    }

+  }

+

+  return EFI_NOT_FOUND;

+}

+

+

+/**

+  Configure the opened Udp6 instance until the corresponding Ip6 instance

+  has been configured.

+

+  @param[in]  UdpIo                  The pointer to the Udp6 Io.

+  @param[in]  UdpCfgData             The pointer to the Udp6 configure data.

+

+  @retval EFI_SUCCESS            Configure the Udp6 instance successfully.

+  @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not

+                                 been configured yet.

+

+**/

+EFI_STATUS

+Mtftp6GetMapping (

+  IN UDP_IO                 *UdpIo,

+  IN EFI_UDP6_CONFIG_DATA   *UdpCfgData

+  )

+{

+  EFI_IP6_MODE_DATA         Ip6Mode;

+  EFI_UDP6_PROTOCOL         *Udp6;

+  EFI_STATUS                Status;

+  EFI_EVENT                 Event;

+

+  Event  = NULL;

+  Udp6   = UdpIo->Protocol.Udp6;

+

+  //

+  // Create a timer to check whether the Ip6 instance configured or not.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_TIMER,

+                  TPL_CALLBACK,

+                  NULL,

+                  NULL,

+                  &Event

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  Status = gBS->SetTimer (

+                  Event,

+                  TimerRelative,

+                  MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Check the Ip6 mode data till timeout.

+  //

+  while (EFI_ERROR (gBS->CheckEvent (Event))) {

+

+    Udp6->Poll (Udp6);

+

+    Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL);

+

+    if (!EFI_ERROR (Status)) {

+

+      if  (Ip6Mode.IsConfigured) {

+        //

+        // Continue to configure the Udp6 instance.

+        //

+        Status = Udp6->Configure (Udp6, UdpCfgData);

+      } else {

+        Status = EFI_NO_MAPPING;

+      }

+    }

+  }

+

+ON_EXIT:

+

+  if (Event != NULL) {

+    gBS->CloseEvent (Event);

+  }

+

+  return Status;

+}

+

+

+/**

+  The dummy configure routine for create a new Udp6 Io.

+

+  @param[in]  UdpIo                  The pointer to the Udp6 Io.

+  @param[in]  Context                The pointer to the context.

+

+  @retval EFI_SUCCESS                This value is always returned.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ConfigDummyUdpIo (

+  IN UDP_IO                 *UdpIo,

+  IN VOID                   *Context

+  )

+{

+  return EFI_SUCCESS;

+}

+

+

+/**

+  The configure routine for Mtftp6 instance to transmit/receive.

+

+  @param[in]  UdpIo                  The pointer to the Udp6 Io.

+  @param[in]  ServerIp               The pointer to the server address.

+  @param[in]  ServerPort             The pointer to the server port.

+  @param[in]  LocalIp                The pointer to the local address.

+  @param[in]  LocalPort              The pointer to the local port.

+

+  @retval EFI_SUCCESS            Configured the Udp6 Io for Mtftp6 successfully.

+  @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not been

+                                 configured yet.

+

+**/

+EFI_STATUS

+Mtftp6ConfigUdpIo (

+  IN UDP_IO                 *UdpIo,

+  IN EFI_IPv6_ADDRESS       *ServerIp,

+  IN UINT16                 ServerPort,

+  IN EFI_IPv6_ADDRESS       *LocalIp,

+  IN UINT16                 LocalPort

+  )

+{

+  EFI_STATUS                Status;

+  EFI_UDP6_PROTOCOL         *Udp6;

+  EFI_UDP6_CONFIG_DATA      *Udp6Cfg;

+

+  Udp6    = UdpIo->Protocol.Udp6;

+  Udp6Cfg = &(UdpIo->Config.Udp6);

+

+  ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));

+

+  //

+  // Set the Udp6 Io configure data.

+  //

+  Udp6Cfg->AcceptPromiscuous  = FALSE;

+  Udp6Cfg->AcceptAnyPort      = FALSE;

+  Udp6Cfg->AllowDuplicatePort = FALSE;

+  Udp6Cfg->TrafficClass       = 0;

+  Udp6Cfg->HopLimit           = 128;

+  Udp6Cfg->ReceiveTimeout     = 0;

+  Udp6Cfg->TransmitTimeout    = 0;

+  Udp6Cfg->StationPort        = LocalPort;

+  Udp6Cfg->RemotePort         = ServerPort;

+

+  CopyMem (

+    &Udp6Cfg->StationAddress,

+    LocalIp,

+    sizeof (EFI_IPv6_ADDRESS)

+    );

+

+  CopyMem (

+    &Udp6Cfg->RemoteAddress,

+    ServerIp,

+    sizeof (EFI_IPv6_ADDRESS)

+    );

+

+  //

+  // Configure the Udp6 instance with current configure data.

+  //

+  Status = Udp6->Configure (Udp6, Udp6Cfg);

+

+  if (Status == EFI_NO_MAPPING) {

+

+    return Mtftp6GetMapping (UdpIo, Udp6Cfg);

+  }

+

+  return Status;

+}

+

+

+/**

+  Build and transmit the request packet for the Mtftp6 instance.

+

+  @param[in]  Instance               The pointer to the Mtftp6 instance.

+  @param[in]  Operation              The operation code of this packet.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the request.

+  @retval EFI_SUCCESS            The request is built and sent.

+  @retval Others                 Failed to transmit the packet.

+

+**/

+EFI_STATUS

+Mtftp6SendRequest (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 Operation

+  )

+{

+  EFI_MTFTP6_PACKET         *Packet;

+  EFI_MTFTP6_OPTION         *Options;

+  EFI_MTFTP6_TOKEN          *Token;

+  NET_BUF                   *Nbuf;

+  UINT8                     *Mode;

+  UINT8                     *Cur;

+  UINT32                    Len1;

+  UINT32                    Len2;

+  UINT32                    Len;

+  UINTN                     Index;

+

+  Token   = Instance->Token;

+  Options = Token->OptionList;

+  Mode    = Token->ModeStr;

+

+  if (Mode == NULL) {

+    Mode = (UINT8 *) "octet";

+  }

+

+  //

+  // The header format of RRQ/WRQ packet is:

+  //

+  //   2 bytes     string    1 byte     string   1 byte

+  //   ------------------------------------------------

+  //  | Opcode |  Filename  |   0  |    Mode    |   0  |

+  //   ------------------------------------------------

+  //

+  // The common option format is:

+  //

+  //    string     1 byte     string   1 byte

+  //   ---------------------------------------

+  //  | OptionStr |   0  |  ValueStr  |   0   |

+  //   ---------------------------------------

+  //

+

+  //

+  // Compute the size of new Mtftp6 packet.

+  //

+  Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Token->Filename);

+  Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Mode);

+  Len  = Len1 + Len2 + 4;

+

+  for (Index = 0; Index < Token->OptionCount; Index++) {

+    Len1  = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);

+    Len2  = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);

+    Len  += Len1 + Len2 + 2;

+  }

+

+  //

+  // Allocate a packet then copy the data.

+  //

+  if ((Nbuf = NetbufAlloc (Len)) == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Copy the opcode, filename and mode into packet.

+  //

+  Packet         = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);

+  ASSERT (Packet != NULL);

+

+  Packet->OpCode = HTONS (Operation);

+  Cur            = Packet->Rrq.Filename;

+  Cur            = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Token->Filename);

+  Cur           += AsciiStrLen ((CHAR8 *) Token->Filename) + 1;

+  Cur            = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Mode);

+  Cur           += AsciiStrLen ((CHAR8 *) Mode) + 1;

+

+  //

+  // Copy all the extension options into the packet.

+  //

+  for (Index = 0; Index < Token->OptionCount; ++Index) {

+    Cur  = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].OptionStr);

+    Cur += AsciiStrLen ((CHAR8 *) Options[Index].OptionStr) + 1;

+    Cur  = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].ValueStr);

+    Cur += AsciiStrLen ((CHAR8 *) (CHAR8 *) Options[Index].ValueStr) + 1;

+  }

+

+  //

+  // Save the packet buf for retransmit

+  //

+  if (Instance->LastPacket != NULL) {

+    NetbufFree (Instance->LastPacket);

+  }

+

+  Instance->LastPacket = Nbuf;

+  Instance->CurRetry   = 0;

+

+  return Mtftp6TransmitPacket (Instance, Nbuf);

+}

+

+

+/**

+  Build and send an error packet.

+

+  @param[in]  Instance               The pointer to the Mtftp6 instance.

+  @param[in]  ErrCode                The error code in the packet.

+  @param[in]  ErrInfo                The error message in the packet.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the error packet.

+  @retval EFI_SUCCESS            The error packet is transmitted.

+  @retval Others                 Failed to transmit the packet.

+

+**/

+EFI_STATUS

+Mtftp6SendError (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 ErrCode,

+  IN UINT8*                 ErrInfo

+  )

+{

+  NET_BUF                   *Nbuf;

+  EFI_MTFTP6_PACKET         *TftpError;

+  UINT32                    Len;

+

+  //

+  // Allocate a packet then copy the data.

+  //

+  Len  = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER));

+  Nbuf = NetbufAlloc (Len);

+

+  if (Nbuf == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);

+

+  if (TftpError == NULL) {

+    NetbufFree (Nbuf);

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  TftpError->OpCode          = HTONS (EFI_MTFTP6_OPCODE_ERROR);

+  TftpError->Error.ErrorCode = HTONS (ErrCode);

+

+  AsciiStrCpy ((CHAR8 *) TftpError->Error.ErrorMessage, (CHAR8 *) ErrInfo);

+

+  //

+  // Save the packet buf for retransmit

+  //

+  if (Instance->LastPacket != NULL) {

+    NetbufFree (Instance->LastPacket);

+  }

+

+  Instance->LastPacket = Nbuf;

+  Instance->CurRetry   = 0;

+

+  return Mtftp6TransmitPacket (Instance, Nbuf);

+}

+

+

+/**

+  The callback function called when the packet is transmitted.

+

+  @param[in]  Packet                 The pointer to the packet.

+  @param[in]  UdpEpt                 The pointer to the Udp6 access point.

+  @param[in]  IoStatus               The result of the transmission.

+  @param[in]  Context                The pointer to the context.

+

+**/

+VOID

+EFIAPI

+Mtftp6OnPacketSent (

+  IN NET_BUF                   *Packet,

+  IN UDP_END_POINT             *UdpEpt,

+  IN EFI_STATUS                IoStatus,

+  IN VOID                      *Context

+  )

+{

+  NetbufFree (Packet);

+  *(BOOLEAN *) Context = TRUE;

+}

+

+

+/**

+  Send the packet for the Mtftp6 instance.

+

+  @param[in]  Instance               The pointer to the Mtftp6 instance.

+  @param[in]  Packet                 The pointer to the packet to be sent.

+

+  @retval EFI_SUCCESS            The packet was sent out

+  @retval Others                 Failed to transmit the packet.

+

+**/

+EFI_STATUS

+Mtftp6TransmitPacket (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN NET_BUF                *Packet

+  )

+{

+  EFI_UDP6_PROTOCOL         *Udp6;

+  EFI_UDP6_CONFIG_DATA      Udp6CfgData;

+  EFI_STATUS                Status;

+  UINT16                    *Temp;

+  UINT16                    Value;

+  UINT16                    OpCode;

+

+  ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA));

+  Udp6 = Instance->UdpIo->Protocol.Udp6;

+

+  //

+  // Set the live time of the packet.

+  //

+  Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2);

+

+  Temp   = (UINT16 *) NetbufGetByte (Packet, 0, NULL);

+  ASSERT (Temp != NULL);

+

+  Value  = *Temp;

+  OpCode = NTOHS (Value);

+

+  if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) {

+    //

+    // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as

+    // (serverip, 69, localip, localport) to send.

+    // Usually local address and local port are both default as zero.

+    //

+    Status = Udp6->Configure (Udp6, NULL);

+

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    Status = Mtftp6ConfigUdpIo (

+               Instance->UdpIo,

+               &Instance->ServerIp,

+               Instance->ServerCmdPort,

+               &Instance->Config->StationIp,

+               Instance->Config->LocalPort

+               );

+

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    //

+    // Get the current local address and port by get Udp6 mode data.

+    //

+    Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    NET_GET_REF (Packet);

+

+    Instance->IsTransmitted = FALSE;

+

+    Status = UdpIoSendDatagram (

+               Instance->UdpIo,

+               Packet,

+               NULL,

+               NULL,

+               Mtftp6OnPacketSent,

+               &Instance->IsTransmitted

+               );

+

+    if (EFI_ERROR (Status)) {

+      NET_PUT_REF (Packet);

+      return Status;

+    }

+

+    //

+    // Poll till the packet sent out from the ip6 queue.

+    //

+    gBS->RestoreTPL (Instance->OldTpl);

+

+    while (!Instance->IsTransmitted) {

+      Udp6->Poll (Udp6);

+    }

+

+    Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+    //

+    // For the subsequent exchange of such requests, reconfigure the Udp6Io as

+    // (serverip, 0, localip, localport) to receive.

+    // Currently local address and local port are specified by Udp6 mode data.

+    //

+    Status = Udp6->Configure (Udp6, NULL);

+

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    Status = Mtftp6ConfigUdpIo (

+               Instance->UdpIo,

+               &Instance->ServerIp,

+               Instance->ServerDataPort,

+               &Udp6CfgData.StationAddress,

+               Udp6CfgData.StationPort

+               );

+  } else {

+    //

+    // For the data exchange, configure the Udp6Io as (serverip, dataport,

+    // localip, localport) to send/receive.

+    // Currently local address and local port are specified by Udp6 mode data.

+    //

+    Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    if (Udp6CfgData.RemotePort != Instance->ServerDataPort) {

+

+      Status = Udp6->Configure (Udp6, NULL);

+

+      if (EFI_ERROR (Status)) {

+        return Status;

+      }

+

+      Status = Mtftp6ConfigUdpIo (

+                 Instance->UdpIo,

+                 &Instance->ServerIp,

+                 Instance->ServerDataPort,

+                 &Udp6CfgData.StationAddress,

+                 Udp6CfgData.StationPort

+                 );

+

+      if (EFI_ERROR (Status)) {

+        return Status;

+      }

+    }

+

+    NET_GET_REF (Packet);

+

+    Instance->IsTransmitted = FALSE;

+

+    Status = UdpIoSendDatagram (

+               Instance->UdpIo,

+               Packet,

+               NULL,

+               NULL,

+               Mtftp6OnPacketSent,

+               &Instance->IsTransmitted

+               );

+

+    if (EFI_ERROR (Status)) {

+      NET_PUT_REF (Packet);

+    }

+

+    //

+    // Poll till the packet sent out from the ip6 queue.

+    //

+    gBS->RestoreTPL (Instance->OldTpl);

+

+    while (!Instance->IsTransmitted) {

+      Udp6->Poll (Udp6);

+    }

+

+    Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+  }

+

+  return Status;

+}

+

+

+/**

+  Check packet for GetInfo callback routine.

+

+  GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect

+  the first packet from server, then abort the session.

+

+  @param[in]  This                   The pointer to the Mtftp6 protocol.

+  @param[in]  Token                  The pointer to the Mtftp6 token.

+  @param[in]  PacketLen              The length of the packet.

+  @param[in]  Packet                 The pointer to the received packet.

+

+  @retval EFI_ABORTED            Abort the Mtftp6 operation.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6CheckPacket (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_TOKEN       *Token,

+  IN UINT16                 PacketLen,

+  IN EFI_MTFTP6_PACKET      *Packet

+  )

+{

+  MTFTP6_GETINFO_CONTEXT    *Context;

+  UINT16                    OpCode;

+

+  Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context;

+  OpCode  = NTOHS (Packet->OpCode);

+

+  //

+  // Set the GetInfo's return status according to the OpCode.

+  //

+  switch (OpCode) {

+  case EFI_MTFTP6_OPCODE_ERROR:

+    Context->Status = EFI_TFTP_ERROR;

+    break;

+

+  case EFI_MTFTP6_OPCODE_OACK:

+    Context->Status = EFI_SUCCESS;

+    break;

+

+  default:

+    Context->Status = EFI_PROTOCOL_ERROR;

+  }

+

+  //

+  // Allocate buffer then copy the packet over. Use gBS->AllocatePool

+  // in case NetAllocatePool will implements something tricky.

+  //

+  *(Context->Packet) = AllocateZeroPool (PacketLen);

+

+  if (*(Context->Packet) == NULL) {

+    Context->Status = EFI_OUT_OF_RESOURCES;

+    return EFI_ABORTED;

+  }

+

+  *(Context->PacketLen) = PacketLen;

+  CopyMem (*(Context->Packet), Packet, PacketLen);

+

+  return EFI_ABORTED;

+}

+

+

+/**

+  Clean up the current Mtftp6 operation.

+

+  @param[in]  Instance               The pointer to the Mtftp6 instance.

+  @param[in]  Result                 The result to be returned to the user.

+

+**/

+VOID

+Mtftp6OperationClean (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN EFI_STATUS             Result

+  )

+{

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  MTFTP6_BLOCK_RANGE        *Block;

+

+  //

+  // Clean up the current token and event.

+  //

+  if (Instance->Token != NULL) {

+    Instance->Token->Status = Result;

+    if (Instance->Token->Event != NULL) {

+      gBS->SignalEvent (Instance->Token->Event);

+    }

+    Instance->Token = NULL;

+  }

+

+  //

+  // Clean up the corresponding Udp6Io.

+  //

+  if (Instance->UdpIo != NULL) {

+    UdpIoCleanIo (Instance->UdpIo);

+  }

+

+  if (Instance->McastUdpIo != NULL) {

+    UdpIoFreeIo (Instance->McastUdpIo);

+    Instance->McastUdpIo = NULL;

+  }

+

+  //

+  // Clean up the stored last packet.

+  //

+  if (Instance->LastPacket != NULL) {

+    NetbufFree (Instance->LastPacket);

+    Instance->LastPacket = NULL;

+  }

+

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {

+    Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);

+    RemoveEntryList (Entry);

+    FreePool (Block);

+  }

+

+  //

+  // Reinitialize the corresponding fields of the Mtftp6 operation.

+  //

+  ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));

+  ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS));

+  ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));

+

+  Instance->ServerCmdPort  = 0;

+  Instance->ServerDataPort = 0;

+  Instance->McastPort      = 0;

+  Instance->BlkSize        = 0;

+  Instance->LastBlk        = 0;

+  Instance->PacketToLive   = 0;

+  Instance->MaxRetry       = 0;

+  Instance->CurRetry       = 0;

+  Instance->Timeout        = 0;

+  Instance->IsMaster       = TRUE;

+}

+

+

+/**

+  Start the Mtftp6 instance to perform the operation, such as read file,

+  write file, and read directory.

+

+  @param[in]  This                   The MTFTP session.

+  @param[in]  Token                  The token than encapsues the user's request.

+  @param[in]  OpCode                 The operation to perform.

+

+  @retval EFI_INVALID_PARAMETER  Some of the parameters are invalid.

+  @retval EFI_NOT_STARTED        The MTFTP session hasn't been configured.

+  @retval EFI_ALREADY_STARTED    There is pending operation for the session.

+  @retval EFI_SUCCESS            The operation is successfully started.

+

+**/

+EFI_STATUS

+Mtftp6OperationStart (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_TOKEN       *Token,

+  IN UINT16                 OpCode

+  )

+{

+  MTFTP6_INSTANCE           *Instance;

+  EFI_STATUS                Status;

+

+  if (This == NULL ||

+      Token == NULL ||

+      Token->Filename == NULL ||

+      (Token->OptionCount != 0 && Token->OptionList == NULL) ||

+      (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp))

+      ) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // At least define one method to collect the data for download.

+  //

+  if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) &&

+      Token->Buffer == NULL &&

+      Token->CheckPacket == NULL

+      ) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // At least define one method to provide the data for upload.

+  //

+  if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = MTFTP6_INSTANCE_FROM_THIS (This);

+

+  if (Instance->Config == NULL) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (Instance->Token != NULL) {

+    return EFI_ACCESS_DENIED;

+  }

+

+  Status           = EFI_SUCCESS;

+  Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // Parse the extension options in the request packet.

+  //

+  if (Token->OptionCount != 0) {

+

+    Status = Mtftp6ParseExtensionOption (

+               Token->OptionList,

+               Token->OptionCount,

+               TRUE,

+               &Instance->ExtInfo

+               );

+

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+  }

+

+  //

+  // Initialize runtime data from config data or override data.

+  //

+  Instance->Token           = Token;

+  Instance->ServerCmdPort   = Instance->Config->InitialServerPort;

+  Instance->ServerDataPort  = 0;

+  Instance->MaxRetry        = Instance->Config->TryCount;

+  Instance->Timeout         = Instance->Config->TimeoutValue;

+  Instance->IsMaster        = TRUE;

+

+  CopyMem (

+    &Instance->ServerIp,

+    &Instance->Config->ServerIp,

+    sizeof (EFI_IPv6_ADDRESS)

+    );

+

+  if (Token->OverrideData != NULL) {

+    Instance->ServerCmdPort = Token->OverrideData->ServerPort;

+    Instance->MaxRetry      = Token->OverrideData->TryCount;

+    Instance->Timeout       = Token->OverrideData->TimeoutValue;

+

+    CopyMem (

+      &Instance->ServerIp,

+      &Token->OverrideData->ServerIp,

+      sizeof (EFI_IPv6_ADDRESS)

+      );

+  }

+

+  //

+  // Set default value for undefined parameters.

+  //

+  if (Instance->ServerCmdPort == 0) {

+    Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT;

+  }

+  if (Instance->BlkSize == 0) {

+    Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE;

+  }

+  if (Instance->MaxRetry == 0) {

+    Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY;

+  }

+  if (Instance->Timeout == 0) {

+    Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT;

+  }

+

+  Token->Status = EFI_NOT_READY;

+

+  //

+  // Switch the routines by the operation code.

+  //

+  switch (OpCode) {

+  case EFI_MTFTP6_OPCODE_RRQ:

+    Status = Mtftp6RrqStart (Instance, OpCode);

+    break;

+

+  case EFI_MTFTP6_OPCODE_DIR:

+    Status = Mtftp6RrqStart (Instance, OpCode);

+    break;

+

+  case EFI_MTFTP6_OPCODE_WRQ:

+    Status = Mtftp6WrqStart (Instance, OpCode);

+    break;

+

+  default:

+    Status = EFI_DEVICE_ERROR;

+    goto ON_ERROR;

+  }

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Return immediately for asynchronous or poll the instance for synchronous.

+  //

+  gBS->RestoreTPL (Instance->OldTpl);

+

+  if (Token->Event == NULL) {

+    while (Token->Status == EFI_NOT_READY) {

+      This->Poll (This);

+    }

+    return Token->Status;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  Mtftp6OperationClean (Instance, Status);

+  gBS->RestoreTPL (Instance->OldTpl);

+

+  return Status;

+}

+

+

+/**

+  The timer ticking routine for the Mtftp6 instance.

+

+  @param[in]  Event                  The pointer to the ticking event.

+  @param[in]  Context                The pointer to the context.

+

+**/

+VOID

+EFIAPI

+Mtftp6OnTimerTick (

+  IN EFI_EVENT              Event,

+  IN VOID                   *Context

+  )

+{

+  MTFTP6_SERVICE            *Service;

+  MTFTP6_INSTANCE           *Instance;

+  LIST_ENTRY                *Entry;

+  LIST_ENTRY                *Next;

+  EFI_MTFTP6_TOKEN          *Token;

+  EFI_STATUS                Status;

+

+  Service = (MTFTP6_SERVICE *) Context;

+

+  //

+  // Iterate through all the children of the Mtftp service instance. Time

+  // out the packet. If maximum retries reached, clean the session up.

+  //

+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) {

+

+    Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link);

+

+    if (Instance->Token == NULL) {

+      continue;

+    }

+

+    if (Instance->PacketToLive > 0) {

+      Instance->PacketToLive--;

+      continue;

+    }

+

+    Instance->CurRetry++;

+    Token = Instance->Token;

+

+    if (Token->TimeoutCallback != NULL) {

+      //

+      // Call the timeout callback routine if has.

+      //

+      Status = Token->TimeoutCallback (&Instance->Mtftp6, Token);

+

+      if (EFI_ERROR (Status)) {

+        Mtftp6SendError (

+           Instance,

+           EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,

+           (UINT8 *) "User aborted the transfer in time out"

+           );

+        Mtftp6OperationClean (Instance, EFI_ABORTED);

+        continue;

+      }

+    }

+

+    //

+    // Retransmit the packet if haven't reach the maxmium retry count,

+    // otherwise exit the transfer.

+    //

+    if (Instance->CurRetry < Instance->MaxRetry) {

+      Mtftp6TransmitPacket (Instance, Instance->LastPacket);

+    } else {

+      Mtftp6OperationClean (Instance, EFI_TIMEOUT);

+      continue;

+    }

+  }

+}

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h
new file mode 100644
index 0000000..37f03fe
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h
@@ -0,0 +1,359 @@
+/** @file

+  Mtftp6 support functions declaration.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_MTFTP6_SUPPORT_H__

+#define __EFI_MTFTP6_SUPPORT_H__

+

+//

+// The structure representing a range of block numbers, [Start, End].

+// It is used to remember the holes in the MTFTP block space. If all

+// the holes are filled in, then the download or upload has completed.

+//

+typedef struct {

+  LIST_ENTRY                Link;

+  INTN                      Start;

+  INTN                      End;

+  INTN                      Round;

+  INTN                      Bound;

+} MTFTP6_BLOCK_RANGE;

+

+

+/**

+  Initialize the block range for either RRQ or WRQ. RRQ and WRQ have

+  different requirements for Start and End. For example, during startup,

+  WRQ initializes its whole valid block range to [0, 0xffff]. This

+  is because the server will send an ACK0 to inform the user to start the

+  upload. When the client receives an ACK0, it will remove 0 from the range,

+  get the next block number, which is 1, then upload the BLOCK1. For RRQ

+  without option negotiation, the server will directly send us the BLOCK1

+  in response to the client's RRQ. When BLOCK1 is received, the client will

+  remove it from the block range and send an ACK. It also works if there

+  is option negotiation.

+

+  @param[in]  Head                   The block range head to initialize.

+  @param[in]  Start                  The Start block number.

+  @param[in]  End                    The last block number.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for initial block range.

+  @retval EFI_SUCCESS            The initial block range is created.

+

+**/

+EFI_STATUS

+Mtftp6InitBlockRange (

+  IN LIST_ENTRY             *Head,

+  IN UINT16                 Start,

+  IN UINT16                 End

+  );

+

+

+/**

+  Get the first valid block number on the range list.

+

+  @param[in]  Head                   The block range head.

+

+  @retval     ==-1                   If the block range is empty.

+  @retval     >-1                    The first valid block number.

+

+**/

+INTN

+Mtftp6GetNextBlockNum (

+  IN LIST_ENTRY             *Head

+  );

+

+

+/**

+  Set the last block number of the block range list. It

+  removes all the blocks after the Last. MTFTP initialize the

+  block range to the maximum possible range, such as [0, 0xffff]

+  for WRQ. When it gets the last block number, it calls

+  this function to set the last block number.

+

+  @param[in]  Head                   The block range list.

+  @param[in]  Last                   The last block number.

+

+**/

+VOID

+Mtftp6SetLastBlockNum (

+  IN LIST_ENTRY             *Head,

+  IN UINT16                 Last

+  );

+

+

+/**

+  Remove the block number from the block range list.

+

+  @param[in]  Head                   The block range list to remove from.

+  @param[in]  Num                    The block number to remove.

+  @param[in]  Completed              Whether Num is the last block number

+  @param[out] TotalBlock             The continuous block number in all

+

+  @retval EFI_NOT_FOUND          The block number isn't in the block range list.

+  @retval EFI_SUCCESS            The block number has been removed from the list.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.

+

+**/

+EFI_STATUS

+Mtftp6RemoveBlockNum (

+  IN LIST_ENTRY             *Head,

+  IN UINT16                 Num,

+  IN BOOLEAN                Completed,

+  OUT UINT64                *TotalBlock

+  );

+

+

+/**

+  Build and transmit the request packet for the Mtftp6 instance.

+

+  @param[in]  Instance               The pointer to the Mtftp6 instance.

+  @param[in]  Operation              The operation code of this packet.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the request.

+  @retval EFI_SUCCESS            The request was built and sent.

+  @retval Others                 Failed to transmit the packet.

+

+**/

+EFI_STATUS

+Mtftp6SendRequest (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 Operation

+  );

+

+

+/**

+  Build and send an error packet.

+

+  @param[in]  Instance               The pointer to the Mtftp6 instance.

+  @param[in]  ErrCode                The error code in the packet.

+  @param[in]  ErrInfo                The error message in the packet.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the error packet.

+  @retval EFI_SUCCESS            The error packet was transmitted.

+  @retval Others                 Failed to transmit the packet.

+

+**/

+EFI_STATUS

+Mtftp6SendError (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 ErrCode,

+  IN UINT8*                 ErrInfo

+  );

+

+

+/**

+  Send the packet for the Mtftp6 instance.

+

+  @param[in]  Instance               The pointer to the Mtftp6 instance.

+  @param[in]  Packet                 The pointer to the packet to be sent.

+

+  @retval EFI_SUCCESS            The packet was sent out

+  @retval Others                 Failed to transmit the packet.

+

+**/

+EFI_STATUS

+Mtftp6TransmitPacket (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN NET_BUF                *Packet

+  );

+

+

+/**

+  Check packet for GetInfo callback routine.

+

+  @param[in]  This                   The pointer to the Mtftp6 protocol.

+  @param[in]  Token                  The pointer to the Mtftp6 token.

+  @param[in]  PacketLen              The length of the packet

+  @param[in]  Packet                 The pointer to the received packet.

+

+  @retval EFI_SUCCESS            The check process passed successfully.

+  @retval EFI_ABORTED            Abort the Mtftp6 operation.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6CheckPacket (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_TOKEN       *Token,

+  IN UINT16                 PacketLen,

+  IN EFI_MTFTP6_PACKET      *Packet

+  );

+

+

+/**

+  The dummy configure routine for create a new Udp6 Io.

+

+  @param[in]  UdpIo                  The pointer to the Udp6 Io.

+  @param[in]  Context                The pointer to the context.

+

+  @retval EFI_SUCCESS                The value is always returned.

+

+**/

+EFI_STATUS

+EFIAPI

+Mtftp6ConfigDummyUdpIo (

+  IN UDP_IO                 *UdpIo,

+  IN VOID                   *Context

+  );

+

+

+/**

+  The configure routine for the Mtftp6 instance to transmit/receive.

+

+  @param[in]  UdpIo                  The pointer to the Udp6 Io.

+  @param[in]  ServerIp               The pointer to the server address.

+  @param[in]  ServerPort             The pointer to the server port.

+  @param[in]  LocalIp                The pointer to the local address.

+  @param[in]  LocalPort              The pointer to the local port.

+

+  @retval EFI_SUCCESS            Configure the Udp6 Io for Mtftp6 successfully.

+  @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not been

+                                 configured yet.

+

+**/

+EFI_STATUS

+Mtftp6ConfigUdpIo (

+  IN UDP_IO                 *UdpIo,

+  IN EFI_IPv6_ADDRESS       *ServerIp,

+  IN UINT16                 ServerPort,

+  IN EFI_IPv6_ADDRESS       *LocalIp,

+  IN UINT16                 LocalPort

+  );

+

+

+/**

+  Clean up the current Mtftp6 operation.

+

+  @param[in]  Instance               The pointer to the Mtftp6 instance.

+  @param[in]  Result                 The result to be returned to the user.

+

+**/

+VOID

+Mtftp6OperationClean (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN EFI_STATUS             Result

+  );

+

+

+/**

+  Start the Mtftp6 instance to perform the operation, such as read file,

+  write file, and read directory.

+

+  @param[in]  This                   The MTFTP session

+  @param[in]  Token                  The token that encapsulates the user's request.

+  @param[in]  OpCode                 The operation to perform.

+

+  @retval EFI_INVALID_PARAMETER  Some of the parameters are invalid.

+  @retval EFI_NOT_STARTED        The MTFTP session hasn't been configured.

+  @retval EFI_ALREADY_STARTED    There is pending operation for the session.

+  @retval EFI_SUCCESS            The operation was successfully started.

+

+**/

+EFI_STATUS

+Mtftp6OperationStart (

+  IN EFI_MTFTP6_PROTOCOL    *This,

+  IN EFI_MTFTP6_TOKEN       *Token,

+  IN UINT16                 OpCode

+  );

+

+

+/**

+  The timer ticking routine for the Mtftp6 instance.

+

+  @param[in]  Event                  The pointer to the ticking event.

+  @param[in]  Context                The pointer to the context.

+

+**/

+VOID

+EFIAPI

+Mtftp6OnTimerTick (

+  IN EFI_EVENT              Event,

+  IN VOID                   *Context

+  );

+

+

+/**

+  The packet process callback for Mtftp6 upload.

+

+  @param[in]  UdpPacket             The pointer to the packet received.

+  @param[in]  UdpEpt                The pointer to the Udp6 access point.

+  @param[in]  IoStatus              The status from the Udp6 instance.

+  @param[in]  Context               The pointer to the context.

+

+**/

+VOID

+EFIAPI

+Mtftp6WrqInput (

+  IN NET_BUF                *UdpPacket,

+  IN UDP_END_POINT          *UdpEpt,

+  IN EFI_STATUS             IoStatus,

+  IN VOID                   *Context

+  );

+

+

+/**

+  Start the Mtftp6 instance to upload. It will first init some states,

+  then send the WRQ request packet, and start to receive the packet.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  Operation             The operation code of current packet.

+

+  @retval EFI_SUCCESS           The Mtftp6 was started to upload.

+  @retval Others                Failed to start to upload.

+

+**/

+EFI_STATUS

+Mtftp6WrqStart (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 Operation

+  );

+

+

+/**

+  The packet process callback for Mtftp6 download.

+

+  @param[in]  UdpPacket             The pointer to the packet received.

+  @param[in]  UdpEpt                The pointer to the Udp6 access point.

+  @param[in]  IoStatus              The status from Udp6 instance.

+  @param[in]  Context               The pointer to the context.

+

+**/

+VOID

+EFIAPI

+Mtftp6RrqInput (

+  IN NET_BUF                *UdpPacket,

+  IN UDP_END_POINT          *UdpEpt,

+  IN EFI_STATUS             IoStatus,

+  IN VOID                   *Context

+  );

+

+

+/**

+  Start the Mtftp6 instance to download. It first initializes some

+  of the internal states then builds and sends an RRQ reqeuest packet.

+  Finally, it starts receive for the downloading.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  Operation             The operation code of current packet.

+

+  @retval EFI_SUCCESS           The Mtftp6 was started to download.

+  @retval Others                Failed to start to download.

+

+**/

+EFI_STATUS

+Mtftp6RrqStart (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 Operation

+  );

+

+#endif

diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
new file mode 100644
index 0000000..69ef4d2
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
@@ -0,0 +1,602 @@
+/** @file

+  Mtftp6 Wrq process functions implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Mtftp6Impl.h"

+

+

+

+/**

+  Build and send a Mtftp6 data packet for upload.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  BlockNum              The block num to be sent.

+

+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the packet.

+  @retval EFI_SUCCESS           The data packet was sent.

+  @retval EFI_ABORTED           The user aborted this process.

+

+**/

+EFI_STATUS

+Mtftp6WrqSendBlock (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 BlockNum

+  )

+{

+  EFI_MTFTP6_PACKET         *Packet;

+  EFI_MTFTP6_TOKEN          *Token;

+  NET_BUF                   *UdpPacket;

+  EFI_STATUS                Status;

+  UINT16                    DataLen;

+  UINT8                     *DataBuf;

+  UINT64                    Start;

+

+  //

+  // Allocate net buffer to create data packet.

+  //

+  UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN);

+

+  if (UdpPacket == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (

+                                   UdpPacket,

+                                   MTFTP6_DATA_HEAD_LEN,

+                                   FALSE

+                                   );

+  ASSERT (Packet != NULL);

+

+  Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA);

+  Packet->Data.Block  = HTONS (BlockNum);

+

+  //

+  // Read the block from either the buffer or PacketNeeded callback

+  //

+  Token   = Instance->Token;

+  DataLen = Instance->BlkSize;

+

+  if (Token->Buffer != NULL) {

+    Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);

+

+    if (Token->BufferSize < Start + Instance->BlkSize) {

+      DataLen           = (UINT16) (Token->BufferSize - Start);

+      Instance->LastBlk = BlockNum;

+      Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);

+    }

+

+    if (DataLen > 0) {

+      NetbufAllocSpace (UdpPacket, DataLen, FALSE);

+      CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);

+    }

+

+  } else {

+    //

+    // Get data from PacketNeeded

+    //

+    DataBuf = NULL;

+    Status  = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf);

+

+    if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {

+      if (DataBuf != NULL) {

+        gBS->FreePool (DataBuf);

+      }

+      //

+      // The received packet has already been freed.

+      //

+      Mtftp6SendError (

+        Instance,

+        EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,

+        (UINT8 *) "User aborted the transfer"

+        );

+

+      return EFI_ABORTED;

+    }

+

+    if (DataLen < Instance->BlkSize) {

+      Instance->LastBlk = BlockNum;

+      Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);

+    }

+

+    if (DataLen > 0) {

+      NetbufAllocSpace (UdpPacket, DataLen, FALSE);

+      CopyMem (Packet->Data.Data, DataBuf, DataLen);

+      gBS->FreePool (DataBuf);

+    }

+  }

+

+  //

+  // Reset current retry count of the instance.

+  //

+  Instance->CurRetry = 0;

+

+  return Mtftp6TransmitPacket (Instance, UdpPacket);

+}

+

+

+/**

+  Function to handle received ACK packet. If the ACK number matches the

+  expected block number, with more data pending, send the next

+  block. Otherwise, tell the caller that we are done.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  Packet                The pointer to the received packet.

+  @param[in]  Len                   The length of the packet.

+  @param[out] UdpPacket             The net buf of received packet.

+  @param[out] IsCompleted           If TRUE, the upload has been completed.

+                                    Otherwise, the upload has not been completed.

+

+  @retval EFI_SUCCESS           The ACK packet successfully processed.

+  @retval EFI_TFTP_ERROR        The block number loops back.

+  @retval Others                Failed to transmit the next data packet.

+

+**/

+EFI_STATUS

+Mtftp6WrqHandleAck (

+  IN  MTFTP6_INSTANCE       *Instance,

+  IN  EFI_MTFTP6_PACKET     *Packet,

+  IN  UINT32                Len,

+  OUT NET_BUF               **UdpPacket,

+  OUT BOOLEAN               *IsCompleted

+  )

+{

+  UINT16                    AckNum;

+  INTN                      Expected;

+  UINT64                    TotalBlock;

+

+  *IsCompleted = FALSE;

+  AckNum       = NTOHS (Packet->Ack.Block[0]);

+  Expected     = Mtftp6GetNextBlockNum (&Instance->BlkList);

+

+  ASSERT (Expected >= 0);

+

+  //

+  // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput

+  // restart receive.

+  //

+  if (Expected != AckNum) {

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Remove the acked block number, if this is the last block number,

+  // tell the Mtftp6WrqInput to finish the transfer. This is the last

+  // block number if the block range are empty..

+  //

+  Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &TotalBlock);

+

+  Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);

+

+  if (Expected < 0) {

+    //

+    // The block range is empty. It may either because the the last

+    // block has been ACKed, or the sequence number just looped back,

+    // that is, there is more than 0xffff blocks.

+    //

+    if (Instance->LastBlk == AckNum) {

+      ASSERT (Instance->LastBlk >= 1);

+      *IsCompleted = TRUE;

+      return EFI_SUCCESS;

+

+    } else {

+      //

+      // Free the received packet before send new packet in ReceiveNotify,

+      // since the udpio might need to be reconfigured.

+      //

+      NetbufFree (*UdpPacket);

+      *UdpPacket = NULL;

+      //

+      // Send the Mtftp6 error message if block number rolls back.

+      //

+      Mtftp6SendError (

+        Instance,

+        EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,

+        (UINT8 *) "Block number rolls back, not supported, try blksize option"

+        );

+

+      return EFI_TFTP_ERROR;

+    }

+  }

+

+  //

+  // Free the receive buffer before send new packet since it might need

+  // reconfigure udpio.

+  //

+  NetbufFree (*UdpPacket);

+  *UdpPacket = NULL;

+

+  return Mtftp6WrqSendBlock (Instance, (UINT16) Expected);

+}

+

+

+/**

+  Check whether the received OACK is valid. The OACK is valid

+  only if:

+  1. It only include options requested by us.

+  2. It can only include a smaller block size.

+  3. It can't change the proposed time out value.

+  4. Other requirements of the individal MTFTP6 options as required.

+

+  @param[in]  ReplyInfo             The pointer to options information in reply packet.

+  @param[in]  RequestInfo           The pointer to requested options information.

+

+  @retval     TRUE                  If the option in OACK is valid.

+  @retval     FALSE                 If the option is invalid.

+

+**/

+BOOLEAN

+Mtftp6WrqOackValid (

+  IN MTFTP6_EXT_OPTION_INFO     *ReplyInfo,

+  IN MTFTP6_EXT_OPTION_INFO     *RequestInfo

+  )

+{

+  //

+  // It is invalid for server to return options we don't request

+  //

+  if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {

+    return FALSE;

+  }

+

+  //

+  // Server can only specify a smaller block size to be used and

+  // return the timeout matches that requested.

+  //

+  if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||

+      (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))

+      ) {

+

+    return FALSE;

+  }

+

+  return TRUE;

+}

+

+

+/**

+  Process the OACK packet for Wrq.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  Packet                The pointer to the received packet.

+  @param[in]  Len                   The length of the packet.

+  @param[out] UdpPacket             The net buf of received packet.

+  @param[out] IsCompleted           If TRUE, the upload has been completed.

+                                    Otherwise, the upload has not been completed.

+

+  @retval EFI_SUCCESS           The OACK packet successfully processed.

+  @retval EFI_TFTP_ERROR        An TFTP communication error happened.

+  @retval Others                Failed to process the OACK packet.

+

+**/

+EFI_STATUS

+Mtftp6WrqHandleOack (

+  IN  MTFTP6_INSTANCE       *Instance,

+  IN  EFI_MTFTP6_PACKET     *Packet,

+  IN  UINT32                Len,

+  OUT NET_BUF               **UdpPacket,

+  OUT BOOLEAN               *IsCompleted

+  )

+{

+  EFI_MTFTP6_OPTION         *Options;

+  UINT32                    Count;

+  MTFTP6_EXT_OPTION_INFO    ExtInfo;

+  EFI_MTFTP6_PACKET         Dummy;

+  EFI_STATUS                Status;

+  INTN                      Expected;

+

+  *IsCompleted = FALSE;

+

+  //

+  // Ignore the OACK if already started the upload

+  //

+  Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);

+

+  if (Expected != 0) {

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Parse and validate the options from server

+  //

+  ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));

+

+  Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);

+

+  if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) {

+    //

+    // Don't send a MTFTP error packet when out of resource, it can

+    // only make it worse.

+    //

+    if (Status != EFI_OUT_OF_RESOURCES) {

+      //

+      // Free the received packet before send new packet in ReceiveNotify,

+      // since the udpio might need to be reconfigured.

+      //

+      NetbufFree (*UdpPacket);

+      *UdpPacket = NULL;

+      //

+      // Send the Mtftp6 error message if invalid Oack packet received.

+      //

+      Mtftp6SendError (

+        Instance,

+        EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,

+        (UINT8 *) "Mal-formated OACK packet"

+        );

+    }

+

+    return EFI_TFTP_ERROR;

+  }

+

+  if (ExtInfo.BlkSize != 0) {

+    Instance->BlkSize = ExtInfo.BlkSize;

+  }

+

+  if (ExtInfo.Timeout != 0) {

+    Instance->Timeout = ExtInfo.Timeout;

+  }

+

+  //

+  // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck,

+  // which will start the transmission of the first data block.

+  //

+  Dummy.Ack.OpCode   = HTONS (EFI_MTFTP6_OPCODE_ACK);

+  Dummy.Ack.Block[0] = 0;

+

+  return Mtftp6WrqHandleAck (

+           Instance,

+           &Dummy,

+           sizeof (EFI_MTFTP6_ACK_HEADER),

+           UdpPacket,

+           IsCompleted

+           );

+}

+

+

+/**

+  The packet process callback for Mtftp6 upload.

+

+  @param[in]  UdpPacket             The pointer to the packet received.

+  @param[in]  UdpEpt                The pointer to the Udp6 access point.

+  @param[in]  IoStatus              The status from Udp6 instance.

+  @param[in]  Context               The pointer to the context.

+

+**/

+VOID

+EFIAPI

+Mtftp6WrqInput (

+  IN NET_BUF                *UdpPacket,

+  IN UDP_END_POINT          *UdpEpt,

+  IN EFI_STATUS             IoStatus,

+  IN VOID                   *Context

+  )

+{

+  MTFTP6_INSTANCE           *Instance;

+  EFI_MTFTP6_PACKET         *Packet;

+  BOOLEAN                   IsCompleted;

+  EFI_STATUS                Status;

+  UINT32                    TotalNum;

+  UINT32                    Len;

+  UINT16                    Opcode;

+

+  Instance = (MTFTP6_INSTANCE *) Context;

+

+  NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);

+

+  IsCompleted = FALSE;

+  Packet      = NULL;

+  Status      = EFI_SUCCESS;

+  TotalNum    = 0;

+

+  //

+  // Return error status if Udp6 instance failed to receive.

+  //

+  if (EFI_ERROR (IoStatus)) {

+    Status = IoStatus;

+    goto ON_EXIT;

+  }

+

+  ASSERT (UdpPacket != NULL);

+

+  if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Client send initial request to server's listening port. Server

+  // will select a UDP port to communicate with the client.

+  //

+  if (UdpEpt->RemotePort != Instance->ServerDataPort) {

+    if (Instance->ServerDataPort != 0) {

+      goto ON_EXIT;

+    } else {

+      Instance->ServerDataPort = UdpEpt->RemotePort;

+    }

+  }

+

+  //

+  // Copy the MTFTP packet to a continuous buffer if it isn't already so.

+  //

+  Len      = UdpPacket->TotalSize;

+  TotalNum = UdpPacket->BlockOpNum;

+

+  if (TotalNum > 1) {

+    Packet = AllocateZeroPool (Len);

+

+    if (Packet == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_EXIT;

+    }

+

+    NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);

+

+  } else {

+    Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);

+    ASSERT (Packet != NULL);

+  }

+

+  Opcode = NTOHS (Packet->OpCode);

+

+  //

+  // Callback to the user's CheckPacket if provided. Abort the transmission

+  // if CheckPacket returns an EFI_ERROR code.

+  //

+  if (Instance->Token->CheckPacket != NULL &&

+      (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)

+      ) {

+

+    Status = Instance->Token->CheckPacket (

+                                &Instance->Mtftp6,

+                                Instance->Token,

+                                (UINT16) Len,

+                                Packet

+                                );

+

+    if (EFI_ERROR (Status)) {

+      //

+      // Send an error message to the server to inform it

+      //

+      if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {

+        //

+        // Free the received packet before send new packet in ReceiveNotify,

+        // since the udpio might need to be reconfigured.

+        //

+        NetbufFree (UdpPacket);

+        UdpPacket = NULL;

+        //

+        // Send the Mtftp6 error message if user aborted the current session.

+        //

+        Mtftp6SendError (

+          Instance,

+          EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,

+          (UINT8 *) "User aborted the transfer"

+          );

+      }

+

+      Status = EFI_ABORTED;

+      goto ON_EXIT;

+    }

+  }

+

+  //

+  // Switch the process routines by the operation code.

+  //

+  switch (Opcode) {

+  case EFI_MTFTP6_OPCODE_ACK:

+    if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) {

+      goto ON_EXIT;

+    }

+    //

+    // Handle the Ack packet of Wrq.

+    //

+    Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted);

+    break;

+

+  case EFI_MTFTP6_OPCODE_OACK:

+    if (Len <= MTFTP6_OPCODE_LEN) {

+      goto ON_EXIT;

+    }

+    //

+    // Handle the Oack packet of Wrq.

+    //

+    Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted);

+    break;

+

+  default:

+    //

+    // Drop and return eror if received error message.

+    //

+    Status = EFI_TFTP_ERROR;

+    break;

+  }

+

+ON_EXIT:

+  //

+  // Free the resources, then if !EFI_ERROR (Status) and not completed,

+  // restart the receive, otherwise end the session.

+  //

+  if (Packet != NULL && TotalNum > 1) {

+    FreePool (Packet);

+  }

+

+  if (UdpPacket != NULL) {

+    NetbufFree (UdpPacket);

+  }

+

+  if (!EFI_ERROR (Status) && !IsCompleted) {

+    Status = UdpIoRecvDatagram (

+               Instance->UdpIo,

+               Mtftp6WrqInput,

+               Instance,

+               0

+               );

+  }

+  //

+  // Clean up the current session if failed to continue.

+  //

+  if (EFI_ERROR (Status) || IsCompleted) {

+    Mtftp6OperationClean (Instance, Status);

+  }

+}

+

+

+/**

+  Start the Mtftp6 instance to upload. It will first init some states,

+  then send the WRQ request packet, and start to receive the packet.

+

+  @param[in]  Instance              The pointer to the Mtftp6 instance.

+  @param[in]  Operation             The operation code of the current packet.

+

+  @retval EFI_SUCCESS           The Mtftp6 was started to upload.

+  @retval Others                Failed to start to upload.

+

+**/

+EFI_STATUS

+Mtftp6WrqStart (

+  IN MTFTP6_INSTANCE        *Instance,

+  IN UINT16                 Operation

+  )

+{

+  EFI_STATUS                Status;

+

+  //

+  // The valid block number range are [0, 0xffff]. For example:

+  // the client sends an WRQ request to the server, the server

+  // ACK with an ACK0 to let client start transfer the first

+  // packet.

+  //

+  Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = Mtftp6SendRequest (Instance, Operation);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  return UdpIoRecvDatagram (

+           Instance->UdpIo,

+           Mtftp6WrqInput,

+           Instance,

+           0

+           );

+}

+

diff --git a/NetworkPkg/NetworkPkg.dec b/NetworkPkg/NetworkPkg.dec
new file mode 100644
index 0000000..0c8df4e
--- /dev/null
+++ b/NetworkPkg/NetworkPkg.dec
@@ -0,0 +1,21 @@
+## @file

+#

+# This package provides network modules that conform to UEFI 2.2 specification.

+#

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

+#

+# This program and the accompanying materials are licensed and made available under

+# the terms and conditions of the BSD License which accompanies this distribution.

+# The full text of the license may be found at

+# http://opensource.org/licenses/bsd-license.php

+#

+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  DEC_SPECIFICATION              = 0x00010005

+  PACKAGE_NAME                   = NetworkPkg

+  PACKAGE_GUID                   = 947988BE-8D5C-471a-893D-AD181C46BEBB

+  PACKAGE_VERSION                = 0.92

diff --git a/NetworkPkg/NetworkPkg.dsc b/NetworkPkg/NetworkPkg.dsc
new file mode 100644
index 0000000..b12d5fd
--- /dev/null
+++ b/NetworkPkg/NetworkPkg.dsc
@@ -0,0 +1,95 @@
+## @file

+# UEFI 2.2 Network Module Package for All Architectures

+#

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

+#

+#    This program and the accompanying materials

+#    are licensed and made available under the terms and conditions of the BSD License

+#    which accompanies this distribution. The full text of the license may be found at

+#    http://opensource.org/licenses/bsd-license.php

+#

+#    THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#    WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  PLATFORM_NAME                  = NetworkPkg

+  PLATFORM_GUID                  = 3FD34E9B-E90C-44e1-B510-1F632A509F10

+  PLATFORM_VERSION               = 0.92

+  DSC_SPECIFICATION              = 0x00010005

+  OUTPUT_DIRECTORY               = Build/NetworkPkg

+  SUPPORTED_ARCHITECTURES        = IA32|IPF|X64|EBC|ARM

+  BUILD_TARGETS                  = DEBUG|RELEASE

+  SKUID_IDENTIFIER               = DEFAULT

+

+[LibraryClasses]

+  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf

+  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf

+  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf

+  HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf

+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf

+  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf

+  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf

+  UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf

+  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf

+  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf

+  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf

+  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf

+  UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf

+

+  DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf

+  NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf

+  IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf

+  UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf

+

+[LibraryClasses.common.UEFI_DRIVER]

+  DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf

+

+[LibraryClasses.common.UEFI_APPLICATION]

+  DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf

+  FileHandleLib|ShellPkg/Library/BaseFileHandleLib/BaseFileHandleLib.inf

+  ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf

+

+[PcdsFeatureFlag]

+  gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable|TRUE

+  gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable|TRUE

+

+[PcdsFixedAtBuild]

+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2f

+  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000

+

+###################################################################################################

+#

+# Components Section - list of the modules and components that will be processed by compilation

+#                      tools and the EDK II tools to generate PE32/PE32+/Coff image files.

+#

+# Note: The EDK II DSC file is not used to specify how compiled binary images get placed

+#       into firmware volume images. This section is just a list of modules to compile from

+#       source into UEFI-compliant binaries.

+#       It is the FDF file that contains information on combining binary files into firmware

+#       volume images, whose concept is beyond UEFI and is described in PI specification.

+#       Binary modules do not need to be listed in this section, as they should be

+#       specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi),

+#       Logo (Logo.bmp), and etc.

+#       There may also be modules listed in this section that are not required in the FDF file,

+#       When a module listed here is excluded from FDF file, then UEFI-compliant binary will be

+#       generated for it, but the binary will not be put into any firmware volume.

+#

+###################################################################################################

+

+[Components]

+  NetworkPkg/IpSecDxe/IpSecDxe.inf

+  NetworkPkg/Ip6Dxe/Ip6Dxe.inf

+  NetworkPkg/TcpDxe/TcpDxe.inf

+  NetworkPkg/Udp6Dxe/Udp6Dxe.inf

+  NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf

+  NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf

+

+

+[Components.IA32, Components.X64, Components.IPF]

+  NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf

+  NetworkPkg/Application/Ping6/Ping6.inf

+  NetworkPkg/Application/IfConfig6/IfConfig6.inf

+  NetworkPkg/Application/IpsecConfig/IpSecConfig.inf

+  NetworkPkg/Application/VConfig/VConfig.inf

diff --git a/NetworkPkg/TcpDxe/ComponentName.c b/NetworkPkg/TcpDxe/ComponentName.c
new file mode 100644
index 0000000..956792a
--- /dev/null
+++ b/NetworkPkg/TcpDxe/ComponentName.c
@@ -0,0 +1,304 @@
+/** @file

+  Implementation of protocols EFI_COMPONENT_NAME_PROTOCOL and

+  EFI_COMPONENT_NAME2_PROTOCOL.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+//

+// EFI Component Name Functions

+//

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This, and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language or DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language, from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  EFI_HANDLE                   ChildHandle  OPTIONAL,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **ControllerName

+  );

+

+///

+/// EFI Component Name Protocol

+///

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL     gTcpComponentName = {

+  TcpComponentNameGetDriverName,

+  TcpComponentNameGetControllerName,

+  "eng"

+};

+

+///

+/// EFI Component Name 2 Protocol

+///

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL    gTcpComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) TcpComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) TcpComponentNameGetControllerName,

+  "en"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE        mTcpDriverNameTable[] = {

+  {

+    "eng;en",

+    L"TCP Network Service Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This, and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language or DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+           Language,

+           This->SupportedLanguages,

+           mTcpDriverNameTable,

+           DriverName,

+           (BOOLEAN) (This == &gTcpComponentName)

+           );

+}

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language, from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  EFI_HANDLE                   ChildHandle  OPTIONAL,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

diff --git a/NetworkPkg/TcpDxe/SockImpl.c b/NetworkPkg/TcpDxe/SockImpl.c
new file mode 100644
index 0000000..7fad042
--- /dev/null
+++ b/NetworkPkg/TcpDxe/SockImpl.c
@@ -0,0 +1,1230 @@
+/** @file

+  Implementation of the Socket.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "SockImpl.h"

+

+/**

+  Get the first buffer block in the specific socket buffer.

+

+  @param[in]  Sockbuf               Pointer to the socket buffer.

+

+  @return Pointer to the first buffer in the queue. NULL if the queue is empty.

+

+**/

+NET_BUF *

+SockBufFirst (

+  IN SOCK_BUFFER *Sockbuf

+  )

+{

+  LIST_ENTRY  *NetbufList;

+

+  NetbufList = &(Sockbuf->DataQueue->BufList);

+

+  if (IsListEmpty (NetbufList)) {

+    return NULL;

+  }

+

+  return NET_LIST_HEAD (NetbufList, NET_BUF, List);

+}

+

+/**

+  Get the next buffer block in the specific socket buffer.

+

+  @param[in]  Sockbuf     Pointer to the socket buffer.

+  @param[in]  SockEntry   Pointer to the buffer block prior to the required one.

+

+  @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is

+          the tail or head entry.

+

+**/

+NET_BUF *

+SockBufNext (

+  IN SOCK_BUFFER *Sockbuf,

+  IN NET_BUF     *SockEntry

+  )

+{

+  LIST_ENTRY  *NetbufList;

+

+  NetbufList = &(Sockbuf->DataQueue->BufList);

+

+  if ((SockEntry->List.ForwardLink == NetbufList) ||

+      (SockEntry->List.BackLink == &SockEntry->List) ||

+      (SockEntry->List.ForwardLink == &SockEntry->List)

+      ) {

+

+    return NULL;

+  }

+

+  return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List);

+}

+

+/**

+  User provided callback function for NetbufFromExt.

+

+  @param[in] Event    The Event this notify function registered to, ignored.

+

+**/

+VOID

+EFIAPI

+SockFreeFoo (

+  IN EFI_EVENT Event

+  )

+{

+  return;

+}

+

+/**

+  Get the length of the data that can be retrieved from the socket

+  receive buffer.

+

+  @param[in]  SockBuffer            Pointer to the socket receive buffer.

+  @param[out] IsUrg                 Pointer to a BOOLEAN variable.

+                                    If TRUE the data is OOB.

+  @param[in]  BufLen                The maximum length of the data buffer to

+                                    store the received data in the socket layer.

+

+  @return The length of the data can be retreived.

+

+**/

+UINT32

+SockTcpDataToRcv (

+  IN  SOCK_BUFFER   *SockBuffer,

+  OUT BOOLEAN       *IsUrg,

+  IN  UINT32        BufLen

+  )

+{

+  NET_BUF       *RcvBufEntry;

+  UINT32        DataLen;

+  TCP_RSV_DATA  *TcpRsvData;

+  BOOLEAN       Urg;

+

+  ASSERT ((SockBuffer != NULL) && (IsUrg != NULL) && (BufLen > 0));

+

+  //

+  // Get the first socket receive buffer

+  //

+  RcvBufEntry = SockBufFirst (SockBuffer);

+  ASSERT (RcvBufEntry != NULL);

+

+  TcpRsvData  = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;

+

+  //

+  // Check whether the receive data is out of bound. If yes, calculate the maximum

+  // allowed length of the urgent data and output it.

+  //

+  *IsUrg      = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);

+

+  if (*IsUrg && (TcpRsvData->UrgLen < RcvBufEntry->TotalSize)) {

+

+    DataLen = MIN (TcpRsvData->UrgLen, BufLen);

+

+    if (DataLen < TcpRsvData->UrgLen) {

+      TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen;

+    } else {

+      TcpRsvData->UrgLen = 0;

+    }

+

+    return DataLen;

+

+  }

+

+  //

+  // Process the next socket receive buffer to get the maximum allowed length

+  // of the received data.

+  //

+  DataLen     = RcvBufEntry->TotalSize;

+

+  RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);

+

+  while ((BufLen > DataLen) && (RcvBufEntry != NULL)) {

+

+    TcpRsvData  = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;

+

+    Urg         = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);

+

+    if (*IsUrg != Urg) {

+      break;

+    }

+

+    if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) {

+

+      if (TcpRsvData->UrgLen + DataLen < BufLen) {

+        TcpRsvData->UrgLen = 0;

+      } else {

+        TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen);

+      }

+

+      return MIN (TcpRsvData->UrgLen + DataLen, BufLen);

+

+    }

+

+    DataLen += RcvBufEntry->TotalSize;

+

+    RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);

+  }

+

+  DataLen = MIN (BufLen, DataLen);

+  return DataLen;

+}

+

+/**

+  Copy data from socket buffer to an application provided receive buffer.

+

+  @param[in]  Sock        Pointer to the socket.

+  @param[in]  TcpRxData   Pointer to the application provided receive buffer.

+  @param[in]  RcvdBytes   The maximum length of the data can be copied.

+  @param[in]  IsUrg       If TRUE the data is Out of Bound, FALSE the data is normal.

+

+**/

+VOID

+SockSetTcpRxData (

+  IN SOCKET     *Sock,

+  IN VOID       *TcpRxData,

+  IN UINT32     RcvdBytes,

+  IN BOOLEAN    IsUrg

+  )

+{

+  UINT32                  Index;

+  UINT32                  CopyBytes;

+  UINT32                  OffSet;

+  EFI_TCP4_RECEIVE_DATA   *RxData;

+  EFI_TCP4_FRAGMENT_DATA  *Fragment;

+

+  RxData  = (EFI_TCP4_RECEIVE_DATA *) TcpRxData;

+

+  OffSet  = 0;

+

+  ASSERT (RxData->DataLength >= RcvdBytes);

+

+  RxData->DataLength  = RcvdBytes;

+  RxData->UrgentFlag  = IsUrg;

+

+  //

+  // Copy the CopyBytes data from socket receive buffer to RxData.

+  //

+  for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) {

+

+    Fragment  = &RxData->FragmentTable[Index];

+    CopyBytes = MIN ((UINT32) (Fragment->FragmentLength), RcvdBytes);

+

+    NetbufQueCopy (

+      Sock->RcvBuffer.DataQueue,

+      OffSet,

+      CopyBytes,

+      Fragment->FragmentBuffer

+      );

+

+    Fragment->FragmentLength = CopyBytes;

+    RcvdBytes -= CopyBytes;

+    OffSet += CopyBytes;

+  }

+}

+

+/**

+  Process the send token.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockProcessSndToken (

+  IN OUT SOCKET *Sock

+  )

+{

+  UINT32                  FreeSpace;

+  SOCK_TOKEN              *SockToken;

+  UINT32                  DataLen;

+  SOCK_IO_TOKEN           *SndToken;

+  EFI_TCP4_TRANSMIT_DATA  *TxData;

+  EFI_STATUS              Status;

+

+  ASSERT ((Sock != NULL) && (SockStream == Sock->Type));

+

+  FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);

+

+  //

+  // to determine if process a send token using

+  // socket layer flow control policy

+  //

+  while ((FreeSpace >= Sock->SndBuffer.LowWater) && !IsListEmpty (&Sock->SndTokenList)) {

+

+    SockToken = NET_LIST_HEAD (

+                  &(Sock->SndTokenList),

+                  SOCK_TOKEN,

+                  TokenList

+                  );

+

+    //

+    // process this token

+    //

+    RemoveEntryList (&(SockToken->TokenList));

+    InsertTailList (

+      &(Sock->ProcessingSndTokenList),

+      &(SockToken->TokenList)

+      );

+

+    //

+    // Proceess it in the light of SockType

+    //

+    SndToken  = (SOCK_IO_TOKEN *) SockToken->Token;

+    TxData    = SndToken->Packet.TxData;

+

+    DataLen   = TxData->DataLength;

+    Status    = SockProcessTcpSndData (Sock, TxData);

+

+    if (EFI_ERROR (Status)) {

+      goto OnError;

+    }

+

+    if (DataLen >= FreeSpace) {

+      FreeSpace = 0;

+

+    } else {

+      FreeSpace -= DataLen;

+

+    }

+  }

+

+  return;

+

+OnError:

+

+  RemoveEntryList (&SockToken->TokenList);

+  SIGNAL_TOKEN (SockToken->Token, Status);

+  FreePool (SockToken);

+}

+

+/**

+  Get received data from the socket layer to the receive token.

+

+  @param[in, out]  Sock       Pointer to the socket.

+  @param[in, out]  RcvToken   Pointer to the application provided receive token.

+

+  @return The length of data received in this token.

+

+**/

+UINT32

+SockProcessRcvToken (

+  IN OUT SOCKET        *Sock,

+  IN OUT SOCK_IO_TOKEN *RcvToken

+  )

+{

+  UINT32                TokenRcvdBytes;

+  EFI_TCP4_RECEIVE_DATA *RxData;

+  BOOLEAN               IsUrg;

+

+  ASSERT (Sock != NULL);

+

+  ASSERT (SockStream == Sock->Type);

+

+  RxData = RcvToken->Packet.RxData;

+

+  TokenRcvdBytes = SockTcpDataToRcv (

+                     &Sock->RcvBuffer,

+                     &IsUrg,

+                     RxData->DataLength

+                     );

+

+  //

+  // Copy data from RcvBuffer of socket to user

+  // provided RxData and set the fields in TCP RxData

+  //

+  SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg);

+

+  NetbufQueTrim (Sock->RcvBuffer.DataQueue, TokenRcvdBytes);

+  SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS);

+

+  return TokenRcvdBytes;

+}

+

+/**

+  Process the TCP send data, buffer the tcp txdata, and append

+  the buffer to socket send buffer, then try to send it.

+

+  @param[in]  Sock              Pointer to the socket.

+  @param[in]  TcpTxData         Pointer to the application provided send buffer.

+

+  @retval EFI_SUCCESS           The operation completed successfully.

+  @retval EFI_OUT_OF_RESOURCES  Failed due to resource limits.

+

+**/

+EFI_STATUS

+SockProcessTcpSndData (

+  IN SOCKET   *Sock,

+  IN VOID     *TcpTxData

+  )

+{

+  NET_BUF                 *SndData;

+  EFI_STATUS              Status;

+  EFI_TCP4_TRANSMIT_DATA  *TxData;

+

+  TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData;

+

+  //

+  // transform this TxData into a NET_BUFFER

+  // and insert it into Sock->SndBuffer

+  //

+  SndData = NetbufFromExt (

+              (NET_FRAGMENT *) TxData->FragmentTable,

+              TxData->FragmentCount,

+              0,

+              0,

+              SockFreeFoo,

+              NULL

+              );

+

+  if (NULL == SndData) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockKProcessSndData: Failed to call NetBufferFromExt\n")

+      );

+

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData);

+

+  //

+  // notify the low layer protocol to handle this send token

+  //

+  if (TxData->Urgent) {

+    Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL);

+

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+  }

+

+  if (TxData->Push) {

+    Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL);

+

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+  }

+

+  //

+  // low layer protocol should really handle the sending

+  // process when catching SOCK_SND request

+  //

+  Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL);

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Flush the tokens in the specific token list.

+

+  @param[in]       Sock                  Pointer to the socket.

+  @param[in, out]  PendingTokenList      Pointer to the token list to be flushed.

+

+**/

+VOID

+SockFlushPendingToken (

+  IN     SOCKET         *Sock,

+  IN OUT LIST_ENTRY     *PendingTokenList

+  )

+{

+  SOCK_TOKEN            *SockToken;

+  SOCK_COMPLETION_TOKEN *Token;

+

+  ASSERT ((Sock != NULL) && (PendingTokenList != NULL));

+

+  while (!IsListEmpty (PendingTokenList)) {

+    SockToken = NET_LIST_HEAD (

+                  PendingTokenList,

+                  SOCK_TOKEN,

+                  TokenList

+                  );

+

+    Token = SockToken->Token;

+    SIGNAL_TOKEN (Token, Sock->SockError);

+

+    RemoveEntryList (&(SockToken->TokenList));

+    FreePool (SockToken);

+  }

+}

+

+/**

+  Wake up the connection token while the connection is successfully established,

+  then try to process any pending send token.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockWakeConnToken (

+  IN OUT SOCKET *Sock

+  )

+{

+  ASSERT (Sock->ConnectionToken != NULL);

+

+  SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS);

+  Sock->ConnectionToken = NULL;

+

+  //

+  // check to see if some pending send token existed?

+  //

+  SockProcessSndToken (Sock);

+}

+

+/**

+  Wake up the listen token while the connection is established successfully.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockWakeListenToken (

+  IN OUT SOCKET *Sock

+  )

+{

+  SOCKET                *Parent;

+  SOCK_TOKEN            *SockToken;

+  EFI_TCP4_LISTEN_TOKEN *ListenToken;

+

+  Parent = Sock->Parent;

+

+  ASSERT ((Parent != NULL) && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock));

+

+  if (!IsListEmpty (&Parent->ListenTokenList)) {

+    SockToken = NET_LIST_HEAD (

+                  &Parent->ListenTokenList,

+                  SOCK_TOKEN,

+                  TokenList

+                  );

+

+    ListenToken                 = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token;

+    ListenToken->NewChildHandle = Sock->SockHandle;

+

+    SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);

+

+    RemoveEntryList (&SockToken->TokenList);

+    FreePool (SockToken);

+

+    RemoveEntryList (&Sock->ConnectionList);

+

+    Parent->ConnCnt--;

+    DEBUG (

+      (EFI_D_INFO,

+      "SockWakeListenToken: accept a socket, now conncnt is %d",

+      Parent->ConnCnt)

+      );

+

+    Sock->Parent = NULL;

+  }

+}

+

+/**

+  Wake up the receive token while some data is received.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockWakeRcvToken (

+  IN OUT SOCKET *Sock

+  )

+{

+  UINT32        RcvdBytes;

+  UINT32        TokenRcvdBytes;

+  SOCK_TOKEN    *SockToken;

+  SOCK_IO_TOKEN *RcvToken;

+

+  ASSERT (Sock->RcvBuffer.DataQueue != NULL);

+

+  RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize;

+

+  ASSERT (RcvdBytes > 0);

+

+  while (RcvdBytes > 0 && !IsListEmpty (&Sock->RcvTokenList)) {

+

+    SockToken = NET_LIST_HEAD (

+                  &Sock->RcvTokenList,

+                  SOCK_TOKEN,

+                  TokenList

+                  );

+

+    RcvToken        = (SOCK_IO_TOKEN *) SockToken->Token;

+    TokenRcvdBytes  = SockProcessRcvToken (Sock, RcvToken);

+

+    if (0 == TokenRcvdBytes) {

+      return ;

+    }

+

+    RemoveEntryList (&(SockToken->TokenList));

+    FreePool (SockToken);

+    RcvdBytes -= TokenRcvdBytes;

+  }

+}

+

+/**

+  Create a socket with initial data SockInitData.

+

+  @param[in]  SockInitData          Pointer to the initial data of the socket.

+

+  @return Pointer to the newly created socket, return NULL when an exception occurs.

+

+**/

+SOCKET *

+SockCreate (

+  IN SOCK_INIT_DATA *SockInitData

+  )

+{

+  SOCKET      *Sock;

+  SOCKET      *Parent;

+  EFI_STATUS  Status;

+  EFI_GUID    *TcpProtocolGuid;

+  UINTN       ProtocolLength;

+

+  ASSERT ((SockInitData != NULL) && (SockInitData->ProtoHandler != NULL));

+  ASSERT (SockInitData->Type == SockStream);

+  ASSERT ((SockInitData->ProtoData != NULL) && (SockInitData->DataSize <= PROTO_RESERVED_LEN));

+

+  if (SockInitData->IpVersion == IP_VERSION_4) {

+    TcpProtocolGuid = &gEfiTcp4ProtocolGuid;

+    ProtocolLength  = sizeof (EFI_TCP4_PROTOCOL);

+  } else {

+    TcpProtocolGuid = &gEfiTcp6ProtocolGuid;

+    ProtocolLength  = sizeof (EFI_TCP6_PROTOCOL);

+  }

+

+

+  Parent = SockInitData->Parent;

+

+  if ((Parent != NULL) && (Parent->ConnCnt == Parent->BackLog)) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockCreate: Socket parent has reached its connection limit with %d ConnCnt and %d BackLog\n",

+      Parent->ConnCnt,

+      Parent->BackLog)

+      );

+

+    return NULL;

+  }

+

+  Sock = AllocateZeroPool (sizeof (SOCKET));

+  if (NULL == Sock) {

+

+    DEBUG ((EFI_D_ERROR, "SockCreate: No resource to create a new socket\n"));

+    return NULL;

+  }

+

+  InitializeListHead (&Sock->Link);

+  InitializeListHead (&Sock->ConnectionList);

+  InitializeListHead (&Sock->ListenTokenList);

+  InitializeListHead (&Sock->RcvTokenList);

+  InitializeListHead (&Sock->SndTokenList);

+  InitializeListHead (&Sock->ProcessingSndTokenList);

+

+  EfiInitializeLock (&(Sock->Lock), TPL_CALLBACK);

+

+  Sock->SndBuffer.DataQueue = NetbufQueAlloc ();

+  if (NULL == Sock->SndBuffer.DataQueue) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockCreate: No resource to allocate SndBuffer for new socket\n")

+      );

+

+    goto OnError;

+  }

+

+  Sock->RcvBuffer.DataQueue = NetbufQueAlloc ();

+  if (NULL == Sock->RcvBuffer.DataQueue) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockCreate: No resource to allocate RcvBuffer for new socket\n")

+      );

+

+    goto OnError;

+  }

+

+  Sock->Signature           = SOCK_SIGNATURE;

+

+  Sock->Parent              = Parent;

+  Sock->BackLog             = SockInitData->BackLog;

+  Sock->ProtoHandler        = SockInitData->ProtoHandler;

+  Sock->SndBuffer.HighWater = SockInitData->SndBufferSize;

+  Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize;

+  Sock->Type                = SockInitData->Type;

+  Sock->DriverBinding       = SockInitData->DriverBinding;

+  Sock->State               = SockInitData->State;

+  Sock->CreateCallback      = SockInitData->CreateCallback;

+  Sock->DestroyCallback     = SockInitData->DestroyCallback;

+  Sock->Context             = SockInitData->Context;

+

+  Sock->SockError           = EFI_ABORTED;

+  Sock->SndBuffer.LowWater  = SOCK_BUFF_LOW_WATER;

+  Sock->RcvBuffer.LowWater  = SOCK_BUFF_LOW_WATER;

+

+  Sock->IpVersion           = SockInitData->IpVersion;

+

+  //

+  // Install protocol on Sock->SockHandle

+  //

+  CopyMem (&Sock->NetProtocol, SockInitData->Protocol, ProtocolLength);

+

+  //

+  // copy the protodata into socket

+  //

+  CopyMem (Sock->ProtoReserved, SockInitData->ProtoData, SockInitData->DataSize);

+

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &Sock->SockHandle,

+                  TcpProtocolGuid,

+                  &Sock->NetProtocol,

+                  NULL

+                  );

+

+  if (EFI_ERROR (Status)) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockCreate: Install TCP protocol in socket failed with %r\n",

+      Status)

+      );

+

+    goto OnError;

+  }

+

+  if (Parent != NULL) {

+    ASSERT (Parent->BackLog > 0);

+    ASSERT (SOCK_IS_LISTENING (Parent));

+

+    //

+    // need to add it into Parent->ConnectionList

+    // if the Parent->ConnCnt < Parent->BackLog

+    //

+    Parent->ConnCnt++;

+

+    DEBUG (

+      (EFI_D_INFO,

+      "SockCreate: Create a new socket and add to parent, now conncnt is %d\n",

+      Parent->ConnCnt)

+      );

+

+    InsertTailList (&Parent->ConnectionList, &Sock->ConnectionList);

+  }

+

+  if (Sock->CreateCallback != NULL) {

+    Status = Sock->CreateCallback (Sock, Sock->Context);

+    if (EFI_ERROR (Status)) {

+      goto OnError;

+    }

+  }

+

+  return Sock;

+

+OnError:

+

+  if (Sock->SockHandle != NULL) {

+    gBS->UninstallMultipleProtocolInterfaces (

+           Sock->SockHandle,

+           TcpProtocolGuid,

+           &Sock->NetProtocol,

+           NULL

+           );

+  }

+

+  if (NULL != Sock->SndBuffer.DataQueue) {

+    NetbufQueFree (Sock->SndBuffer.DataQueue);

+  }

+

+  if (NULL != Sock->RcvBuffer.DataQueue) {

+    NetbufQueFree (Sock->RcvBuffer.DataQueue);

+  }

+

+  FreePool (Sock);

+

+  return NULL;

+}

+

+/**

+  Destroy a socket.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockDestroy (

+  IN OUT SOCKET *Sock

+  )

+{

+  VOID        *SockProtocol;

+  EFI_GUID    *TcpProtocolGuid;

+  EFI_STATUS  Status;

+

+  ASSERT (SockStream == Sock->Type);

+

+  if (Sock->DestroyCallback != NULL) {

+    Sock->DestroyCallback (Sock, Sock->Context);

+  }

+

+  //

+  // Flush the completion token buffered

+  // by sock and rcv, snd buffer

+  //

+  if (!SOCK_IS_UNCONFIGURED (Sock)) {

+

+    SockConnFlush (Sock);

+    SockSetState (Sock, SO_CLOSED);

+    Sock->ConfigureState = SO_UNCONFIGURED;

+

+  }

+  //

+  // Destory the RcvBuffer Queue and SendBuffer Queue

+  //

+  NetbufQueFree (Sock->RcvBuffer.DataQueue);

+  NetbufQueFree (Sock->SndBuffer.DataQueue);

+

+  //

+  // Remove it from parent connection list if needed

+  //

+  if (Sock->Parent != NULL) {

+

+    RemoveEntryList (&(Sock->ConnectionList));

+    (Sock->Parent->ConnCnt)--;

+

+    DEBUG (

+      (EFI_D_WARN,

+      "SockDestory: Delete a unaccepted socket from parent now conncnt is %d\n",

+      Sock->Parent->ConnCnt)

+      );

+

+    Sock->Parent = NULL;

+  }

+

+  //

+  // Set the protocol guid and driver binding handle

+  // in the light of Sock->SockType

+  //

+  if (Sock->IpVersion == IP_VERSION_4) {

+    TcpProtocolGuid = &gEfiTcp4ProtocolGuid;

+  } else {

+    TcpProtocolGuid = &gEfiTcp6ProtocolGuid;

+  }

+

+  //

+  // Retrieve the protocol installed on this sock

+  //

+  Status = gBS->OpenProtocol (

+                  Sock->SockHandle,

+                  TcpProtocolGuid,

+                  &SockProtocol,

+                  Sock->DriverBinding,

+                  Sock->SockHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockDestroy: Open protocol installed on socket failed with %r\n",

+      Status)

+      );

+

+    goto FreeSock;

+  }

+

+  //

+  // Uninstall the protocol installed on this sock

+  // in the light of Sock->SockType

+  //

+  gBS->UninstallMultipleProtocolInterfaces (

+        Sock->SockHandle,

+        TcpProtocolGuid,

+        SockProtocol,

+        NULL

+        );

+

+FreeSock:

+

+  FreePool (Sock);

+}

+

+/**

+  Flush the sndBuffer and rcvBuffer of socket.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockConnFlush (

+  IN OUT SOCKET *Sock

+  )

+{

+  SOCKET  *Child;

+

+  ASSERT (Sock != NULL);

+

+  //

+  // Clear the flag in this socket

+  //

+  Sock->Flag = 0;

+

+  //

+  // Flush the SndBuffer and RcvBuffer of Sock

+  //

+  NetbufQueFlush (Sock->SndBuffer.DataQueue);

+  NetbufQueFlush (Sock->RcvBuffer.DataQueue);

+

+  //

+  // Signal the pending token

+  //

+  if (Sock->ConnectionToken != NULL) {

+    SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError);

+    Sock->ConnectionToken = NULL;

+  }

+

+  if (Sock->CloseToken != NULL) {

+    SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError);

+    Sock->CloseToken = NULL;

+  }

+

+  SockFlushPendingToken (Sock, &(Sock->ListenTokenList));

+  SockFlushPendingToken (Sock, &(Sock->RcvTokenList));

+  SockFlushPendingToken (Sock, &(Sock->SndTokenList));

+  SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList));

+

+  //

+  // Destroy the pending connection, if it is a listening socket

+  //

+  if (SOCK_IS_LISTENING (Sock)) {

+    while (!IsListEmpty (&Sock->ConnectionList)) {

+      Child = NET_LIST_HEAD (

+                &Sock->ConnectionList,

+                SOCKET,

+                ConnectionList

+                );

+

+      SockDestroyChild (Child);

+    }

+

+    Sock->ConnCnt = 0;

+  }

+

+}

+

+/**

+  Set the state of the socket.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+  @param[in]       State                 The new socket state to be set.

+

+**/

+VOID

+SockSetState (

+  IN OUT SOCKET     *Sock,

+  IN     UINT8      State

+  )

+{

+  Sock->State = State;

+}

+

+/**

+  Clone a new socket, including its associated protocol control block.

+

+  @param[in]  Sock                  Pointer to the socket to be cloned.

+

+  @return Pointer to the newly cloned socket. If NULL, an error condition occurred.

+

+**/

+SOCKET *

+SockClone (

+  IN SOCKET *Sock

+  )

+{

+  SOCKET          *ClonedSock;

+  SOCK_INIT_DATA  InitData;

+

+  InitData.BackLog         = Sock->BackLog;

+  InitData.Parent          = Sock;

+  InitData.State           = Sock->State;

+  InitData.ProtoHandler    = Sock->ProtoHandler;

+  InitData.Type            = Sock->Type;

+  InitData.RcvBufferSize   = Sock->RcvBuffer.HighWater;

+  InitData.SndBufferSize   = Sock->SndBuffer.HighWater;

+  InitData.DriverBinding   = Sock->DriverBinding;

+  InitData.IpVersion       = Sock->IpVersion;

+  InitData.Protocol        = &(Sock->NetProtocol);

+  InitData.CreateCallback  = Sock->CreateCallback;

+  InitData.DestroyCallback = Sock->DestroyCallback;

+  InitData.Context         = Sock->Context;

+  InitData.ProtoData       = Sock->ProtoReserved;

+  InitData.DataSize        = sizeof (Sock->ProtoReserved);

+

+  ClonedSock               = SockCreate (&InitData);

+

+  if (NULL == ClonedSock) {

+    DEBUG ((EFI_D_ERROR, "SockClone: no resource to create a cloned sock\n"));

+    return NULL;

+  }

+

+  SockSetState (ClonedSock, SO_CONNECTING);

+  ClonedSock->ConfigureState = Sock->ConfigureState;

+

+  return ClonedSock;

+}

+

+/**

+  Called by the low layer protocol to indicate the socket a connection is

+  established.

+

+  This function just changes the socket's state to SO_CONNECTED

+  and signals the token used for connection establishment.

+

+  @param[in, out]  Sock         Pointer to the socket associated with the

+                                established connection.

+

+**/

+VOID

+SockConnEstablished (

+  IN OUT SOCKET *Sock

+  )

+{

+

+  ASSERT (SO_CONNECTING == Sock->State);

+

+  SockSetState (Sock, SO_CONNECTED);

+

+  if (NULL == Sock->Parent) {

+    SockWakeConnToken (Sock);

+  } else {

+    SockWakeListenToken (Sock);

+  }

+

+}

+

+/**

+  Called by the low layer protocol to indicate the connection is closed.

+

+  This function flushes the socket, sets the state to SO_CLOSED, and signals

+  the close token.

+

+  @param[in, out]  Sock         Pointer to the socket associated with the closed

+                                connection.

+

+**/

+VOID

+SockConnClosed (

+  IN OUT SOCKET *Sock

+  )

+{

+  if (Sock->CloseToken != NULL) {

+    SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS);

+    Sock->CloseToken = NULL;

+  }

+

+  SockConnFlush (Sock);

+  SockSetState (Sock, SO_CLOSED);

+

+  if (Sock->Parent != NULL) {

+    SockDestroyChild (Sock);

+  }

+

+}

+

+/**

+  Called by low layer protocol to indicate that some data was sent or processed.

+

+  This function trims the sent data in the socket send buffer, and signals the data

+  token if proper.

+

+  @param[in, out]  Sock      Pointer to the socket.

+  @param[in]       Count     The length of the data processed or sent, in bytes.

+

+**/

+VOID

+SockDataSent (

+  IN OUT SOCKET     *Sock,

+  IN     UINT32     Count

+  )

+{

+  SOCK_TOKEN            *SockToken;

+  SOCK_COMPLETION_TOKEN *SndToken;

+

+  ASSERT (!IsListEmpty (&Sock->ProcessingSndTokenList));

+  ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize);

+

+  NetbufQueTrim (Sock->SndBuffer.DataQueue, Count);

+

+  //

+  // To check if we can signal some snd token in this socket

+  //

+  while (Count > 0) {

+    SockToken = NET_LIST_HEAD (

+                  &(Sock->ProcessingSndTokenList),

+                  SOCK_TOKEN,

+                  TokenList

+                  );

+

+    SndToken = SockToken->Token;

+

+    if (SockToken->RemainDataLen <= Count) {

+

+      RemoveEntryList (&(SockToken->TokenList));

+      SIGNAL_TOKEN (SndToken, EFI_SUCCESS);

+      Count -= SockToken->RemainDataLen;

+      FreePool (SockToken);

+    } else {

+

+      SockToken->RemainDataLen -= Count;

+      Count = 0;

+    }

+  }

+

+  //

+  // to judge if we can process some send token in

+  // Sock->SndTokenList, if so process those send token

+  //

+  SockProcessSndToken (Sock);

+}

+

+/**

+  Called by the low layer protocol to copy some data in the socket send

+  buffer starting from the specific offset to a buffer provided by

+  the caller.

+

+  @param[in]  Sock                  Pointer to the socket.

+  @param[in]  Offset                The start point of the data to be copied.

+  @param[in]  Len                   The length of the data to be copied.

+  @param[out] Dest                  Pointer to the destination to copy the data.

+

+  @return The data size copied.

+

+**/

+UINT32

+SockGetDataToSend (

+  IN  SOCKET      *Sock,

+  IN  UINT32      Offset,

+  IN  UINT32      Len,

+  OUT UINT8       *Dest

+  )

+{

+  ASSERT ((Sock != NULL) && SockStream == Sock->Type);

+

+  return NetbufQueCopy (

+          Sock->SndBuffer.DataQueue,

+          Offset,

+          Len,

+          Dest

+          );

+}

+

+/**

+  Called by the low layer protocol to deliver received data to socket layer.

+

+  This function will append the data to the socket receive buffer, set the

+  urgent data length, and then check if any receive token can be signaled.

+

+  @param[in, out]  Sock       Pointer to the socket.

+  @param[in, out]  NetBuffer  Pointer to the buffer that contains the received data.

+  @param[in]       UrgLen     The length of the urgent data in the received data.

+

+**/

+VOID

+SockDataRcvd (

+  IN OUT SOCKET    *Sock,

+  IN OUT NET_BUF   *NetBuffer,

+  IN     UINT32    UrgLen

+  )

+{

+  ASSERT ((Sock != NULL) && (Sock->RcvBuffer.DataQueue != NULL) &&

+    UrgLen <= NetBuffer->TotalSize);

+

+  NET_GET_REF (NetBuffer);

+

+  ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen;

+

+  NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer);

+

+  SockWakeRcvToken (Sock);

+}

+

+/**

+  Get the length of the free space of the specific socket buffer.

+

+  @param[in]  Sock              Pointer to the socket.

+  @param[in]  Which             Flag to indicate which socket buffer to check:

+                                either send buffer or receive buffer.

+

+  @return The length of the free space, in bytes.

+

+**/

+UINT32

+SockGetFreeSpace (

+  IN SOCKET  *Sock,

+  IN UINT32  Which

+  )

+{

+  UINT32      BufferCC;

+  SOCK_BUFFER *SockBuffer;

+

+  ASSERT (Sock != NULL && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which)));

+

+  if (SOCK_SND_BUF == Which) {

+    SockBuffer = &(Sock->SndBuffer);

+  } else {

+    SockBuffer = &(Sock->RcvBuffer);

+  }

+

+  BufferCC = (SockBuffer->DataQueue)->BufSize;

+

+  if (BufferCC >= SockBuffer->HighWater) {

+

+    return 0;

+  }

+

+  return SockBuffer->HighWater - BufferCC;

+}

+

+/**

+  Called by the low layer protocol to indicate that there will be no more data

+  from the communication peer.

+

+  This function sets the socket's state to SO_NO_MORE_DATA and signals all queued

+  IO tokens with the error status EFI_CONNECTION_FIN.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockNoMoreData (

+  IN OUT SOCKET *Sock

+  )

+{

+  EFI_STATUS  Err;

+

+  SOCK_NO_MORE_DATA (Sock);

+

+  if (!IsListEmpty (&Sock->RcvTokenList)) {

+

+    ASSERT (0 == GET_RCV_DATASIZE (Sock));

+

+    Err = Sock->SockError;

+

+    SOCK_ERROR (Sock, EFI_CONNECTION_FIN);

+

+    SockFlushPendingToken (Sock, &Sock->RcvTokenList);

+

+    SOCK_ERROR (Sock, Err);

+

+  }

+}

+

diff --git a/NetworkPkg/TcpDxe/SockImpl.h b/NetworkPkg/TcpDxe/SockImpl.h
new file mode 100644
index 0000000..bb4f6c2
--- /dev/null
+++ b/NetworkPkg/TcpDxe/SockImpl.h
@@ -0,0 +1,103 @@
+/** @file

+  The function declaration that provided for Socket Interface.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _SOCK_IMPL_H_

+#define _SOCK_IMPL_H_

+

+#include "Socket.h"

+

+/**

+  Signal a event with the given status.

+

+  @param[in] Token        The token's event is to be signaled.

+  @param[in] TokenStatus  The status to be sent with the event.

+

+**/

+#define SIGNAL_TOKEN(Token, TokenStatus) \

+  do { \

+    (Token)->Status = (TokenStatus); \

+    gBS->SignalEvent ((Token)->Event); \

+  } while (0)

+

+#define SOCK_HEADER_SPACE (60 + 60 + 72)

+

+/**

+  Process the TCP send data, buffer the tcp txdata and append

+  the buffer to socket send buffer, then try to send it.

+

+  @param[in]  Sock              Pointer to the socket.

+  @param[in]  TcpTxData         Pointer to the application provided send buffer.

+

+  @retval EFI_SUCCESS           The operation completed successfully.

+  @retval EFI_OUT_OF_RESOURCES  Failed due to resource limits.

+

+**/

+EFI_STATUS

+SockProcessTcpSndData (

+  IN SOCKET   *Sock,

+  IN VOID     *TcpTxData

+  );

+

+/**

+  Get received data from the socket layer to the receive token.

+

+  @param[in, out]  Sock       Pointer to the socket.

+  @param[in, out]  RcvToken   Pointer to the application provided receive token.

+

+  @return The length of data received in this token.

+

+**/

+UINT32

+SockProcessRcvToken (

+  IN OUT SOCKET        *Sock,

+  IN OUT SOCK_IO_TOKEN *RcvToken

+  );

+

+/**

+  Flush the sndBuffer and rcvBuffer of socket.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockConnFlush (

+  IN OUT SOCKET *Sock

+  );

+

+/**

+  Create a socket with initial data SockInitData.

+

+  @param[in]  SockInitData          Pointer to the initial data of the socket.

+

+  @return Pointer to the newly created socket, return NULL when exception occured.

+

+**/

+SOCKET *

+SockCreate (

+  IN SOCK_INIT_DATA *SockInitData

+  );

+

+/**

+  Destroy a socket.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockDestroy (

+  IN OUT SOCKET *Sock

+  );

+

+#endif

diff --git a/NetworkPkg/TcpDxe/SockInterface.c b/NetworkPkg/TcpDxe/SockInterface.c
new file mode 100644
index 0000000..e36c0e9
--- /dev/null
+++ b/NetworkPkg/TcpDxe/SockInterface.c
@@ -0,0 +1,999 @@
+/** @file

+  Interface function of the Socket.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "SockImpl.h"

+

+/**

+  Check whether the Event is in the List.

+

+  @param[in]  List             Pointer to the token list to be searched.

+  @param[in]  Event            The event to be checked.

+

+  @retval  TRUE                The specific Event exists in the List.

+  @retval  FALSE               The specific Event is not in the List.

+

+**/

+BOOLEAN

+SockTokenExistedInList (

+  IN LIST_ENTRY     *List,

+  IN EFI_EVENT      Event

+  )

+{

+  LIST_ENTRY      *ListEntry;

+  SOCK_TOKEN      *SockToken;

+

+  NET_LIST_FOR_EACH (ListEntry, List) {

+    SockToken = NET_LIST_USER_STRUCT (

+                  ListEntry,

+                  SOCK_TOKEN,

+                  TokenList

+                  );

+

+    if (Event == SockToken->Token->Event) {

+      return TRUE;

+    }

+  }

+

+  return FALSE;

+}

+

+/**

+  Call SockTokenExistedInList() to check whether the Event is

+  in the related socket's lists.

+

+  @param[in]  Sock             Pointer to the instance's socket.

+  @param[in]  Event            The event to be checked.

+

+  @retval  TRUE                The Event exists in related socket's lists.

+  @retval  FALSE               The Event is not in related socket's lists.

+

+**/

+BOOLEAN

+SockTokenExisted (

+  IN SOCKET    *Sock,

+  IN EFI_EVENT Event

+  )

+{

+

+  if (SockTokenExistedInList (&Sock->SndTokenList, Event) ||

+      SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) ||

+      SockTokenExistedInList (&Sock->RcvTokenList, Event) ||

+      SockTokenExistedInList (&Sock->ListenTokenList, Event)

+        ) {

+

+    return TRUE;

+  }

+

+  if ((Sock->ConnectionToken != NULL) && (Sock->ConnectionToken->Event == Event)) {

+

+    return TRUE;

+  }

+

+  if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Buffer a token into the specific list of the socket Sock.

+

+  @param[in]  Sock             Pointer to the instance's socket.

+  @param[in]  List             Pointer to the list to store the token.

+  @param[in]  Token            Pointer to the token to be buffered.

+  @param[in]  DataLen          The data length of the buffer contained in Token.

+

+  @return Pointer to the token that wraps Token. If NULL, an error condition occurred.

+

+**/

+SOCK_TOKEN *

+SockBufferToken (

+  IN SOCKET         *Sock,

+  IN LIST_ENTRY     *List,

+  IN VOID           *Token,

+  IN UINT32         DataLen

+  )

+{

+  SOCK_TOKEN  *SockToken;

+

+  SockToken = AllocateZeroPool (sizeof (SOCK_TOKEN));

+  if (NULL == SockToken) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockBufferIOToken: No Memory to allocate SockToken\n")

+      );

+

+    return NULL;

+  }

+

+  SockToken->Sock           = Sock;

+  SockToken->Token          = (SOCK_COMPLETION_TOKEN *) Token;

+  SockToken->RemainDataLen  = DataLen;

+  InsertTailList (List, &SockToken->TokenList);

+

+  return SockToken;

+}

+

+/**

+  Destory the socket Sock and its associated protocol control block.

+

+  @param[in, out]  Sock                 The socket to be destroyed.

+

+  @retval EFI_SUCCESS          The socket Sock was destroyed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.

+

+**/

+EFI_STATUS

+SockDestroyChild (

+  IN OUT SOCKET *Sock

+  )

+{

+  EFI_STATUS  Status;

+

+  ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL));

+

+  if (Sock->IsDestroyed) {

+    return EFI_SUCCESS;

+  }

+

+  Sock->IsDestroyed = TRUE;

+

+  Status            = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockDestroyChild: Get the lock to access socket failed with %r\n",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  //

+  // force protocol layer to detach the PCB

+  //

+  Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL);

+

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockDestroyChild: Protocol detach socket failed with %r\n",

+      Status)

+      );

+

+    Sock->IsDestroyed = FALSE;

+  } else if (SOCK_IS_CONFIGURED (Sock)) {

+

+    SockConnFlush (Sock);

+    SockSetState (Sock, SO_CLOSED);

+

+    Sock->ConfigureState = SO_UNCONFIGURED;

+  }

+

+  EfiReleaseLock (&(Sock->Lock));

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  SockDestroy (Sock);

+  return EFI_SUCCESS;

+}

+

+/**

+  Create a socket and its associated protocol control block

+  with the intial data SockInitData and protocol specific

+  data ProtoData.

+

+  @param[in]  SockInitData         Inital data to setting the socket.

+

+  @return Pointer to the newly created socket. If NULL, an error condition occured.

+

+**/

+SOCKET *

+SockCreateChild (

+  IN SOCK_INIT_DATA *SockInitData

+  )

+{

+  SOCKET      *Sock;

+  EFI_STATUS  Status;

+

+  //

+  // create a new socket

+  //

+  Sock = SockCreate (SockInitData);

+  if (NULL == Sock) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockCreateChild: No resource to create a new socket\n")

+      );

+

+    return NULL;

+  }

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockCreateChild: Get the lock to access socket failed with %r\n",

+      Status)

+      );

+

+    SockDestroy (Sock);

+    return NULL;

+  }

+  //

+  // inform the protocol layer to attach the socket

+  // with a new protocol control block

+  //

+  Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL);

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockCreateChild: Protocol failed to attach a socket with %r\n",

+      Status)

+      );

+

+    SockDestroy (Sock);

+    Sock = NULL;

+  }

+

+  EfiReleaseLock (&(Sock->Lock));

+  return Sock;

+}

+

+/**

+  Configure the specific socket Sock using configuration data ConfigData.

+

+  @param[in]  Sock             Pointer to the socket to be configured.

+  @param[in]  ConfigData       Pointer to the configuration data.

+

+  @retval EFI_SUCCESS          The socket configured successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is already configured.

+

+**/

+EFI_STATUS

+SockConfigure (

+  IN SOCKET *Sock,

+  IN VOID   *ConfigData

+  )

+{

+  EFI_STATUS  Status;

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockConfigure: Get the access for socket failed with %r",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  if (SOCK_IS_CONFIGURED (Sock)) {

+    Status = EFI_ACCESS_DENIED;

+    goto OnExit;

+  }

+

+  ASSERT (Sock->State == SO_CLOSED);

+

+  Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData);

+

+OnExit:

+  EfiReleaseLock (&(Sock->Lock));

+

+  return Status;

+}

+

+/**

+  Initiate a connection establishment process.

+

+  @param[in]  Sock             Pointer to the socket to initiate the initate the

+                               connection.

+  @param[in]  Token            Pointer to the token used for the connection

+                               operation.

+

+  @retval EFI_SUCCESS          The connection initialized successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not configured to

+                               be an active one, or the token is already in one of

+                               this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockConnect (

+  IN SOCKET *Sock,

+  IN VOID   *Token

+  )

+{

+  EFI_STATUS  Status;

+  EFI_EVENT   Event;

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockConnect: Get the access for socket failed with %r",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  if (SOCK_IS_NO_MAPPING (Sock)) {

+    Status = EFI_NO_MAPPING;

+    goto OnExit;

+  }

+

+  if (SOCK_IS_UNCONFIGURED (Sock)) {

+

+    Status = EFI_NOT_STARTED;

+    goto OnExit;

+  }

+

+  if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) {

+

+    Status = EFI_ACCESS_DENIED;

+    goto OnExit;

+  }

+

+  Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;

+

+  if (SockTokenExisted (Sock, Event)) {

+

+    Status = EFI_ACCESS_DENIED;

+    goto OnExit;

+  }

+

+  Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token;

+  SockSetState (Sock, SO_CONNECTING);

+  Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL);

+

+OnExit:

+  EfiReleaseLock (&(Sock->Lock));

+  return Status;

+}

+

+/**

+  Issue a listen token to get an existed connected network instance

+  or wait for a connection if there is none.

+

+  @param[in]  Sock             Pointer to the socket to accept connections.

+  @param[in]  Token            The token to accept a connection.

+

+  @retval EFI_SUCCESS          Either a connection is accpeted or the Token is

+                               buffered for further acception.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not configured to

+                               be a passive one, or the token is already in one of

+                               this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the Token due to memory limits.

+

+**/

+EFI_STATUS

+SockAccept (

+  IN SOCKET *Sock,

+  IN VOID   *Token

+  )

+{

+  EFI_TCP4_LISTEN_TOKEN *ListenToken;

+  LIST_ENTRY            *ListEntry;

+  EFI_STATUS            Status;

+  SOCKET                *Socket;

+  EFI_EVENT             Event;

+

+  ASSERT (SockStream == Sock->Type);

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockAccept: Get the access for socket failed with %r",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  if (SOCK_IS_NO_MAPPING (Sock)) {

+    Status = EFI_NO_MAPPING;

+    goto Exit;

+  }

+

+  if (SOCK_IS_UNCONFIGURED (Sock)) {

+

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  if (!SOCK_IS_LISTENING (Sock)) {

+

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;

+

+  if (SockTokenExisted (Sock, Event)) {

+

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token;

+

+  //

+  // Check if a connection has already in this Sock->ConnectionList

+  //

+  NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) {

+

+    Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList);

+

+    if (SOCK_IS_CONNECTED (Socket)) {

+      ListenToken->NewChildHandle = Socket->SockHandle;

+      SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);

+

+      RemoveEntryList (ListEntry);

+

+      ASSERT (Socket->Parent != NULL);

+

+      Socket->Parent->ConnCnt--;

+

+      DEBUG (

+        (EFI_D_INFO,

+        "SockAccept: Accept a socket, now conncount is %d",

+        Socket->Parent->ConnCnt)

+        );

+      Socket->Parent = NULL;

+

+      goto Exit;

+    }

+  }

+

+  //

+  // Buffer this token for latter incoming connection request

+  //

+  if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) {

+

+    Status = EFI_OUT_OF_RESOURCES;

+  }

+

+Exit:

+  EfiReleaseLock (&(Sock->Lock));

+

+  return Status;

+}

+

+/**

+  Issue a token with data to the socket to send out.

+

+  @param[in]  Sock             Pointer to the socket to process the token with

+                               data.

+  @param[in]  Token            The token with data that needs to send out.

+

+  @retval EFI_SUCCESS          The token processed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not in a

+                               synchronized state , or the token is already in one

+                               of this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the token due to memory limits.

+

+**/

+EFI_STATUS

+SockSend (

+  IN SOCKET *Sock,

+  IN VOID   *Token

+  )

+{

+  SOCK_IO_TOKEN           *SndToken;

+  EFI_EVENT               Event;

+  UINT32                  FreeSpace;

+  EFI_TCP4_TRANSMIT_DATA  *TxData;

+  EFI_STATUS              Status;

+  SOCK_TOKEN              *SockToken;

+  UINT32                  DataLen;

+

+  ASSERT (SockStream == Sock->Type);

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockSend: Get the access for socket failed with %r",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  if (SOCK_IS_NO_MAPPING (Sock)) {

+    Status = EFI_NO_MAPPING;

+    goto Exit;

+  }

+

+  SndToken  = (SOCK_IO_TOKEN *) Token;

+  TxData    = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData;

+

+  if (SOCK_IS_UNCONFIGURED (Sock)) {

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) {

+

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  //

+  // check if a token is already in the token buffer

+  //

+  Event = SndToken->Token.Event;

+

+  if (SockTokenExisted (Sock, Event)) {

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  DataLen = TxData->DataLength;

+

+  //

+  // process this sending token now or buffer it only?

+  //

+  FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);

+

+  if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) {

+

+    SockToken = SockBufferToken (

+                  Sock,

+                  &Sock->SndTokenList,

+                  SndToken,

+                  DataLen

+                  );

+

+    if (NULL == SockToken) {

+      Status = EFI_OUT_OF_RESOURCES;

+    }

+  } else {

+

+    SockToken = SockBufferToken (

+                  Sock,

+                  &Sock->ProcessingSndTokenList,

+                  SndToken,

+                  DataLen

+                  );

+

+    if (NULL == SockToken) {

+      DEBUG (

+        (EFI_D_ERROR,

+        "SockSend: Failed to buffer IO token into socket processing SndToken List\n",

+        Status)

+        );

+

+      Status = EFI_OUT_OF_RESOURCES;

+      goto Exit;

+    }

+

+    Status = SockProcessTcpSndData (Sock, TxData);

+

+    if (EFI_ERROR (Status)) {

+      DEBUG (

+        (EFI_D_ERROR,

+        "SockSend: Failed to process Snd Data\n",

+        Status)

+        );

+

+      RemoveEntryList (&(SockToken->TokenList));

+      FreePool (SockToken);

+    }

+  }

+

+Exit:

+  EfiReleaseLock (&(Sock->Lock));

+  return Status;

+}

+

+/**

+  Issue a token to get data from the socket.

+

+  @param[in]  Sock             Pointer to the socket to get data from.

+  @param[in]  Token            The token to store the received data from the

+                               socket.

+

+  @retval EFI_SUCCESS          The token processed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not in a

+                               synchronized state , or the token is already in one

+                               of this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+  @retval EFI_CONNECTION_FIN   The connection is closed and there is no more data.

+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the token due to memory limit.

+

+**/

+EFI_STATUS

+SockRcv (

+  IN SOCKET *Sock,

+  IN VOID   *Token

+  )

+{

+  SOCK_IO_TOKEN *RcvToken;

+  UINT32        RcvdBytes;

+  EFI_STATUS    Status;

+  EFI_EVENT     Event;

+

+  ASSERT (SockStream == Sock->Type);

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockRcv: Get the access for socket failed with %r",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  if (SOCK_IS_NO_MAPPING (Sock)) {

+

+    Status = EFI_NO_MAPPING;

+    goto Exit;

+  }

+

+  if (SOCK_IS_UNCONFIGURED (Sock)) {

+

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) {

+

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  RcvToken = (SOCK_IO_TOKEN *) Token;

+

+  //

+  // check if a token is already in the token buffer of this socket

+  //

+  Event = RcvToken->Token.Event;

+  if (SockTokenExisted (Sock, Event)) {

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  RcvToken  = (SOCK_IO_TOKEN *) Token;

+  RcvdBytes = GET_RCV_DATASIZE (Sock);

+

+  //

+  // check whether an error has happened before

+  //

+  if (EFI_ABORTED != Sock->SockError) {

+

+    SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError);

+    Sock->SockError = EFI_ABORTED;

+    goto Exit;

+  }

+

+  //

+  // check whether can not receive and there is no any

+  // data buffered in Sock->RcvBuffer

+  //

+  if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) {

+

+    Status = EFI_CONNECTION_FIN;

+    goto Exit;

+  }

+

+  if (RcvdBytes != 0) {

+    Status = SockProcessRcvToken (Sock, RcvToken);

+

+    if (EFI_ERROR (Status)) {

+      goto Exit;

+    }

+

+    Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL);

+  } else {

+

+    if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) {

+      Status = EFI_OUT_OF_RESOURCES;

+    }

+  }

+

+Exit:

+  EfiReleaseLock (&(Sock->Lock));

+  return Status;

+}

+

+/**

+  Reset the socket and its associated protocol control block.

+

+  @param[in, out]  Sock        Pointer to the socket to be flushed.

+

+  @retval EFI_SUCCESS          The socket is flushed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.

+

+**/

+EFI_STATUS

+SockFlush (

+  IN OUT SOCKET *Sock

+  )

+{

+  EFI_STATUS  Status;

+

+  ASSERT (SockStream == Sock->Type);

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockFlush: Get the access for socket failed with %r",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  if (!SOCK_IS_CONFIGURED (Sock)) {

+

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL);

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockFlush: Protocol failed handling SOCK_FLUSH with %r",

+      Status)

+      );

+

+    goto Exit;

+  }

+

+  SOCK_ERROR (Sock, EFI_ABORTED);

+  SockConnFlush (Sock);

+  SockSetState (Sock, SO_CLOSED);

+

+  Sock->ConfigureState = SO_UNCONFIGURED;

+

+Exit:

+  EfiReleaseLock (&(Sock->Lock));

+  return Status;

+}

+

+/**

+  Close or abort the socket associated connection.

+

+  @param[in, out]  Sock        Pointer to the socket of the connection to close

+                               or abort.

+  @param[in]  Token            The token for a close operation.

+  @param[in]  OnAbort          TRUE for aborting the connection; FALSE to close it.

+

+  @retval EFI_SUCCESS          The close or abort operation initialized

+                               successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not in a

+                               synchronized state , or the token is already in one

+                               of this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockClose (

+  IN OUT SOCKET  *Sock,

+  IN     VOID    *Token,

+  IN     BOOLEAN OnAbort

+  )

+{

+  EFI_STATUS  Status;

+  EFI_EVENT   Event;

+

+  ASSERT (SockStream == Sock->Type);

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockClose: Get the access for socket failed with %r",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  if (SOCK_IS_NO_MAPPING (Sock)) {

+    Status = EFI_NO_MAPPING;

+    goto Exit;

+  }

+

+  if (SOCK_IS_UNCONFIGURED (Sock)) {

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  if (SOCK_IS_DISCONNECTING (Sock)) {

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;

+

+  if (SockTokenExisted (Sock, Event)) {

+    Status = EFI_ACCESS_DENIED;

+    goto Exit;

+  }

+

+  Sock->CloseToken = Token;

+  SockSetState (Sock, SO_DISCONNECTING);

+

+  if (OnAbort) {

+    Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL);

+  } else {

+    Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL);

+  }

+

+Exit:

+  EfiReleaseLock (&(Sock->Lock));

+  return Status;

+}

+

+/**

+  Get the mode data of the low layer protocol.

+

+  @param[in]       Sock        Pointer to the socket to get mode data from.

+  @param[in, out]  Mode        Pointer to the data to store the low layer mode

+                               information.

+

+  @retval EFI_SUCCESS          The mode data was obtained successfully.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockGetMode (

+  IN     SOCKET *Sock,

+  IN OUT VOID   *Mode

+  )

+{

+  return Sock->ProtoHandler (Sock, SOCK_MODE, Mode);

+}

+

+/**

+  Configure the low level protocol to join a multicast group for

+  this socket's connection.

+

+  @param[in]  Sock             Pointer to the socket of the connection to join the

+                               specific multicast group.

+  @param[in]  GroupInfo        Pointer to the multicast group info.

+

+  @retval EFI_SUCCESS          The configuration completed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockGroup (

+  IN SOCKET *Sock,

+  IN VOID   *GroupInfo

+  )

+{

+  EFI_STATUS  Status;

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+

+  if (EFI_ERROR (Status)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockGroup: Get the access for socket failed with %r",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  if (SOCK_IS_UNCONFIGURED (Sock)) {

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo);

+

+Exit:

+  EfiReleaseLock (&(Sock->Lock));

+  return Status;

+}

+

+/**

+  Add or remove route information in IP route table associated

+  with this socket.

+

+  @param[in]  Sock             Pointer to the socket associated with the IP route

+                               table to operate on.

+  @param[in]  RouteInfo        Pointer to the route information to be processed.

+

+  @retval EFI_SUCCESS          The route table updated successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is  not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockRoute (

+  IN SOCKET    *Sock,

+  IN VOID      *RouteInfo

+  )

+{

+  EFI_STATUS  Status;

+

+  Status = EfiAcquireLockOrFail (&(Sock->Lock));

+  if (EFI_ERROR (Status)) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "SockRoute: Get the access for socket failed with %r",

+      Status)

+      );

+

+    return EFI_ACCESS_DENIED;

+  }

+

+  if (SOCK_IS_NO_MAPPING (Sock)) {

+    Status = EFI_NO_MAPPING;

+    goto Exit;

+  }

+

+  if (SOCK_IS_UNCONFIGURED (Sock)) {

+    Status = EFI_NOT_STARTED;

+    goto Exit;

+  }

+

+  Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo);

+

+Exit:

+  EfiReleaseLock (&(Sock->Lock));

+  return Status;

+}

+

diff --git a/NetworkPkg/TcpDxe/Socket.h b/NetworkPkg/TcpDxe/Socket.h
new file mode 100644
index 0000000..a006252
--- /dev/null
+++ b/NetworkPkg/TcpDxe/Socket.h
@@ -0,0 +1,924 @@
+/** @file

+  Common head file for TCP socket.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _SOCKET_H_

+#define _SOCKET_H_

+

+#include <Uefi.h>

+

+#include <Protocol/Tcp4.h>

+#include <Protocol/Tcp6.h>

+

+#include <Library/NetLib.h>

+#include <Library/DebugLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/UefiRuntimeServicesTableLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/DpcLib.h>

+

+#define SOCK_SND_BUF        0

+#define SOCK_RCV_BUF        1

+

+#define SOCK_BUFF_LOW_WATER (2 * 1024)

+#define SOCK_RCV_BUFF_SIZE  (8 * 1024)

+#define SOCK_SND_BUFF_SIZE  (8 * 1024)

+#define SOCK_BACKLOG        5

+

+#define PROTO_RESERVED_LEN  20

+

+#define SO_NO_MORE_DATA     0x0001

+

+//

+//

+//

+// When a socket is created it enters into SO_UNCONFIGURED,

+// no actions can be taken on this socket, only after calling

+// SockConfigure. The state transition diagram of socket is

+// as following:

+//

+// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING

+//  ^      |                                    |

+//  |      --->  SO_LISTENING                   |

+//  |                                           |

+//  |------------------SO_DISCONNECTING<-- SO_CONNECTED

+//

+// A passive socket can only go into SO_LISTENING and

+// SO_UNCONFIGURED state. SO_XXXING state is a middle state

+// when a socket is undergoing a protocol procedure such

+// as requesting a TCP connection.

+//

+//

+//

+

+///

+/// Socket state

+///

+#define SO_CLOSED        0

+#define SO_LISTENING     1

+#define SO_CONNECTING    2

+#define SO_CONNECTED     3

+#define SO_DISCONNECTING 4

+

+///

+/// Socket configure state

+///

+#define SO_UNCONFIGURED        0

+#define SO_CONFIGURED_ACTIVE   1

+#define SO_CONFIGURED_PASSIVE  2

+#define SO_NO_MAPPING          3

+

+///

+///  The request issued from socket layer to protocol layer.

+///

+#define SOCK_ATTACH     0    ///< Attach current socket to a new PCB

+#define SOCK_DETACH     1    ///< Detach current socket from the PCB

+#define SOCK_CONFIGURE  2    ///< Configure attached PCB

+#define SOCK_FLUSH      3    ///< Flush attached PCB

+#define SOCK_SND        4    ///< Need protocol to send something

+#define SOCK_SNDPUSH    5    ///< Need protocol to send pushed data

+#define SOCK_SNDURG     6    ///< Need protocol to send urgent data

+#define SOCK_CONSUMED   7    ///< Application has retrieved data from socket

+#define SOCK_CONNECT    8    ///< Need to connect to a peer

+#define SOCK_CLOSE      9    ///< Need to close the protocol process

+#define SOCK_ABORT      10   ///< Need to reset the protocol process

+#define SOCK_POLL       11   ///< Need to poll to the protocol layer

+#define SOCK_ROUTE      12   ///< Need to add a route information

+#define SOCK_MODE       13   ///< Need to get the mode data of the protocol

+#define SOCK_GROUP      14   ///< Need to join a mcast group

+

+/**

+  Set socket SO_NO_MORE_DATA flag.

+

+  @param[in] Sock            Pointer to the socket

+

+**/

+#define SOCK_NO_MORE_DATA(Sock)     ((Sock)->Flag |= SO_NO_MORE_DATA)

+

+/**

+  Check whether the socket is unconfigured.

+

+  @param[in]  Sock           Pointer to the socket.

+

+  @retval TRUE               The socket is unconfigued.

+  @retval FALSE              The socket is not unconfigued.

+

+**/

+#define SOCK_IS_UNCONFIGURED(Sock)  ((Sock)->ConfigureState == SO_UNCONFIGURED)

+

+/**

+  Check whether the socket is configured.

+

+  @param[in] Sock            Pointer to the socket

+

+  @retval TRUE               The socket is configued

+  @retval FALSE              The socket is not configued

+

+**/

+#define SOCK_IS_CONFIGURED(Sock) \

+    (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \

+    ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE))

+

+/**

+  Check whether the socket is configured to active mode.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @retval TRUE               The socket is configued to active mode.

+  @retval FALSE              The socket is not configued to active mode.

+

+**/

+#define SOCK_IS_CONFIGURED_ACTIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE)

+

+/**

+  Check whether the socket is configured to passive mode.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @retval TRUE               The socket is configued to passive mode.

+  @retval FALSE              The socket is not configued to passive mode.

+

+**/

+#define SOCK_IS_CONNECTED_PASSIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)

+

+/**

+  Check whether the socket is mapped.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @retval TRUE               The socket is not mapping.

+  @retval FALSE              The socket is mapped.

+

+**/

+#define SOCK_IS_NO_MAPPING(Sock)  ((Sock)->ConfigureState == SO_NO_MAPPING)

+

+/**

+  Check whether the socket is closed.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @retval TRUE               The socket is closed.

+  @retval FALSE              The socket is not closed.

+

+**/

+#define SOCK_IS_CLOSED(Sock)          ((Sock)->State == SO_CLOSED)

+

+/**

+  Check whether the socket is listening.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @retval TRUE               The socket is listening.

+  @retval FALSE              The socket is not listening.

+

+**/

+#define SOCK_IS_LISTENING(Sock)       ((Sock)->State == SO_LISTENING)

+

+/**

+  Check whether the socket is connecting.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @retval TRUE               The socket is connecting.

+  @retval FALSE              The socket is not connecting.

+

+**/

+#define SOCK_IS_CONNECTING(Sock)      ((Sock)->State == SO_CONNECTING)

+

+/**

+  Check whether the socket has connected.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @retval TRUE               The socket has connected.

+  @retval FALSE              The socket has not connected.

+

+**/

+#define SOCK_IS_CONNECTED(Sock)       ((Sock)->State == SO_CONNECTED)

+

+/**

+  Check whether the socket is disconnecting.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @retval TRUE               The socket is disconnecting.

+  @retval FALSE              The socket is not disconnecting.

+

+**/

+#define SOCK_IS_DISCONNECTING(Sock)   ((Sock)->State == SO_DISCONNECTING)

+

+/**

+  Check whether the socket is no more data.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @retval TRUE               The socket is no more data.

+  @retval FALSE              The socket still has data.

+

+**/

+#define SOCK_IS_NO_MORE_DATA(Sock)    (0 != ((Sock)->Flag & SO_NO_MORE_DATA))

+

+/**

+  Set the size of the receive buffer.

+

+  @param[in] Sock            Pointer to the socket.

+  @param[in] Size            The size to set.

+

+**/

+#define SET_RCV_BUFFSIZE(Sock, Size)  ((Sock)->RcvBuffer.HighWater = (Size))

+

+/**

+  Get the size of the receive buffer.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @return The receive buffer size.

+

+**/

+#define GET_RCV_BUFFSIZE(Sock)        ((Sock)->RcvBuffer.HighWater)

+

+/**

+  Get the size of the receive data.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @return The received data size.

+

+**/

+#define GET_RCV_DATASIZE(Sock)        (((Sock)->RcvBuffer.DataQueue)->BufSize)

+

+/**

+  Set the size of the send buffer.

+

+  @param[in] Sock            Pointer to the socket.

+  @param[in] Size            The size to set.

+

+**/

+#define SET_SND_BUFFSIZE(Sock, Size)  ((Sock)->SndBuffer.HighWater = (Size))

+

+/**

+  Get the size of the send buffer.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @return The send buffer size.

+

+**/

+#define GET_SND_BUFFSIZE(Sock)        ((Sock)->SndBuffer.HighWater)

+

+/**

+  Get the size of the send data.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @return The send data size.

+

+**/

+#define GET_SND_DATASIZE(Sock)        (((Sock)->SndBuffer.DataQueue)->BufSize)

+

+/**

+  Set the backlog value of the socket.

+

+  @param[in] Sock            Pointer to the socket.

+  @param[in] Value           The value to set.

+

+**/

+#define SET_BACKLOG(Sock, Value)      ((Sock)->BackLog = (Value))

+

+/**

+  Get the backlog value of the socket.

+

+  @param[in] Sock            Pointer to the socket.

+

+  @return The backlog value.

+

+**/

+#define GET_BACKLOG(Sock)             ((Sock)->BackLog)

+

+/**

+  Set the socket with error state.

+

+  @param[in] Sock            Pointer to the socket.

+  @param[in] Error           The error state.

+

+**/

+#define SOCK_ERROR(Sock, Error)       ((Sock)->SockError = (Error))

+

+#define SOCK_SIGNATURE                SIGNATURE_32 ('S', 'O', 'C', 'K')

+

+#define SOCK_FROM_THIS(a)             CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE)

+

+#define SOCK_FROM_TOKEN(Token)        (((SOCK_TOKEN *) (Token))->Sock)

+

+#define PROTO_TOKEN_FORM_SOCK(SockToken, Type)  ((Type *) (((SOCK_TOKEN *) (SockToken))->Token))

+

+typedef struct _TCP_SOCKET SOCKET;

+

+///

+/// Socket completion token

+///

+typedef struct _SOCK_COMPLETION_TOKEN {

+  EFI_EVENT   Event;            ///< The event to be issued

+  EFI_STATUS  Status;           ///< The status to be issued

+} SOCK_COMPLETION_TOKEN;

+

+typedef union {

+  VOID  *RxData;

+  VOID  *TxData;

+} SOCK_IO_DATA;

+

+///

+/// The application token with data packet

+///

+typedef struct _SOCK_IO_TOKEN {

+  SOCK_COMPLETION_TOKEN Token;

+  SOCK_IO_DATA          Packet;

+} SOCK_IO_TOKEN;

+

+///

+///  The socket type.

+///

+typedef enum {

+  SockDgram, ///< This socket providing datagram service

+  SockStream ///< This socket providing stream service

+} SOCK_TYPE;

+

+///

+///  The buffer structure of rcvd data and send data used by socket.

+///

+typedef struct _SOCK_BUFFER {

+  UINT32        HighWater;  ///< The buffersize upper limit of sock_buffer

+  UINT32        LowWater;   ///< The low warter mark of sock_buffer

+  NET_BUF_QUEUE *DataQueue; ///< The queue to buffer data

+} SOCK_BUFFER;

+

+/**

+  The handler of protocol for request from socket.

+

+  @param[in] Socket          The socket issuing the request to protocol.

+  @param[in] Request         The request issued by socket.

+  @param[in] RequestData     The request related data.

+

+  @retval EFI_SUCCESS        The socket request is completed successfully.

+  @retval other              The error status returned by the corresponding TCP

+                             layer function.

+

+**/

+typedef

+EFI_STATUS

+(*SOCK_PROTO_HANDLER) (

+  IN SOCKET       *Socket,

+  IN UINT8        Request,

+  IN VOID         *RequestData

+  );

+

+/**

+  The Callback funtion called after the TCP socket is created.

+

+  @param[in]  This            Pointer to the socket just created.

+  @param[in]  Context         Context of the socket.

+

+  @retval EFI_SUCCESS         This protocol installed successfully.

+  @retval other               Some error occured.

+

+**/

+typedef

+EFI_STATUS

+(*SOCK_CREATE_CALLBACK) (

+  IN SOCKET  *This,

+  IN VOID    *Context

+  );

+

+/**

+  The callback function called before the TCP socket is to be destroyed.

+

+  @param[in]  This                   The TCP socket to be destroyed.

+  @param[in]  Context                The context.

+

+**/

+typedef

+VOID

+(*SOCK_DESTROY_CALLBACK) (

+  IN SOCKET  *This,

+  IN VOID    *Context

+  );

+

+///

+///  The initialize data for create a new socket.

+///

+typedef struct _SOCK_INIT_DATA {

+  SOCK_TYPE              Type;

+  UINT8                  State;

+

+  SOCKET                 *Parent;        ///< The parent of this socket

+  UINT32                 BackLog;        ///< The connection limit for listening socket

+  UINT32                 SndBufferSize;  ///< The high warter mark of send buffer

+  UINT32                 RcvBufferSize;  ///< The high warter mark of receive buffer

+  UINT8                  IpVersion;

+  VOID                   *Protocol;      ///< The pointer to protocol function template

+                                         ///< wanted to install on socket

+

+  //

+  // Callbacks after socket is created and before socket is to be destroyed.

+  //

+  SOCK_CREATE_CALLBACK   CreateCallback;  ///< Callback after created

+  SOCK_DESTROY_CALLBACK  DestroyCallback; ///< Callback before destroied

+  VOID                   *Context;        ///< The context of the callback

+

+  //

+  // Opaque protocol data.

+  //

+  VOID                   *ProtoData;

+  UINT32                 DataSize;

+

+  SOCK_PROTO_HANDLER     ProtoHandler;    ///< The handler of protocol for socket request

+

+  EFI_HANDLE             DriverBinding;   ///< The driver binding handle

+} SOCK_INIT_DATA;

+

+///

+///  The union type of TCP and UDP protocol.

+///

+typedef union _NET_PROTOCOL {

+  EFI_TCP4_PROTOCOL      Tcp4Protocol;    ///< Tcp4 protocol

+  EFI_TCP6_PROTOCOL      Tcp6Protocol;    ///< Tcp6 protocol

+} NET_PROTOCOL;

+///

+/// The socket structure representing a network service access point.

+///

+struct _TCP_SOCKET {

+  //

+  // Socket description information

+  //

+  UINT32                    Signature;      ///< Signature of the socket

+  EFI_HANDLE                SockHandle;     ///< The virtual handle of the socket

+  EFI_HANDLE                DriverBinding;  ///< Socket's driver binding protocol

+  EFI_DEVICE_PATH_PROTOCOL  *ParentDevicePath;

+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;

+  LIST_ENTRY                Link;

+  UINT8                     ConfigureState;

+  SOCK_TYPE                 Type;

+  UINT8                     State;

+  UINT16                    Flag;

+  EFI_LOCK                  Lock;           ///< The lock of socket

+  SOCK_BUFFER               SndBuffer;      ///< Send buffer of application's data

+  SOCK_BUFFER               RcvBuffer;      ///< Receive buffer of received data

+  EFI_STATUS                SockError;      ///< The error returned by low layer protocol

+  BOOLEAN                   IsDestroyed;

+

+  //

+  // Fields used to manage the connection request

+  //

+  UINT32                    BackLog;        ///< the limit of connection to this socket

+  UINT32                    ConnCnt;        ///< the current count of connections to it

+  SOCKET                    *Parent;        ///< listening parent that accept the connection

+  LIST_ENTRY                ConnectionList; ///< the connections maintained by this socket

+  //

+  // The queue to buffer application's asynchronous token

+  //

+  LIST_ENTRY                ListenTokenList;

+  LIST_ENTRY                RcvTokenList;

+  LIST_ENTRY                SndTokenList;

+  LIST_ENTRY                ProcessingSndTokenList;

+

+  SOCK_COMPLETION_TOKEN     *ConnectionToken; ///< app's token to signal if connected

+  SOCK_COMPLETION_TOKEN     *CloseToken;      ///< app's token to signal if closed

+  //

+  // Interface for low level protocol

+  //

+  SOCK_PROTO_HANDLER        ProtoHandler;     ///< The request handler of protocol

+  UINT8                     ProtoReserved[PROTO_RESERVED_LEN];  ///< Data fields reserved for protocol

+  UINT8                     IpVersion;

+  NET_PROTOCOL              NetProtocol;                        ///< TCP or UDP protocol socket used

+  //

+  // Callbacks after socket is created and before socket is to be destroyed.

+  //

+  SOCK_CREATE_CALLBACK      CreateCallback;   ///< Callback after created

+  SOCK_DESTROY_CALLBACK     DestroyCallback;  ///< Callback before destroied

+  VOID                      *Context;         ///< The context of the callback

+};

+

+///

+///  The token structure buffered in socket layer.

+///

+typedef struct _SOCK_TOKEN {

+  LIST_ENTRY            TokenList;      ///< The entry to add in the token list

+  SOCK_COMPLETION_TOKEN *Token;         ///< The application's token

+  UINT32                RemainDataLen;  ///< Unprocessed data length

+  SOCKET                *Sock;          ///< The poninter to the socket this token

+                                        ///< belongs to

+} SOCK_TOKEN;

+

+///

+/// Reserved data to access the NET_BUF delivered by TCP driver.

+///

+typedef struct _TCP_RSV_DATA {

+  UINT32 UrgLen;

+} TCP_RSV_DATA;

+

+//

+// Socket provided oprerations for low layer protocol implemented in SockImpl.c

+//

+

+/**

+  Set the state of the socket.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+  @param[in]       State                 The new socket state to be set.

+

+**/

+VOID

+SockSetState (

+  IN OUT SOCKET     *Sock,

+  IN     UINT8      State

+  );

+

+/**

+  Clone a new socket including its associated protocol control block.

+

+  @param[in]  Sock                  Pointer to the socket to be cloned.

+

+  @return Pointer to the newly cloned socket. If NULL, an error condition occurred.

+

+**/

+SOCKET *

+SockClone (

+  IN SOCKET *Sock

+  );

+

+/**

+  Called by the low layer protocol to indicate the socket a connection is

+  established.

+

+  This function just changes the socket's state to SO_CONNECTED

+  and signals the token used for connection establishment.

+

+  @param[in, out]  Sock         Pointer to the socket associated with the

+                                established connection.

+

+**/

+VOID

+SockConnEstablished (

+  IN OUT SOCKET *Sock

+  );

+

+/**

+  Called by the low layer protocol to indicate that the connection is closed.

+

+  This function flushes the socket, sets the state to SO_CLOSED, and signals

+  the close token.

+

+  @param[in, out]  Sock         Pointer to the socket associated with the closed

+                                connection.

+

+**/

+VOID

+SockConnClosed (

+  IN OUT SOCKET *Sock

+  );

+

+/**

+  Called by low layer protocol to indicate that some data is sent or processed.

+

+  This function trims the sent data in the socket send buffer and signals the data

+  token, if proper.

+

+  @param[in, out]  Sock      Pointer to the socket.

+  @param[in]       Count     The length of the data processed or sent, in bytes.

+

+**/

+VOID

+SockDataSent (

+  IN OUT SOCKET     *Sock,

+  IN     UINT32     Count

+  );

+

+/**

+  Called by the low layer protocol to copy some data in socket send

+  buffer starting from the specific offset to a buffer provided by

+  the caller.

+

+  @param[in]  Sock                  Pointer to the socket.

+  @param[in]  Offset                The start point of the data to be copied.

+  @param[in]  Len                   The length of the data to be copied.

+  @param[out] Dest                  Pointer to the destination to copy the data.

+

+  @return The data size copied.

+

+**/

+UINT32

+SockGetDataToSend (

+  IN  SOCKET      *Sock,

+  IN  UINT32      Offset,

+  IN  UINT32      Len,

+  OUT UINT8       *Dest

+  );

+

+/**

+  Called by the low layer protocol to deliver received data to socket layer.

+

+  This function appends the data to the socket receive buffer, set the

+  urgent data length, then checks if any receive token can be signaled.

+

+  @param[in, out]  Sock       Pointer to the socket.

+  @param[in, out]  NetBuffer  Pointer to the buffer that contains the received data.

+  @param[in]       UrgLen     The length of the urgent data in the received data.

+

+**/

+VOID

+SockDataRcvd (

+  IN OUT SOCKET    *Sock,

+  IN OUT NET_BUF   *NetBuffer,

+  IN     UINT32    UrgLen

+  );

+

+/**

+  Get the length of the free space of the specific socket buffer.

+

+  @param[in]  Sock              Pointer to the socket.

+  @param[in]  Which             Flag to indicate which socket buffer to check:

+                                either send buffer or receive buffer.

+

+  @return The length of the free space, in bytes.

+

+**/

+UINT32

+SockGetFreeSpace (

+  IN SOCKET  *Sock,

+  IN UINT32  Which

+  );

+

+/**

+  Called by the low layer protocol to indicate that there will be no more data

+  from the communication peer.

+

+  This function sets the socket's state to SO_NO_MORE_DATA and signals all queued

+  IO tokens with the error status EFI_CONNECTION_FIN.

+

+  @param[in, out]  Sock                  Pointer to the socket.

+

+**/

+VOID

+SockNoMoreData (

+  IN OUT SOCKET *Sock

+  );

+

+//

+// Socket provided operations for user interface implemented in SockInterface.c

+//

+

+/**

+  Create a socket and its associated protocol control block

+  with the intial data SockInitData and protocol specific

+  data ProtoData.

+

+  @param[in]  SockInitData         Inital data to setting the socket.

+

+  @return Pointer to the newly created socket. If NULL, an error condition occured.

+

+**/

+SOCKET *

+SockCreateChild (

+  IN SOCK_INIT_DATA *SockInitData

+  );

+

+/**

+  Destory the socket Sock and its associated protocol control block.

+

+  @param[in, out]  Sock                 The socket to be destroyed.

+

+  @retval EFI_SUCCESS          The socket Sock was destroyed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.

+

+**/

+EFI_STATUS

+SockDestroyChild (

+  IN OUT SOCKET *Sock

+  );

+

+/**

+  Configure the specific socket Sock using configuration data ConfigData.

+

+  @param[in]  Sock             Pointer to the socket to be configured.

+  @param[in]  ConfigData       Pointer to the configuration data.

+

+  @retval EFI_SUCCESS          The socket configured successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is already configured.

+

+**/

+EFI_STATUS

+SockConfigure (

+  IN SOCKET *Sock,

+  IN VOID   *ConfigData

+  );

+

+/**

+  Initiate a connection establishment process.

+

+  @param[in]  Sock             Pointer to the socket to initiate the initate the

+                               connection.

+  @param[in]  Token            Pointer to the token used for the connection

+                               operation.

+

+  @retval EFI_SUCCESS          The connection initialized successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not configured to

+                               be an active one, or the token is already in one of

+                               this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockConnect (

+  IN SOCKET *Sock,

+  IN VOID   *Token

+  );

+

+/**

+  Issue a listen token to get an existed connected network instance,

+  or wait for a connection if there is none.

+

+  @param[in]  Sock             Pointer to the socket to accept connections.

+  @param[in]  Token            The token to accept a connection.

+

+  @retval EFI_SUCCESS          Either a connection is accepted or the Token is

+                               buffered for further acceptance.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not configured to

+                               be a passive one, or the token is already in one of

+                               this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the Token due to memory limit.

+

+**/

+EFI_STATUS

+SockAccept (

+  IN SOCKET *Sock,

+  IN VOID   *Token

+  );

+

+/**

+  Issue a token with data to the socket to send out.

+

+  @param[in]  Sock             Pointer to the socket to process the token with

+                               data.

+  @param[in]  Token            The token with data that needs to send out.

+

+  @retval EFI_SUCCESS          The token processed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not in a

+                               synchronized state , or the token is already in one

+                               of this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the token due to a memory limit.

+

+**/

+EFI_STATUS

+SockSend (

+  IN SOCKET *Sock,

+  IN VOID   *Token

+  );

+

+/**

+  Issue a token to get data from the socket.

+

+  @param[in]  Sock             Pointer to the socket to get data from.

+  @param[in]  Token            The token to store the received data from the

+                               socket.

+

+  @retval EFI_SUCCESS          The token processed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not in a

+                               synchronized state , or the token is already in one

+                               of this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+  @retval EFI_CONNECTION_FIN   The connection is closed and there is no more data.

+  @retval EFI_OUT_OF_RESOURCE  Failed to buffer the token due to a memory limit.

+

+**/

+EFI_STATUS

+SockRcv (

+  IN SOCKET *Sock,

+  IN VOID   *Token

+  );

+

+/**

+  Reset the socket and its associated protocol control block.

+

+  @param[in, out]  Sock        Pointer to the socket to be flushed.

+

+  @retval EFI_SUCCESS          The socket flushed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.

+

+**/

+EFI_STATUS

+SockFlush (

+  IN OUT SOCKET *Sock

+  );

+

+/**

+  Close or abort the socket associated connection.

+

+  @param[in, out]  Sock        Pointer to the socket of the connection to close

+                               or abort.

+  @param[in]  Token            The token for close operation.

+  @param[in]  OnAbort          TRUE for aborting the connection, FALSE to close it.

+

+  @retval EFI_SUCCESS          The close or abort operation initialized

+                               successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket, or the

+                               socket is closed, or the socket is not in a

+                               synchronized state , or the token is already in one

+                               of this socket's lists.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockClose (

+  IN OUT SOCKET  *Sock,

+  IN     VOID    *Token,

+  IN     BOOLEAN OnAbort

+  );

+

+/**

+  Get the mode data of the low layer protocol.

+

+  @param[in]       Sock        Pointer to the socket to get mode data from.

+  @param[in, out]  Mode        Pointer to the data to store the low layer mode

+                               information.

+

+  @retval EFI_SUCCESS          The mode data was obtained successfully.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockGetMode (

+  IN     SOCKET *Sock,

+  IN OUT VOID   *Mode

+  );

+

+/**

+  Configure the low level protocol to join a multicast group for

+  this socket's connection.

+

+  @param[in]  Sock             Pointer to the socket of the connection to join the

+                               specific multicast group.

+  @param[in]  GroupInfo        Pointer to the multicast group information.

+

+  @retval EFI_SUCCESS          The configuration completed successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockGroup (

+  IN SOCKET *Sock,

+  IN VOID   *GroupInfo

+  );

+

+/**

+  Add or remove route information in IP route table associated

+  with this socket.

+

+  @param[in]  Sock             Pointer to the socket associated with the IP route

+                               table to operate on.

+  @param[in]  RouteInfo        Pointer to the route information to be processed.

+

+  @retval EFI_SUCCESS          The route table updated successfully.

+  @retval EFI_ACCESS_DENIED    Failed to get the lock to access the socket.

+  @retval EFI_NO_MAPPING       The IP address configuration operation is  not

+                               finished.

+  @retval EFI_NOT_STARTED      The socket is not configured.

+

+**/

+EFI_STATUS

+SockRoute (

+  IN SOCKET    *Sock,

+  IN VOID      *RouteInfo

+  );

+

+#endif

diff --git a/NetworkPkg/TcpDxe/TcpDispatcher.c b/NetworkPkg/TcpDxe/TcpDispatcher.c
new file mode 100644
index 0000000..eaa75a4
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpDispatcher.c
@@ -0,0 +1,861 @@
+/** @file

+  The implementation of a dispatch routine for processing TCP requests.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+/**

+  Add or remove a route entry in the IP route table associated with this TCP instance.

+

+  @param[in]  Tcb               Pointer to the TCP_CB of this TCP instance.

+  @param[in]  RouteInfo         Pointer to the route information to be processed.

+

+  @retval EFI_SUCCESS           The operation completed successfully.

+  @retval EFI_NOT_STARTED       The driver instance has not been started.

+  @retval EFI_NO_MAPPING        When using the default address, configuration(DHCP,

+                                BOOTP, RARP, etc.) is not finished yet.

+  @retval EFI_OUT_OF_RESOURCES  Could not add the entry to the routing table.

+  @retval EFI_NOT_FOUND         This route is not in the routing table

+                                (when RouteInfo->DeleteRoute is TRUE).

+  @retval EFI_ACCESS_DENIED     The route is already defined in the routing table

+                                (when RouteInfo->DeleteRoute is FALSE).

+**/

+EFI_STATUS

+Tcp4Route (

+  IN TCP_CB           *Tcb,

+  IN TCP4_ROUTE_INFO  *RouteInfo

+  )

+{

+  IP_IO_IP_PROTOCOL   Ip;

+

+  Ip = Tcb->IpInfo->Ip;

+

+  ASSERT (Ip.Ip4!= NULL);

+

+  return Ip.Ip4->Routes (

+                   Ip.Ip4,

+                   RouteInfo->DeleteRoute,

+                   RouteInfo->SubnetAddress,

+                   RouteInfo->SubnetMask,

+                   RouteInfo->GatewayAddress

+                   );

+

+}

+

+/**

+  Get the operational settings of this TCPv4 instance.

+

+  @param[in]       Tcb           Pointer to the TCP_CB of this TCP instance.

+  @param[in, out]  Mode          Pointer to the buffer to store the operational

+                                 settings.

+

+  @retval EFI_SUCCESS            The mode data was read.

+  @retval EFI_NOT_STARTED        No configuration data is available because this

+                                 instance hasn't been started.

+

+**/

+EFI_STATUS

+Tcp4GetMode (

+  IN     TCP_CB         *Tcb,

+  IN OUT TCP4_MODE_DATA *Mode

+  )

+{

+  SOCKET                *Sock;

+  EFI_TCP4_CONFIG_DATA  *ConfigData;

+  EFI_TCP4_ACCESS_POINT *AccessPoint;

+  EFI_TCP4_OPTION       *Option;

+  EFI_IP4_PROTOCOL      *Ip;

+

+  Sock = Tcb->Sk;

+

+  if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (Mode->Tcp4State != NULL) {

+    *(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State;

+  }

+

+  if (Mode->Tcp4ConfigData != NULL) {

+

+    ConfigData                       = Mode->Tcp4ConfigData;

+    AccessPoint                      = &(ConfigData->AccessPoint);

+    Option                           = ConfigData->ControlOption;

+

+    ConfigData->TypeOfService        = Tcb->Tos;

+    ConfigData->TimeToLive           = Tcb->Ttl;

+

+    AccessPoint->UseDefaultAddress   = Tcb->UseDefaultAddr;

+

+    CopyMem (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));

+

+    AccessPoint->SubnetMask          = Tcb->SubnetMask;

+    AccessPoint->StationPort         = NTOHS (Tcb->LocalEnd.Port);

+

+    CopyMem (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));

+

+    AccessPoint->RemotePort          = NTOHS (Tcb->RemoteEnd.Port);

+    AccessPoint->ActiveFlag          = (BOOLEAN) (Tcb->State != TCP_LISTEN);

+

+    if (Option != NULL) {

+      Option->ReceiveBufferSize      = GET_RCV_BUFFSIZE (Tcb->Sk);

+      Option->SendBufferSize         = GET_SND_BUFFSIZE (Tcb->Sk);

+      Option->MaxSynBackLog          = GET_BACKLOG (Tcb->Sk);

+

+      Option->ConnectionTimeout      = Tcb->ConnectTimeout / TCP_TICK_HZ;

+      Option->DataRetries            = Tcb->MaxRexmit;

+      Option->FinTimeout             = Tcb->FinWait2Timeout / TCP_TICK_HZ;

+      Option->TimeWaitTimeout        = Tcb->TimeWaitTimeout / TCP_TICK_HZ;

+      Option->KeepAliveProbes        = Tcb->MaxKeepAlive;

+      Option->KeepAliveTime          = Tcb->KeepAliveIdle / TCP_TICK_HZ;

+      Option->KeepAliveInterval      = Tcb->KeepAlivePeriod / TCP_TICK_HZ;

+

+      Option->EnableNagle            = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));

+      Option->EnableTimeStamp        = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));

+      Option->EnableWindowScaling    = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));

+

+      Option->EnableSelectiveAck     = FALSE;

+      Option->EnablePathMtuDiscovery = FALSE;

+    }

+  }

+

+  Ip = Tcb->IpInfo->Ip.Ip4;

+  ASSERT (Ip != NULL);

+

+  return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData);

+}

+

+/**

+  Get the operational settings of this TCPv6 instance.

+

+  @param[in]       Tcb           Pointer to the TCP_CB of this TCP instance.

+  @param[in, out]  Mode          Pointer to the buffer to store the operational

+                                 settings.

+

+  @retval EFI_SUCCESS            The mode data was read.

+  @retval EFI_NOT_STARTED        No configuration data is available because this

+                                 instance hasn't been started.

+

+**/

+EFI_STATUS

+Tcp6GetMode (

+  IN     TCP_CB         *Tcb,

+  IN OUT TCP6_MODE_DATA *Mode

+  )

+{

+  SOCKET                *Sock;

+  EFI_TCP6_CONFIG_DATA  *ConfigData;

+  EFI_TCP6_ACCESS_POINT *AccessPoint;

+  EFI_TCP6_OPTION       *Option;

+  EFI_IP6_PROTOCOL      *Ip;

+

+  Sock = Tcb->Sk;

+

+  if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp6ConfigData != NULL)) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (Mode->Tcp6State != NULL) {

+    *(Mode->Tcp6State) = (EFI_TCP6_CONNECTION_STATE) (Tcb->State);

+  }

+

+  if (Mode->Tcp6ConfigData != NULL) {

+

+    ConfigData                       = Mode->Tcp6ConfigData;

+    AccessPoint                      = &(ConfigData->AccessPoint);

+    Option                           = ConfigData->ControlOption;

+

+    ConfigData->TrafficClass         = Tcb->Tos;

+    ConfigData->HopLimit             = Tcb->Ttl;

+

+    AccessPoint->StationPort         = NTOHS (Tcb->LocalEnd.Port);

+    AccessPoint->RemotePort          = NTOHS (Tcb->RemoteEnd.Port);

+    AccessPoint->ActiveFlag          = (BOOLEAN) (Tcb->State != TCP_LISTEN);

+

+    IP6_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip);

+    IP6_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip);

+

+    if (Option != NULL) {

+      Option->ReceiveBufferSize      = GET_RCV_BUFFSIZE (Tcb->Sk);

+      Option->SendBufferSize         = GET_SND_BUFFSIZE (Tcb->Sk);

+      Option->MaxSynBackLog          = GET_BACKLOG (Tcb->Sk);

+

+      Option->ConnectionTimeout      = Tcb->ConnectTimeout / TCP_TICK_HZ;

+      Option->DataRetries            = Tcb->MaxRexmit;

+      Option->FinTimeout             = Tcb->FinWait2Timeout / TCP_TICK_HZ;

+      Option->TimeWaitTimeout        = Tcb->TimeWaitTimeout / TCP_TICK_HZ;

+      Option->KeepAliveProbes        = Tcb->MaxKeepAlive;

+      Option->KeepAliveTime          = Tcb->KeepAliveIdle / TCP_TICK_HZ;

+      Option->KeepAliveInterval      = Tcb->KeepAlivePeriod / TCP_TICK_HZ;

+

+      Option->EnableNagle            = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));

+      Option->EnableTimeStamp        = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));

+      Option->EnableWindowScaling    = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));

+

+      Option->EnableSelectiveAck     = FALSE;

+      Option->EnablePathMtuDiscovery = FALSE;

+    }

+  }

+

+  Ip = Tcb->IpInfo->Ip.Ip6;

+  ASSERT (Ip != NULL);

+

+  return Ip->GetModeData (Ip, Mode->Ip6ModeData, Mode->MnpConfigData, Mode->SnpModeData);

+}

+

+/**

+  If TcpAp->StationPort isn't zero, check whether the access point

+  is registered, else generate a random station port for this

+  access point.

+

+  @param[in]  TcpAp              Pointer to the access point.

+  @param[in]  IpVersion          IP_VERSION_4 or IP_VERSION_6

+

+  @retval EFI_SUCCESS            The check passed or the port is assigned.

+  @retval EFI_INVALID_PARAMETER  The non-zero station port is already used.

+  @retval EFI_OUT_OF_RESOURCES   No port can be allocated.

+

+**/

+EFI_STATUS

+TcpBind (

+  IN TCP_ACCESS_POINT  *TcpAp,

+  IN UINT8             IpVersion

+  )

+{

+  BOOLEAN         Cycle;

+  EFI_IP_ADDRESS  Local;

+  UINT16          *Port;

+  UINT16          *RandomPort;

+

+  if (IpVersion == IP_VERSION_4) {

+    CopyMem (&Local, &TcpAp->Tcp4Ap.StationAddress, sizeof (EFI_IPv4_ADDRESS));

+    Port       = &TcpAp->Tcp4Ap.StationPort;

+    RandomPort = &mTcp4RandomPort;

+  } else {

+    IP6_COPY_ADDRESS (&Local, &TcpAp->Tcp6Ap.StationAddress);

+    Port       = &TcpAp->Tcp6Ap.StationPort;

+    RandomPort = &mTcp6RandomPort;

+  }

+

+  if (0 != *Port) {

+    //

+    // Check if a same endpoing is bound.

+    //

+    if (TcpFindTcbByPeer (&Local, *Port, IpVersion)) {

+

+      return EFI_INVALID_PARAMETER;

+    }

+  } else {

+    //

+    // generate a random port

+    //

+    Cycle = FALSE;

+

+    if (TCP_PORT_USER_RESERVED == *RandomPort) {

+      *RandomPort = TCP_PORT_KNOWN;

+    }

+

+    (*RandomPort)++;

+

+    while (TcpFindTcbByPeer (&Local, *RandomPort, IpVersion)) {

+      (*RandomPort)++;

+

+      if (*RandomPort <= TCP_PORT_KNOWN) {

+        if (Cycle) {

+          DEBUG (

+            (EFI_D_ERROR,

+            "TcpBind: no port can be allocated for this pcb\n")

+            );

+          return EFI_OUT_OF_RESOURCES;

+        }

+

+        *RandomPort = TCP_PORT_KNOWN + 1;

+

+        Cycle       = TRUE;

+      }

+    }

+

+    *Port = *RandomPort;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Flush the Tcb add its associated protocols.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB to be flushed.

+

+**/

+VOID

+TcpFlushPcb (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  SOCKET                    *Sock;

+  TCP_PROTO_DATA            *TcpProto;

+

+  IpIoConfigIp (Tcb->IpInfo, NULL);

+

+  Sock     = Tcb->Sk;

+  TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;

+

+  if (SOCK_IS_CONFIGURED (Sock)) {

+    RemoveEntryList (&Tcb->List);

+

+    if (Sock->DevicePath != NULL) {

+      //

+      // Uninstall the device path protocl.

+      //

+      gBS->UninstallProtocolInterface (

+             Sock->SockHandle,

+             &gEfiDevicePathProtocolGuid,

+             Sock->DevicePath

+             );

+

+      FreePool (Sock->DevicePath);

+      Sock->DevicePath = NULL;

+    }

+

+    TcpSetVariableData (TcpProto->TcpService);

+  }

+

+  NetbufFreeList (&Tcb->SndQue);

+  NetbufFreeList (&Tcb->RcvQue);

+  Tcb->State = TCP_CLOSED;

+}

+

+/**

+  Attach a Pcb to the socket.

+

+  @param[in]  Sk                 Pointer to the socket of this TCP instance.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_OUT_OF_RESOURCES   Failed due to resource limits.

+

+**/

+EFI_STATUS

+TcpAttachPcb (

+  IN SOCKET  *Sk

+  )

+{

+  TCP_CB          *Tcb;

+  TCP_PROTO_DATA  *ProtoData;

+  IP_IO           *IpIo;

+

+  Tcb = AllocateZeroPool (sizeof (TCP_CB));

+

+  if (Tcb == NULL) {

+

+    DEBUG ((EFI_D_ERROR, "TcpConfigurePcb: failed to allocate a TCB\n"));

+

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;

+  IpIo      = ProtoData->TcpService->IpIo;

+

+  //

+  // Create an IpInfo for this Tcb.

+  //

+  Tcb->IpInfo = IpIoAddIp (IpIo);

+  if (Tcb->IpInfo == NULL) {

+

+    FreePool (Tcb);

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  InitializeListHead (&Tcb->List);

+  InitializeListHead (&Tcb->SndQue);

+  InitializeListHead (&Tcb->RcvQue);

+

+  Tcb->State        = TCP_CLOSED;

+  Tcb->Sk           = Sk;

+  ProtoData->TcpPcb = Tcb;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Detach the Pcb of the socket.

+

+  @param[in, out]  Sk           Pointer to the socket of this TCP instance.

+

+**/

+VOID

+TcpDetachPcb (

+  IN OUT SOCKET    *Sk

+  )

+{

+  TCP_PROTO_DATA   *ProtoData;

+  TCP_CB           *Tcb;

+

+  ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;

+  Tcb       = ProtoData->TcpPcb;

+

+  ASSERT (Tcb != NULL);

+

+  TcpFlushPcb (Tcb);

+

+  IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo);

+

+  FreePool (Tcb);

+

+  ProtoData->TcpPcb = NULL;

+}

+

+/**

+  Configure the Pcb using CfgData.

+

+  @param[in]  Sk                 Pointer to the socket of this TCP instance.

+  @param[in]  CfgData            Pointer to the TCP configuration data.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_INVALID_PARAMETER  A same access point has been configured in

+                                 another TCP instance.

+  @retval EFI_OUT_OF_RESOURCES   Failed due to resource limits.

+

+**/

+EFI_STATUS

+TcpConfigurePcb (

+  IN SOCKET           *Sk,

+  IN TCP_CONFIG_DATA  *CfgData

+  )

+{

+  IP_IO_IP_CONFIG_DATA IpCfgData;

+  EFI_STATUS           Status;

+  EFI_TCP4_OPTION      *Option;

+  TCP_PROTO_DATA       *TcpProto;

+  TCP_CB               *Tcb;

+  TCP_ACCESS_POINT     *TcpAp;

+

+  ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL));

+

+  TcpProto = (TCP_PROTO_DATA *) Sk->ProtoReserved;

+  Tcb      = TcpProto->TcpPcb;

+

+  ASSERT (Tcb != NULL);

+

+  if (Sk->IpVersion == IP_VERSION_4) {

+    //

+    // Add Ip for send pkt to the peer

+    //

+    CopyMem (&IpCfgData.Ip4CfgData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA));

+    IpCfgData.Ip4CfgData.DefaultProtocol    = EFI_IP_PROTO_TCP;

+    IpCfgData.Ip4CfgData.TypeOfService      = CfgData->Tcp4CfgData.TypeOfService;

+    IpCfgData.Ip4CfgData.TimeToLive         = CfgData->Tcp4CfgData.TimeToLive;

+    IpCfgData.Ip4CfgData.UseDefaultAddress  = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;

+    IpCfgData.Ip4CfgData.SubnetMask         = CfgData->Tcp4CfgData.AccessPoint.SubnetMask;

+    IpCfgData.Ip4CfgData.ReceiveTimeout     = (UINT32) (-1);

+    CopyMem (

+      &IpCfgData.Ip4CfgData.StationAddress,

+      &CfgData->Tcp4CfgData.AccessPoint.StationAddress,

+      sizeof (EFI_IPv4_ADDRESS)

+      );

+

+  } else {

+    ASSERT (Sk->IpVersion == IP_VERSION_6);

+

+    CopyMem (&IpCfgData.Ip6CfgData, &mIp6IoDefaultIpConfigData, sizeof (EFI_IP6_CONFIG_DATA));

+    IpCfgData.Ip6CfgData.DefaultProtocol    = EFI_IP_PROTO_TCP;

+    IpCfgData.Ip6CfgData.TrafficClass       = CfgData->Tcp6CfgData.TrafficClass;

+    IpCfgData.Ip6CfgData.HopLimit           = CfgData->Tcp6CfgData.HopLimit;

+    IpCfgData.Ip6CfgData.ReceiveTimeout     = (UINT32) (-1);

+    IP6_COPY_ADDRESS (

+      &IpCfgData.Ip6CfgData.StationAddress,

+      &CfgData->Tcp6CfgData.AccessPoint.StationAddress

+      );

+    IP6_COPY_ADDRESS (

+      &IpCfgData.Ip6CfgData.DestinationAddress,

+      &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress

+      );

+  }

+

+  //

+  // Configure the IP instance this Tcb consumes.

+  //

+  Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData);

+  if (EFI_ERROR (Status)) {

+    goto OnExit;

+  }

+

+  if (Sk->IpVersion == IP_VERSION_4) {

+    //

+    // Get the default address information if the instance is configured to use default address.

+    //

+    CfgData->Tcp4CfgData.AccessPoint.StationAddress = IpCfgData.Ip4CfgData.StationAddress;

+    CfgData->Tcp4CfgData.AccessPoint.SubnetMask     = IpCfgData.Ip4CfgData.SubnetMask;

+

+    TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp4CfgData.AccessPoint;

+  } else {

+    IP6_COPY_ADDRESS (

+      &CfgData->Tcp6CfgData.AccessPoint.StationAddress,

+      &IpCfgData.Ip6CfgData.StationAddress

+      );

+

+    TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp6CfgData.AccessPoint;

+  }

+

+  //

+  // check if we can bind this endpoint in CfgData

+  //

+  Status = TcpBind (TcpAp, Sk->IpVersion);

+

+  if (EFI_ERROR (Status)) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "TcpConfigurePcb: Bind endpoint failed with %r\n",

+      Status)

+      );

+

+    goto OnExit;

+  }

+

+  //

+  // Initalize the operating information in this Tcb

+  //

+  ASSERT (Tcb->State == TCP_CLOSED &&

+    IsListEmpty (&Tcb->SndQue) &&

+    IsListEmpty (&Tcb->RcvQue));

+

+  TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);

+  Tcb->State            = TCP_CLOSED;

+

+  Tcb->SndMss           = 536;

+  Tcb->RcvMss           = TcpGetRcvMss (Sk);

+

+  Tcb->SRtt             = 0;

+  Tcb->Rto              = 3 * TCP_TICK_HZ;

+

+  Tcb->CWnd             = Tcb->SndMss;

+  Tcb->Ssthresh         = 0xffffffff;

+

+  Tcb->CongestState     = TCP_CONGEST_OPEN;

+

+  Tcb->KeepAliveIdle    = TCP_KEEPALIVE_IDLE_MIN;

+  Tcb->KeepAlivePeriod  = TCP_KEEPALIVE_PERIOD;

+  Tcb->MaxKeepAlive     = TCP_MAX_KEEPALIVE;

+  Tcb->MaxRexmit        = TCP_MAX_LOSS;

+  Tcb->FinWait2Timeout  = TCP_FIN_WAIT2_TIME;

+  Tcb->TimeWaitTimeout  = TCP_TIME_WAIT_TIME;

+  Tcb->ConnectTimeout   = TCP_CONNECT_TIME;

+

+  if (Sk->IpVersion == IP_VERSION_4) {

+    //

+    // initialize Tcb in the light of CfgData

+    //

+    Tcb->Ttl            = CfgData->Tcp4CfgData.TimeToLive;

+    Tcb->Tos            = CfgData->Tcp4CfgData.TypeOfService;

+

+    Tcb->UseDefaultAddr = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;

+

+    CopyMem (&Tcb->LocalEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.StationAddress, sizeof (IP4_ADDR));

+    Tcb->LocalEnd.Port  = HTONS (CfgData->Tcp4CfgData.AccessPoint.StationPort);

+    Tcb->SubnetMask     = CfgData->Tcp4CfgData.AccessPoint.SubnetMask;

+

+    CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.RemoteAddress, sizeof (IP4_ADDR));

+    Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.RemotePort);

+

+    Option              = CfgData->Tcp4CfgData.ControlOption;

+  } else {

+    Tcb->Ttl            = CfgData->Tcp6CfgData.HopLimit;

+    Tcb->Tos            = CfgData->Tcp6CfgData.TrafficClass;

+

+    IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.StationAddress);

+    Tcb->LocalEnd.Port  = HTONS (CfgData->Tcp6CfgData.AccessPoint.StationPort);

+

+    IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress);

+    Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.RemotePort);

+

+    //

+    // Type EFI_TCP4_OPTION and EFI_TCP6_OPTION are the same.

+    //

+    Option              = (EFI_TCP4_OPTION *) CfgData->Tcp6CfgData.ControlOption;

+  }

+

+  if (Option != NULL) {

+    SET_RCV_BUFFSIZE (

+      Sk,

+      (UINT32) (TCP_COMP_VAL (

+                  TCP_RCV_BUF_SIZE_MIN,

+                  TCP_RCV_BUF_SIZE,

+                  TCP_RCV_BUF_SIZE,

+                  Option->ReceiveBufferSize

+                  )

+               )

+      );

+    SET_SND_BUFFSIZE (

+      Sk,

+      (UINT32) (TCP_COMP_VAL (

+                  TCP_SND_BUF_SIZE_MIN,

+                  TCP_SND_BUF_SIZE,

+                  TCP_SND_BUF_SIZE,

+                  Option->SendBufferSize

+                  )

+               )

+      );

+

+    SET_BACKLOG (

+      Sk,

+      (UINT32) (TCP_COMP_VAL (

+                  TCP_BACKLOG_MIN,

+                  TCP_BACKLOG,

+                  TCP_BACKLOG,

+                  Option->MaxSynBackLog

+                  )

+               )

+      );

+

+    Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL (

+                                TCP_MAX_LOSS_MIN,

+                                TCP_MAX_LOSS,

+                                TCP_MAX_LOSS,

+                                Option->DataRetries

+                                );

+    Tcb->FinWait2Timeout = TCP_COMP_VAL (

+                              TCP_FIN_WAIT2_TIME,

+                              TCP_FIN_WAIT2_TIME_MAX,

+                              TCP_FIN_WAIT2_TIME,

+                              (UINT32) (Option->FinTimeout * TCP_TICK_HZ)

+                              );

+

+    if (Option->TimeWaitTimeout != 0) {

+      Tcb->TimeWaitTimeout = TCP_COMP_VAL (

+                               TCP_TIME_WAIT_TIME,

+                               TCP_TIME_WAIT_TIME_MAX,

+                               TCP_TIME_WAIT_TIME,

+                               (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ)

+                               );

+    } else {

+      Tcb->TimeWaitTimeout = 0;

+    }

+

+    if (Option->KeepAliveProbes != 0) {

+      TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);

+

+      Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL (

+                                    TCP_MAX_KEEPALIVE_MIN,

+                                    TCP_MAX_KEEPALIVE,

+                                    TCP_MAX_KEEPALIVE,

+                                    Option->KeepAliveProbes

+                                    );

+      Tcb->KeepAliveIdle = TCP_COMP_VAL (

+                             TCP_KEEPALIVE_IDLE_MIN,

+                             TCP_KEEPALIVE_IDLE_MAX,

+                             TCP_KEEPALIVE_IDLE_MIN,

+                             (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ)

+                             );

+      Tcb->KeepAlivePeriod = TCP_COMP_VAL (

+                               TCP_KEEPALIVE_PERIOD_MIN,

+                               TCP_KEEPALIVE_PERIOD,

+                               TCP_KEEPALIVE_PERIOD,

+                               (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ)

+                               );

+    }

+

+    Tcb->ConnectTimeout = TCP_COMP_VAL (

+                            TCP_CONNECT_TIME_MIN,

+                            TCP_CONNECT_TIME,

+                            TCP_CONNECT_TIME,

+                            (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ)

+                            );

+

+    if (!Option->EnableNagle) {

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE);

+    }

+

+    if (!Option->EnableTimeStamp) {

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS);

+    }

+

+    if (!Option->EnableWindowScaling) {

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS);

+    }

+  }

+

+  //

+  // The socket is bound, the <SrcIp, SrcPort, DstIp, DstPort> is

+  // determined, construct the IP device path and install it.

+  //

+  Status = TcpInstallDevicePath (Sk);

+  if (EFI_ERROR (Status)) {

+    goto OnExit;

+  }

+

+  //

+  // update state of Tcb and socket

+  //

+  if (((Sk->IpVersion == IP_VERSION_4) && !CfgData->Tcp4CfgData.AccessPoint.ActiveFlag) ||

+      ((Sk->IpVersion == IP_VERSION_6) && !CfgData->Tcp6CfgData.AccessPoint.ActiveFlag)

+      ) {

+

+    TcpSetState (Tcb, TCP_LISTEN);

+    SockSetState (Sk, SO_LISTENING);

+

+    Sk->ConfigureState = SO_CONFIGURED_PASSIVE;

+  } else {

+

+    Sk->ConfigureState = SO_CONFIGURED_ACTIVE;

+  }

+

+  if (Sk->IpVersion == IP_VERSION_6) {

+    Tcb->Tick          = TCP6_REFRESH_NEIGHBOR_TICK;

+  }

+

+  TcpInsertTcb (Tcb);

+

+OnExit:

+

+  return Status;

+}

+

+/**

+  The procotol handler provided to the socket layer, which is used to

+  dispatch the socket level requests by calling the corresponding

+  TCP layer functions.

+

+  @param[in]  Sock               Pointer to the socket of this TCP instance.

+  @param[in]  Request            The code of this operation request.

+  @param[in]  Data               Pointer to the operation specific data passed in

+                                 together with the operation request. This is an

+                                 optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS            The socket request completed successfully.

+  @retval other                  The error status returned by the corresponding TCP

+                                 layer function.

+

+**/

+EFI_STATUS

+TcpDispatcher (

+  IN SOCKET                  *Sock,

+  IN UINT8                   Request,

+  IN VOID                    *Data    OPTIONAL

+  )

+{

+  TCP_CB          *Tcb;

+  TCP_PROTO_DATA  *ProtoData;

+

+  ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved;

+  Tcb       = ProtoData->TcpPcb;

+

+  switch (Request) {

+  case SOCK_POLL:

+    if (Tcb->Sk->IpVersion == IP_VERSION_4) {

+      ProtoData->TcpService->IpIo->Ip.Ip4->Poll (ProtoData->TcpService->IpIo->Ip.Ip4);

+    } else {

+      ProtoData->TcpService->IpIo->Ip.Ip6->Poll (ProtoData->TcpService->IpIo->Ip.Ip6);

+    }

+

+    break;

+

+  case SOCK_CONSUMED:

+    //

+    // After user received data from socket buffer, socket will

+    // notify TCP using this message to give it a chance to send out

+    // window update information

+    //

+    ASSERT (Tcb != NULL);

+    TcpOnAppConsume (Tcb);

+    break;

+

+  case SOCK_SND:

+

+    ASSERT (Tcb != NULL);

+    TcpOnAppSend (Tcb);

+    break;

+

+  case SOCK_CLOSE:

+

+    TcpOnAppClose (Tcb);

+

+    break;

+

+  case SOCK_ABORT:

+

+    TcpOnAppAbort (Tcb);

+

+    break;

+

+  case SOCK_SNDPUSH:

+    Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk);

+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);

+

+    break;

+

+  case SOCK_SNDURG:

+    Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1;

+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);

+

+    break;

+

+  case SOCK_CONNECT:

+

+    TcpOnAppConnect (Tcb);

+

+    break;

+

+  case SOCK_ATTACH:

+

+    return TcpAttachPcb (Sock);

+

+    break;

+

+  case SOCK_FLUSH:

+

+    TcpFlushPcb (Tcb);

+

+    break;

+

+  case SOCK_DETACH:

+

+    TcpDetachPcb (Sock);

+

+    break;

+

+  case SOCK_CONFIGURE:

+

+    return TcpConfigurePcb (

+            Sock,

+            (TCP_CONFIG_DATA *) Data

+            );

+

+    break;

+

+  case SOCK_MODE:

+

+    ASSERT ((Data != NULL) && (Tcb != NULL));

+

+    if (Tcb->Sk->IpVersion == IP_VERSION_4) {

+

+      return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data);

+    } else {

+

+      return Tcp6GetMode (Tcb, (TCP6_MODE_DATA *) Data);

+    }

+

+    break;

+

+  case SOCK_ROUTE:

+

+    ASSERT ((Data != NULL) && (Tcb != NULL) && (Tcb->Sk->IpVersion == IP_VERSION_4));

+

+    return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data);

+

+  default:

+

+    return EFI_UNSUPPORTED;

+  }

+

+  return EFI_SUCCESS;

+}

diff --git a/NetworkPkg/TcpDxe/TcpDriver.c b/NetworkPkg/TcpDxe/TcpDriver.c
new file mode 100644
index 0000000..37e53af
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpDriver.c
@@ -0,0 +1,891 @@
+/** @file

+  The driver binding and service binding protocol for the TCP driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+UINT16                        mTcp4RandomPort;

+UINT16                        mTcp6RandomPort;

+

+TCP_HEARTBEAT_TIMER           mTcpTimer = {

+  NULL,

+  0

+};

+

+EFI_TCP4_PROTOCOL             gTcp4ProtocolTemplate = {

+  Tcp4GetModeData,

+  Tcp4Configure,

+  Tcp4Routes,

+  Tcp4Connect,

+  Tcp4Accept,

+  Tcp4Transmit,

+  Tcp4Receive,

+  Tcp4Close,

+  Tcp4Cancel,

+  Tcp4Poll

+};

+

+EFI_TCP6_PROTOCOL             gTcp6ProtocolTemplate = {

+  Tcp6GetModeData,

+  Tcp6Configure,

+  Tcp6Connect,

+  Tcp6Accept,

+  Tcp6Transmit,

+  Tcp6Receive,

+  Tcp6Close,

+  Tcp6Cancel,

+  Tcp6Poll

+};

+

+SOCK_INIT_DATA                mTcpDefaultSockData = {

+  SockStream,

+  SO_CLOSED,

+  NULL,

+  TCP_BACKLOG,

+  TCP_SND_BUF_SIZE,

+  TCP_RCV_BUF_SIZE,

+  IP_VERSION_4,

+  NULL,

+  TcpCreateSocketCallback,

+  TcpDestroySocketCallback,

+  NULL,

+  NULL,

+  0,

+  TcpDispatcher,

+  NULL,

+};

+

+EFI_DRIVER_BINDING_PROTOCOL   gTcpDriverBinding = {

+  TcpDriverBindingSupported,

+  TcpDriverBindingStart,

+  TcpDriverBindingStop,

+  0xa,

+  NULL,

+  NULL

+};

+

+EFI_SERVICE_BINDING_PROTOCOL  gTcpServiceBinding = {

+  TcpServiceBindingCreateChild,

+  TcpServiceBindingDestroyChild

+};

+

+

+/**

+  Create and start the heartbeat timer for the TCP driver.

+

+  @retval EFI_SUCCESS            The timer was successfully created and started.

+  @retval other                  The timer was not created.

+

+**/

+EFI_STATUS

+TcpCreateTimer (

+  VOID

+  )

+{

+  EFI_STATUS  Status;

+

+  Status = EFI_SUCCESS;

+

+  if (mTcpTimer.RefCnt == 0) {

+

+    Status = gBS->CreateEvent (

+                    EVT_TIMER | EVT_NOTIFY_SIGNAL,

+                    TPL_NOTIFY,

+                    TcpTicking,

+                    NULL,

+                    &mTcpTimer.TimerEvent

+                    );

+    if (!EFI_ERROR (Status)) {

+

+      Status = gBS->SetTimer (

+                      mTcpTimer.TimerEvent,

+                      TimerPeriodic,

+                      (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ)

+                      );

+    }

+  }

+

+  if (!EFI_ERROR (Status)) {

+

+    mTcpTimer.RefCnt++;

+  }

+

+  return Status;

+}

+

+/**

+  Stop and destroy the heartbeat timer for TCP driver.

+

+**/

+VOID

+TcpDestroyTimer (

+  VOID

+  )

+{

+  ASSERT (mTcpTimer.RefCnt > 0);

+

+  mTcpTimer.RefCnt--;

+

+  if (mTcpTimer.RefCnt > 0) {

+    return;

+  }

+

+  gBS->SetTimer (mTcpTimer.TimerEvent, TimerCancel, 0);

+  gBS->CloseEvent (mTcpTimer.TimerEvent);

+  mTcpTimer.TimerEvent = NULL;

+}

+

+/**

+  The entry point for Tcp driver, which is used to install Tcp driver on the ImageHandle.

+

+  @param[in]  ImageHandle   The firmware allocated handle for this driver image.

+  @param[in]  SystemTable   Pointer to the EFI system table.

+

+  @retval EFI_SUCCESS   The driver loaded.

+  @retval other         The driver did not load.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpDriverEntryPoint (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  )

+{

+  EFI_STATUS  Status;

+  UINT32      Seed;

+

+  //

+  // Install the TCP Driver Binding Protocol

+  //

+  Status = EfiLibInstallDriverBindingComponentName2 (

+             ImageHandle,

+             SystemTable,

+             &gTcpDriverBinding,

+             ImageHandle,

+             &gTcpComponentName,

+             &gTcpComponentName2

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Initialize ISS and random port.

+  //

+  Seed            = NetRandomInitSeed ();

+  mTcpGlobalIss   = NET_RANDOM (Seed) % mTcpGlobalIss;

+  mTcp4RandomPort = (UINT16) (TCP_PORT_KNOWN + (NET_RANDOM (Seed) % TCP_PORT_KNOWN));

+  mTcp6RandomPort = mTcp4RandomPort;

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Create a new TCP4 or TCP6 driver service binding protocol

+

+  @param[in]  Controller         Controller handle of device to bind driver to.

+  @param[in]  ImageHandle        The TCP driver's image handle.

+  @param[in]  IpVersion          IP_VERSION_4 or IP_VERSION_6.

+

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.

+  @retval EFI_SUCCESS            A new IP6 service binding private was created.

+

+**/

+EFI_STATUS

+TcpCreateService (

+  IN EFI_HANDLE  Controller,

+  IN EFI_HANDLE  Image,

+  IN UINT8       IpVersion

+  )

+{

+  EFI_STATUS         Status;

+  EFI_GUID           *IpServiceBindingGuid;

+  EFI_GUID           *TcpServiceBindingGuid;

+  TCP_SERVICE_DATA   *TcpServiceData;

+  IP_IO_OPEN_DATA    OpenData;

+

+  if (IpVersion == IP_VERSION_4) {

+    IpServiceBindingGuid  = &gEfiIp4ServiceBindingProtocolGuid;

+    TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;

+  } else {

+    IpServiceBindingGuid  = &gEfiIp6ServiceBindingProtocolGuid;

+    TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Controller,

+                  TcpServiceBindingGuid,

+                  NULL,

+                  Image,

+                  Controller,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+  if (!EFI_ERROR (Status)) {

+    return EFI_ALREADY_STARTED;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Controller,

+                  IpServiceBindingGuid,

+                  NULL,

+                  Image,

+                  Controller,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return EFI_UNSUPPORTED;

+  }

+

+  //

+  // Create the TCP service data.

+  //

+  TcpServiceData = AllocateZeroPool (sizeof (TCP_SERVICE_DATA));

+  if (TcpServiceData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  TcpServiceData->Signature            = TCP_DRIVER_SIGNATURE;

+  TcpServiceData->ControllerHandle     = Controller;

+  TcpServiceData->DriverBindingHandle  = Image;

+  TcpServiceData->IpVersion            = IpVersion;

+  CopyMem (

+    &TcpServiceData->ServiceBinding,

+    &gTcpServiceBinding,

+    sizeof (EFI_SERVICE_BINDING_PROTOCOL)

+    );

+

+  TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion);

+  if (TcpServiceData->IpIo == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_ERROR;

+  }

+

+

+  InitializeListHead (&TcpServiceData->SocketList);

+  ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));

+

+  if (IpVersion == IP_VERSION_4) {

+    CopyMem (

+      &OpenData.IpConfigData.Ip4CfgData,

+      &mIp4IoDefaultIpConfigData,

+      sizeof (EFI_IP4_CONFIG_DATA)

+      );

+    OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;

+  } else {

+    CopyMem (

+      &OpenData.IpConfigData.Ip6CfgData,

+      &mIp6IoDefaultIpConfigData,

+      sizeof (EFI_IP6_CONFIG_DATA)

+      );

+    OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;

+  }

+

+  OpenData.PktRcvdNotify  = TcpRxCallback;

+  Status                  = IpIoOpen (TcpServiceData->IpIo, &OpenData);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = TcpCreateTimer ();

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &Controller,

+                  TcpServiceBindingGuid,

+                  &TcpServiceData->ServiceBinding,

+                  NULL

+                  );

+  if (EFI_ERROR (Status)) {

+    TcpDestroyTimer ();

+

+    goto ON_ERROR;

+  }

+

+  TcpSetVariableData (TcpServiceData);

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  if (TcpServiceData->IpIo != NULL) {

+    IpIoDestroy (TcpServiceData->IpIo);

+  }

+

+  FreePool (TcpServiceData);

+

+  return Status;

+}

+

+/**

+  Destroy a TCP6 or TCP4 service binding instance. It will release all

+  the resources allocated by the instance.

+

+  @param[in]  Controller         Controller handle of device to bind driver to.

+  @param[in]  ImageHandle        The TCP driver's image handle.

+  @param[in]  NumberOfChildren    Number of Handles in ChildHandleBuffer. If number

+                                 of children is zero stop the entire bus driver.

+  @param[in]  IpVersion          IP_VERSION_4 or IP_VERSION_6

+

+  @retval EFI_SUCCESS            The resources used by the instance were cleaned up.

+  @retval Others                 Failed to clean up some of the resources.

+

+**/

+EFI_STATUS

+TcpDestroyService (

+  IN EFI_HANDLE  Controller,

+  IN EFI_HANDLE  ImageHandle,

+  IN UINTN       NumberOfChildren,

+  IN UINT8       IpVersion

+  )

+{

+  EFI_HANDLE                    NicHandle;

+  EFI_GUID                      *IpProtocolGuid;

+  EFI_GUID                      *ServiceBindingGuid;

+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;

+  TCP_SERVICE_DATA              *TcpServiceData;

+  EFI_STATUS                    Status;

+  SOCKET                        *Sock;

+

+  ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));

+

+  if (IpVersion == IP_VERSION_4) {

+    IpProtocolGuid     = &gEfiIp4ProtocolGuid;

+    ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;

+  } else {

+    IpProtocolGuid     = &gEfiIp6ProtocolGuid;

+    ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;

+  }

+

+  NicHandle = NetLibGetNicHandle (Controller, IpProtocolGuid);

+  if (NicHandle == NULL) {

+    return EFI_NOT_FOUND;

+  }

+

+  Status = gBS->OpenProtocol (

+                  NicHandle,

+                  ServiceBindingGuid,

+                  (VOID **) &ServiceBinding,

+                  ImageHandle,

+                  Controller,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  TcpServiceData = TCP_SERVICE_FROM_THIS (ServiceBinding);

+

+  if (NumberOfChildren == 0) {

+    //

+    // Uninstall TCP servicebinding protocol

+    //

+    gBS->UninstallMultipleProtocolInterfaces (

+           NicHandle,

+           ServiceBindingGuid,

+           ServiceBinding,

+           NULL

+           );

+

+    //

+    // Destroy the IpIO consumed by TCP driver

+    //

+    IpIoDestroy (TcpServiceData->IpIo);

+

+    //

+    // Destroy the heartbeat timer.

+    //

+    TcpDestroyTimer ();

+

+    //

+    // Clear the variable.

+    //

+    TcpClearVariableData (TcpServiceData);

+

+    //

+    // Release the TCP service data

+    //

+    FreePool (TcpServiceData);

+  } else {

+

+    while (!IsListEmpty (&TcpServiceData->SocketList)) {

+      Sock = NET_LIST_HEAD (&TcpServiceData->SocketList, SOCKET, Link);

+

+      ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle);

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Test to see if this driver supports ControllerHandle.

+

+  @param[in]  This                Protocol instance pointer.

+  @param[in]  ControllerHandle    Handle of device to test.

+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific

+                                  child device to start.

+

+  @retval EFI_SUCCESS             This driver supports this device.

+  @retval EFI_ALREADY_STARTED     This driver is already running on this device.

+  @retval other                   This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpDriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  )

+{

+  EFI_STATUS  Status;

+  BOOLEAN     IsTcp4Started;

+

+  //

+  // Test for the Tcp4ServiceBinding Protocol

+  //

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiTcp4ServiceBindingProtocolGuid,

+                  NULL,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    //

+    // Test for the Ip4ServiceBinding Protocol

+    //

+    Status = gBS->OpenProtocol (

+                    ControllerHandle,

+                    &gEfiIp4ServiceBindingProtocolGuid,

+                    NULL,

+                    This->DriverBindingHandle,

+                    ControllerHandle,

+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                    );

+    if (!EFI_ERROR (Status)) {

+      return EFI_SUCCESS;

+    }

+

+    IsTcp4Started = FALSE;

+  } else {

+    IsTcp4Started = TRUE;

+  }

+

+  //

+  // Check the Tcp6ServiceBinding Protocol

+  //

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiTcp6ServiceBindingProtocolGuid,

+                  NULL,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    //

+    // Test for the Ip6ServiceBinding Protocol

+    //

+    Status = gBS->OpenProtocol (

+                    ControllerHandle,

+                    &gEfiIp6ServiceBindingProtocolGuid,

+                    NULL,

+                    This->DriverBindingHandle,

+                    ControllerHandle,

+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                    );

+    if (!EFI_ERROR (Status)) {

+      return EFI_SUCCESS;

+    }

+  } else if (IsTcp4Started) {

+    return EFI_ALREADY_STARTED;

+  }

+

+  return EFI_UNSUPPORTED;

+}

+

+/**

+  Start this driver on ControllerHandle.

+

+  @param[in]  This                   Protocol instance pointer.

+  @param[in]  ControllerHandle       Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child

+                                     device to start.

+

+  @retval EFI_SUCCESS            The driver is added to ControllerHandle.

+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources to start the

+                                 driver.

+  @retval other                  The driver cannot be added to ControllerHandle.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpDriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  )

+{

+  EFI_STATUS  Tcp4Status;

+  EFI_STATUS  Tcp6Status;

+

+  Tcp4Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4);

+  if ((Tcp4Status == EFI_ALREADY_STARTED) || (Tcp4Status == EFI_UNSUPPORTED)) {

+    Tcp4Status = EFI_SUCCESS;

+  }

+

+  Tcp6Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6);

+  if ((Tcp6Status == EFI_ALREADY_STARTED) || (Tcp6Status == EFI_UNSUPPORTED)) {

+    Tcp6Status = EFI_SUCCESS;

+  }

+

+  if (!EFI_ERROR (Tcp4Status) || !EFI_ERROR (Tcp6Status)) {

+    return EFI_SUCCESS;

+  } else if (EFI_ERROR (Tcp4Status)) {

+    return Tcp4Status;

+  } else {

+    return Tcp6Status;

+  }

+}

+

+/**

+  Stop this driver on ControllerHandle.

+

+  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.

+  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must

+                                support a bus specific I/O protocol for the driver

+                                to use to stop the device.

+  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.

+  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL

+                                if NumberOfChildren is 0.

+

+  @retval EFI_SUCCESS           The device was stopped.

+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpDriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL

+  )

+{

+  EFI_STATUS  Tcp4Status;

+  EFI_STATUS  Tcp6Status;

+

+  Tcp4Status = TcpDestroyService (

+                 ControllerHandle,

+                 This->DriverBindingHandle,

+                 NumberOfChildren,

+                 IP_VERSION_4

+                 );

+

+  Tcp6Status = TcpDestroyService (

+                 ControllerHandle,

+                 This->DriverBindingHandle,

+                 NumberOfChildren,

+                 IP_VERSION_6

+                 );

+

+  if (EFI_ERROR (Tcp4Status) && EFI_ERROR (Tcp6Status)) {

+    return EFI_DEVICE_ERROR;

+  } else {

+    return EFI_SUCCESS;

+  }

+}

+

+/**

+  The Callback funtion called after the TCP socket was created.

+

+  @param[in]  This            Pointer to the socket just created

+  @param[in]  Context         Context of the socket

+

+  @retval EFI_SUCCESS         This protocol installed successfully.

+  @retval other               An error occured.

+

+**/

+EFI_STATUS

+TcpCreateSocketCallback (

+  IN SOCKET  *This,

+  IN VOID    *Context

+  )

+{

+  EFI_STATUS        Status;

+  TCP_SERVICE_DATA  *TcpServiceData;

+  EFI_GUID          *IpProtocolGuid;

+  VOID              *Ip;

+

+  if (This->IpVersion == IP_VERSION_4) {

+    IpProtocolGuid = &gEfiIp4ProtocolGuid;

+  } else {

+    IpProtocolGuid = &gEfiIp6ProtocolGuid;

+  }

+

+  TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;

+

+  //

+  // Open the default IP protocol of IP_IO BY_DRIVER.

+  //

+  Status = gBS->OpenProtocol (

+                  TcpServiceData->IpIo->ChildHandle,

+                  IpProtocolGuid,

+                  &Ip,

+                  TcpServiceData->DriverBindingHandle,

+                  This->SockHandle,

+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Open the device path on the handle where service binding resides on.

+  //

+  Status = gBS->OpenProtocol (

+                  TcpServiceData->ControllerHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  (VOID **) &This->ParentDevicePath,

+                  TcpServiceData->DriverBindingHandle,

+                  This->SockHandle,

+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                  );

+  if (EFI_ERROR (Status)) {

+    gBS->CloseProtocol (

+           TcpServiceData->IpIo->ChildHandle,

+           IpProtocolGuid,

+           TcpServiceData->DriverBindingHandle,

+           This->SockHandle

+           );

+  } else {

+    //

+    // Insert this socket into the SocketList.

+    //

+    InsertTailList (&TcpServiceData->SocketList, &This->Link);

+  }

+

+  return Status;

+}

+

+/**

+  The callback function called before the TCP socket was to be destroyed.

+

+  @param[in]  This                   The TCP socket to be destroyed.

+  @param[in]  Context                The context of the socket.

+

+**/

+VOID

+TcpDestroySocketCallback (

+  IN SOCKET  *This,

+  IN VOID    *Context

+  )

+{

+  TCP_SERVICE_DATA  *TcpServiceData;

+  EFI_GUID          *IpProtocolGuid;

+

+  if (This->IpVersion == IP_VERSION_4) {

+    IpProtocolGuid = &gEfiIp4ProtocolGuid;

+  } else {

+    IpProtocolGuid = &gEfiIp6ProtocolGuid;

+  }

+

+  TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;

+

+  //

+  // Remove this node from the list.

+  //

+  RemoveEntryList (&This->Link);

+

+  //

+  // Close the device path protocol

+  //

+  gBS->CloseProtocol (

+         TcpServiceData->ControllerHandle,

+         &gEfiDevicePathProtocolGuid,

+         TcpServiceData->DriverBindingHandle,

+         This->SockHandle

+         );

+

+  //

+  // Close the IP protocol.

+  //

+  gBS->CloseProtocol (

+         TcpServiceData->IpIo->ChildHandle,

+         IpProtocolGuid,

+         TcpServiceData->DriverBindingHandle,

+         This->SockHandle

+         );

+}

+

+/**

+  Creates a child handle with a set of TCP services.

+

+  The CreateChild() function installs a protocol on ChildHandle.

+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.

+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.

+

+  @param[in]      This          Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in, out] ChildHandle   Pointer to the handle of the child to create.

+                                If it is NULL, then a new handle is created.

+                                If it is a pointer to an existing UEFI handle,

+                                then the protocol is added to the existing UEFI handle.

+

+  @retval EFI_SUCCES            The protocol was added to ChildHandle.

+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.

+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create

+                                the child.

+  @retval other                 The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpServiceBindingCreateChild (

+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN OUT EFI_HANDLE                    *ChildHandle

+  )

+{

+  SOCKET            *Sock;

+  TCP_SERVICE_DATA  *TcpServiceData;

+  TCP_PROTO_DATA    TcpProto;

+  EFI_STATUS        Status;

+  EFI_TPL           OldTpl;

+

+  if (NULL == This || NULL == ChildHandle) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  Status              = EFI_SUCCESS;

+  TcpServiceData      = TCP_SERVICE_FROM_THIS (This);

+  TcpProto.TcpService = TcpServiceData;

+  TcpProto.TcpPcb     = NULL;

+

+  //

+  // Create a tcp instance with defualt Tcp default

+  // sock init data and TcpProto

+  //

+  mTcpDefaultSockData.ProtoData     = &TcpProto;

+  mTcpDefaultSockData.DataSize      = sizeof (TCP_PROTO_DATA);

+  mTcpDefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle;

+  mTcpDefaultSockData.IpVersion     = TcpServiceData->IpVersion;

+

+  if (TcpServiceData->IpVersion == IP_VERSION_4) {

+    mTcpDefaultSockData.Protocol = &gTcp4ProtocolTemplate;

+  } else {

+    mTcpDefaultSockData.Protocol = &gTcp6ProtocolTemplate;

+  }

+

+  Sock = SockCreateChild (&mTcpDefaultSockData);

+  if (NULL == Sock) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "TcpDriverBindingCreateChild: No resource to create a Tcp Child\n")

+      );

+

+    Status = EFI_OUT_OF_RESOURCES;

+  } else {

+    *ChildHandle = Sock->SockHandle;

+  }

+

+  mTcpDefaultSockData.ProtoData  = NULL;

+

+  gBS->RestoreTPL (OldTpl);

+  return Status;

+}

+

+/**

+  Destroys a child handle with a set of TCP services.

+

+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol

+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the

+  last protocol on ChildHandle, then ChildHandle is destroyed.

+

+  @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param  ChildHandle Handle of the child to be destroyed.

+

+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.

+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.

+  @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.

+  @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle

+                                because its services are being used.

+  @retval other                 The child handle was not destroyed.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    ChildHandle

+  )

+{

+  EFI_STATUS  Status;

+  VOID        *Tcp;

+  SOCKET      *Sock;

+  EFI_TPL     OldTpl;

+

+  if (NULL == This || NULL == ChildHandle) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // retrieve the Tcp4 protocol from ChildHandle

+  //

+  Status = gBS->OpenProtocol (

+                  ChildHandle,

+                  &gEfiTcp4ProtocolGuid,

+                  &Tcp,

+                  gTcpDriverBinding.DriverBindingHandle,

+                  ChildHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    //

+    // No Tcp4, try the Tcp6 protocol

+    //

+    Status = gBS->OpenProtocol (

+                    ChildHandle,

+                    &gEfiTcp6ProtocolGuid,

+                    &Tcp,

+                    gTcpDriverBinding.DriverBindingHandle,

+                    ChildHandle,

+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                    );

+    if (EFI_ERROR (Status)) {

+      Status = EFI_UNSUPPORTED;

+    }

+  }

+

+  if (!EFI_ERROR (Status)) {

+    //

+    // destroy this sock and related Tcp protocol control

+    // block

+    //

+    Sock = SOCK_FROM_THIS (Tcp);

+

+    SockDestroyChild (Sock);

+  }

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

diff --git a/NetworkPkg/TcpDxe/TcpDriver.h b/NetworkPkg/TcpDxe/TcpDriver.h
new file mode 100644
index 0000000..9de4be6
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpDriver.h
@@ -0,0 +1,230 @@
+/** @file

+  The prototype of driver binding and service binding protocol for TCP driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _TCP_DRIVER_H_

+#define _TCP_DRIVER_H_

+

+#define TCP_DRIVER_SIGNATURE   SIGNATURE_32 ('T', 'C', 'P', 'D')

+

+#define TCP_PORT_KNOWN         1024

+#define TCP_PORT_USER_RESERVED 65535

+

+typedef struct _TCP_HEARTBEAT_TIMER {

+  EFI_EVENT  TimerEvent;

+  INTN       RefCnt;

+} TCP_HEARTBEAT_TIMER;

+

+typedef struct _TCP_SERVICE_DATA {

+  UINT32                        Signature;

+  EFI_HANDLE                    ControllerHandle;

+  EFI_HANDLE                    DriverBindingHandle;

+  UINT8                         IpVersion;

+  IP_IO                         *IpIo;

+  EFI_SERVICE_BINDING_PROTOCOL  ServiceBinding;

+  CHAR16                        *MacString;

+  LIST_ENTRY                    SocketList;

+} TCP_SERVICE_DATA;

+

+typedef struct _TCP_PROTO_DATA {

+  TCP_SERVICE_DATA  *TcpService;

+  TCP_CB            *TcpPcb;

+} TCP_PROTO_DATA;

+

+#define TCP_SERVICE_FROM_THIS(a) \

+  CR ( \

+  (a), \

+  TCP_SERVICE_DATA, \

+  ServiceBinding, \

+  TCP_DRIVER_SIGNATURE \

+  )

+

+//

+// Function prototype for the driver's entry point

+//

+

+/**

+  The entry point for Tcp driver, used to install Tcp driver on the ImageHandle.

+

+  @param[in]  ImageHandle   The firmware allocated handle for this driver image.

+  @param[in]  SystemTable   Pointer to the EFI system table.

+

+  @retval EFI_SUCCESS   The driver loaded.

+  @retval other         The driver did not load.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpDriverEntryPoint (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  );

+

+//

+// Function prototypes for the Driver Binding Protocol

+//

+

+/**

+  Test to see if this driver supports ControllerHandle.

+

+  @param[in]  This                Protocol instance pointer.

+  @param[in]  ControllerHandle    Handle of the device to test.

+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific

+                                  child device to start.

+

+  @retval EFI_SUCCESS             This driver supports this device.

+  @retval EFI_ALREADY_STARTED     This driver is already running on this device.

+  @retval other                   This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpDriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+/**

+  Start this driver on ControllerHandle.

+

+  @param[in]  This                   Protocol instance pointer.

+  @param[in]  ControllerHandle       Handle of device to bind driver to.

+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child

+                                     device to start.

+

+  @retval EFI_SUCCESS            The driver was added to ControllerHandle.

+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources to start the

+                                 driver.

+  @retval other                  The driver cannot be added to ControllerHandle.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpDriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+/**

+  Stop this driver on ControllerHandle.

+

+  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.

+  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must

+                                support a bus specific I/O protocol for the driver

+                                to use to stop the device.

+  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.

+  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL

+                                if NumberOfChildren is 0.

+

+  @retval EFI_SUCCESS           The device was stopped.

+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpDriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL

+  );

+

+/**

+  The Callback funtion called after the TCP socket is created.

+

+  @param[in]  This            Pointer to the socket just created.

+  @param[in]  Context         The context of the socket.

+

+  @retval EFI_SUCCESS         This protocol is installed successfully.

+  @retval other               An error occured.

+

+**/

+EFI_STATUS

+TcpCreateSocketCallback (

+  IN SOCKET  *This,

+  IN VOID    *Context

+  );

+

+/**

+  The callback function called before the TCP socket is to be destroyed.

+

+  @param[in]  This                   The TCP socket to be destroyed.

+  @param[in]  Context                The context of the socket.

+

+**/

+VOID

+TcpDestroySocketCallback (

+  IN SOCKET  *This,

+  IN VOID    *Context

+  );

+

+//

+// Function ptototypes for the ServiceBinding Prococol

+//

+

+/**

+  Creates a child handle with a set of TCP services.

+

+  The CreateChild() function installs a protocol on ChildHandle.

+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.

+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.

+

+  @param[in]      This          Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in, out] ChildHandle   Pointer to the handle of the child to create.

+                                If it is NULL, then a new handle is created.

+                                If it is a pointer to an existing UEFI handle,

+                                then the protocol is added to the existing UEFI handle.

+

+  @retval EFI_SUCCES            The protocol was added to ChildHandle.

+  @retval EFI_INVALID_PARAMETER ChildHandle is NULL.

+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources available to create

+                                the child.

+  @retval other                 The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpServiceBindingCreateChild (

+  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN OUT EFI_HANDLE                    *ChildHandle

+  );

+

+/**

+  Destroys a child handle with a set of TCP services.

+

+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol

+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the

+  last protocol on ChildHandle, then ChildHandle is destroyed.

+

+  @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param  ChildHandle Handle of the child to destroy.

+

+  @retval EFI_SUCCES            The protocol was removed from ChildHandle.

+  @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.

+  @retval EFI_INVALID_PARAMETER The child handle is not a valid UEFI Handle.

+  @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle

+                                because its services are being used.

+  @retval other                 The child handle was not destroyed.

+

+**/

+EFI_STATUS

+EFIAPI

+TcpServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    ChildHandle

+  );

+

+#endif

diff --git a/NetworkPkg/TcpDxe/TcpDxe.inf b/NetworkPkg/TcpDxe/TcpDxe.inf
new file mode 100644
index 0000000..6761538
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpDxe.inf
@@ -0,0 +1,83 @@
+## @file TcpDxe.inf

+#  Component description file for Tcp module.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = TcpDxe

+  FILE_GUID                      = 1A7E4468-2F55-4a56-903C-01265EB7622B

+  MODULE_TYPE                    = UEFI_DRIVER

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = TcpDriverEntryPoint

+  UNLOAD_IMAGE                   = NetLibDefaultUnload

+

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC

+#

+

+[Sources]

+  TcpDriver.c

+  SockImpl.c

+  SockInterface.c

+  TcpDispatcher.c

+  TcpOutput.c

+  TcpMain.c

+  SockImpl.h

+  TcpMisc.c

+  TcpProto.h

+  TcpOption.c

+  TcpInput.c

+  TcpFunc.h

+  TcpOption.h

+  TcpTimer.c

+  TcpMain.h

+  Socket.h

+  ComponentName.c

+  TcpIo.c

+  TcpDriver.h

+

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+

+

+[LibraryClasses]

+  BaseLib

+  BaseMemoryLib

+  DevicePathLib

+  DebugLib

+  MemoryAllocationLib

+  UefiLib

+  UefiBootServicesTableLib

+  UefiDriverEntryPoint

+  UefiRuntimeServicesTableLib

+  DpcLib

+  NetLib

+  IpIoLib

+

+

+[Protocols]

+  gEfiDevicePathProtocolGuid                    # PROTOCOL ALWAYS_CONSUMED

+  gEfiIp4ProtocolGuid                           # PROTOCOL ALWAYS_CONSUMED

+  gEfiIp4ServiceBindingProtocolGuid             # PROTOCOL ALWAYS_CONSUMED

+  gEfiTcp4ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED

+  gEfiTcp4ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED

+  gEfiIp6ProtocolGuid                           # PROTOCOL ALWAYS_CONSUMED

+  gEfiIp6ServiceBindingProtocolGuid             # PROTOCOL ALWAYS_CONSUMED

+  gEfiTcp6ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED

+  gEfiTcp6ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED

+

diff --git a/NetworkPkg/TcpDxe/TcpFunc.h b/NetworkPkg/TcpDxe/TcpFunc.h
new file mode 100644
index 0000000..c23ba94
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpFunc.h
@@ -0,0 +1,724 @@
+/** @file

+  Declaration of external functions shared in TCP driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _TCP_FUNC_H_

+#define _TCP_FUNC_H_

+

+#include "TcpOption.h"

+

+#define TCP_COMP_VAL(Min, Max, Default, Val) \

+  ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default))

+

+/**

+  Timeout handler prototype.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+typedef

+VOID

+(*TCP_TIMER_HANDLER) (

+  IN OUT TCP_CB *Tcb

+  );

+

+//

+// Functions in TcpMisc.c

+//

+

+/**

+  Initialize the Tcb locally related members.

+

+  @param[in, out]  Tcb               Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpInitTcbLocal (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Initialize the peer related members.

+

+  @param[in, out]  Tcb    Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Seg    Pointer to the segment that contains the peer's intial information.

+  @param[in]       Opt    Pointer to the options announced by the peer.

+

+**/

+VOID

+TcpInitTcbPeer (

+  IN OUT TCP_CB     *Tcb,

+  IN     TCP_SEG    *Seg,

+  IN     TCP_OPTION *Opt

+  );

+

+/**

+  Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.

+

+  @param[in]  Addr     Pointer to the IP address needs to match.

+  @param[in]  Port     The port number needs to match.

+  @param[in]  Version  IP_VERSION_4 indicates TCP is running on IP4 stack.

+                       IP_VERSION_6 indicates TCP is running on IP6 stack.

+

+

+  @retval     TRUE     The Tcb which matches the <Addr Port> pairs exists.

+  @retval     FALSE    Otherwise

+

+**/

+BOOLEAN

+TcpFindTcbByPeer (

+  IN EFI_IP_ADDRESS  *Addr,

+  IN TCP_PORTNO      Port,

+  IN UINT8           Version

+  );

+

+/**

+  Locate the TCP_CB related to the socket pair.

+

+  @param[in]  LocalPort      The local port number.

+  @param[in]  LocalIp        The local IP address.

+  @param[in]  RemotePort     The remote port number.

+  @param[in]  RemoteIp       The remote IP address.

+  @param[in]  Version        IP_VERSION_4 indicates TCP is running on IP4 stack,

+                             IP_VERSION_6 indicates TCP is running on IP6 stack.

+  @param[in]  Syn            If TRUE, the listen sockets are searched.

+

+  @return Pointer to the related TCP_CB. If NULL, no match is found.

+

+**/

+TCP_CB *

+TcpLocateTcb (

+  IN TCP_PORTNO      LocalPort,

+  IN EFI_IP_ADDRESS  *LocalIp,

+  IN TCP_PORTNO      RemotePort,

+  IN EFI_IP_ADDRESS  *RemoteIp,

+  IN UINT8           Version,

+  IN BOOLEAN         Syn

+  );

+

+/**

+  Insert a Tcb into the proper queue.

+

+  @param[in]  Tcb               Pointer to the TCP_CB to be inserted.

+

+  @retval 0                     The Tcb was inserted successfully.

+  @retval -1                    An error condition occurred.

+

+**/

+INTN

+TcpInsertTcb (

+  IN TCP_CB *Tcb

+  );

+

+/**

+  Clone a TCP_CB from Tcb.

+

+  @param[in]  Tcb                   Pointer to the TCP_CB to be cloned.

+

+  @return Pointer to the new cloned TCP_CB. If NULL, an error condition occurred.

+

+**/

+TCP_CB *

+TcpCloneTcb (

+  IN TCP_CB *Tcb

+  );

+

+/**

+  Compute an ISS to be used by a new connection.

+

+  @return The result ISS.

+

+**/

+TCP_SEQNO

+TcpGetIss (

+  VOID

+  );

+

+/**

+  Get the local mss.

+

+  @param[in]  Sock        Pointer to the socket to get mss.

+

+  @return The mss size.

+

+**/

+UINT16

+TcpGetRcvMss (

+  IN SOCKET  *Sock

+  );

+

+/**

+  Set the Tcb's state.

+

+  @param[in]  Tcb                   Pointer to the TCP_CB of this TCP instance.

+  @param[in]  State                 The state to be set.

+

+**/

+VOID

+TcpSetState (

+  IN TCP_CB *Tcb,

+  IN UINT8  State

+  );

+

+/**

+  Compute the TCP segment's checksum.

+

+  @param[in]  Nbuf       Pointer to the buffer that contains the TCP segment.

+  @param[in]  HeadSum    The checksum value of the fixed part of pseudo header.

+

+  @return The checksum value.

+

+**/

+UINT16

+TcpChecksum (

+  IN NET_BUF *Nbuf,

+  IN UINT16  HeadSum

+  );

+

+/**

+  Translate the information from the head of the received TCP

+  segment Nbuf contains, and fill it into a TCP_SEG structure.

+

+  @param[in]       Tcb           Pointer to the TCP_CB of this TCP instance.

+  @param[in, out]  Nbuf          Pointer to the buffer contains the TCP segment.

+

+  @return Pointer to the TCP_SEG that contains the translated TCP head information.

+

+**/

+TCP_SEG *

+TcpFormatNetbuf (

+  IN     TCP_CB  *Tcb,

+  IN OUT NET_BUF *Nbuf

+  );

+

+/**

+  Initialize an active connection,

+

+  @param[in, out]  Tcb          Pointer to the TCP_CB that wants to initiate a

+                                connection.

+

+**/

+VOID

+TcpOnAppConnect (

+  IN OUT TCP_CB  *Tcb

+  );

+

+/**

+  Application has consumed some data, check whether

+  to send a window update ack or a delayed ack.

+

+  @param[in]  Tcb        Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpOnAppConsume (

+  IN TCP_CB *Tcb

+  );

+

+/**

+  Initiate the connection close procedure, called when

+  applications want to close the connection.

+

+  @param[in, out]  Tcb          Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpOnAppClose (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Check whether the application's newly delivered data can be sent out.

+

+  @param[in, out]  Tcb          Pointer to the TCP_CB of this TCP instance.

+

+  @retval 0                     The data has been sent out successfully.

+  @retval -1                    The Tcb is not in a state that data is permitted to

+                                be sent out.

+

+**/

+INTN

+TcpOnAppSend (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Abort the connection by sending a reset segment: called

+  when the application wants to abort the connection.

+

+  @param[in]  Tcb                   Pointer to the TCP_CB of the TCP instance.

+

+**/

+VOID

+TcpOnAppAbort (

+  IN TCP_CB *Tcb

+  );

+

+/**

+  Reset the connection related with Tcb.

+

+  @param[in]  Tcb         Pointer to the TCP_CB of the connection to be reset.

+

+**/

+VOID

+TcpResetConnection (

+  IN TCP_CB *Tcb

+  );

+

+/**

+  Set the Tcp variable data.

+

+  @param[in]  TcpService        Tcp service data.

+

+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.

+  @retval other                 Set variable failed.

+

+**/

+EFI_STATUS

+TcpSetVariableData (

+  IN TCP_SERVICE_DATA  *TcpService

+  );

+

+/**

+  Clear the variable and free the resource.

+

+  @param[in]  TcpService            Tcp service data.

+

+**/

+VOID

+TcpClearVariableData (

+  IN TCP_SERVICE_DATA  *TcpService

+  );

+

+/**

+  Install the device path protocol on the TCP instance.

+

+  @param[in]  Sock          Pointer to the socket representing the TCP instance.

+

+  @retval EFI_SUCCESS           The device path protocol installed.

+  @retval other                 Failed to install the device path protocol.

+

+**/

+EFI_STATUS

+TcpInstallDevicePath (

+  IN SOCKET *Sock

+  );

+

+

+//

+// Functions in TcpOutput.c

+//

+

+/**

+  Compute the sequence space left in the old receive window.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+  @return The sequence space left in the old receive window.

+

+**/

+UINT32

+TcpRcvWinOld (

+  IN TCP_CB *Tcb

+  );

+

+/**

+  Compute the current receive window.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+  @return The size of the current receive window, in bytes.

+

+**/

+UINT32

+TcpRcvWinNow (

+  IN TCP_CB *Tcb

+  );

+

+/**

+  Get the maximum SndNxt.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+  @return The sequence number of the maximum SndNxt.

+

+**/

+TCP_SEQNO

+TcpGetMaxSndNxt (

+  IN TCP_CB *Tcb

+  );

+

+/**

+  Compute how much data to send.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Force   If TRUE, ignore the sender's SWS avoidance algorithm

+                      and send out data by force.

+

+  @return The length of the data that can be sent. If 0, no data can be sent.

+

+**/

+UINT32

+TcpDataToSend (

+  IN TCP_CB *Tcb,

+  IN INTN   Force

+  );

+

+/**

+  Retransmit the segment from sequence Seq.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Seq     The sequence number of the segment to be retransmitted.

+

+  @retval 0       The retransmission succeeded.

+  @retval -1      An error condition occurred.

+

+**/

+INTN

+TcpRetransmit (

+  IN TCP_CB    *Tcb,

+  IN TCP_SEQNO Seq

+  );

+

+/**

+  Check whether to send data/SYN/FIN and piggyback an ACK.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Force   If TRUE, ignore the sender's SWS avoidance algorithm

+                           and send out data by force.

+

+  @return The number of bytes sent.

+

+**/

+INTN

+TcpToSendData (

+  IN OUT TCP_CB *Tcb,

+  IN     INTN   Force

+  );

+

+/**

+  Check whether to send an ACK or delayed ACK.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpToSendAck (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Send an ACK immediately.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpSendAck (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Send a zero probe segment. It can be used by keepalive and zero window probe.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+  @retval 0       The zero probe segment was sent out successfully.

+  @retval other   An error condition occurred.

+

+**/

+INTN

+TcpSendZeroProbe (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Send a RESET segment in response to the segment received.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance, may be NULL.

+  @param[in]  Head    TCP header of the segment that triggers the reset.

+  @param[in]  Len     Length of the segment that triggers the reset.

+  @param[in]  Local   Local IP address.

+  @param[in]  Remote  Remote peer's IP address.

+  @param[in]  Version IP_VERSION_4 indicates TCP is running on IP4 stack,

+                      IP_VERSION_6 indicates TCP is running on IP6 stack.

+

+  @retval     0       A reset is sent or no need to send it.

+  @retval     -1      No reset is sent.

+

+**/

+INTN

+TcpSendReset (

+  IN TCP_CB          *Tcb,

+  IN TCP_HEAD        *Head,

+  IN INT32           Len,

+  IN EFI_IP_ADDRESS  *Local,

+  IN EFI_IP_ADDRESS  *Remote,

+  IN UINT8           Version

+  );

+

+/**

+  Verify that the segment is in good shape.

+

+  @param[in]  Nbuf    Buffer that contains the segment to be checked.

+

+  @retval     0       The segment is broken.

+  @retval     1       The segment is in good shape.

+

+**/

+INTN

+TcpVerifySegment (

+  IN NET_BUF *Nbuf

+  );

+

+//

+// Functions from TcpInput.c

+//

+

+/**

+  Process the received ICMP error messages for TCP.

+

+  @param[in]  Nbuf     Buffer that contains part of the TCP segment without IP header

+                       truncated from the ICMP error packet.

+  @param[in]  IcmpErr  The ICMP error code interpreted from an ICMP error packet.

+  @param[in]  Src      Source address of the ICMP error message.

+  @param[in]  Dst      Destination address of the ICMP error message.

+  @param[in]  Version  IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates

+                       IP6 stack.

+

+**/

+VOID

+TcpIcmpInput (

+  IN NET_BUF         *Nbuf,

+  IN UINT8           IcmpErr,

+  IN EFI_IP_ADDRESS  *Src,

+  IN EFI_IP_ADDRESS  *Dst,

+  IN UINT8           Version

+  );

+

+/**

+  Process the received TCP segments.

+

+  @param[in]  Nbuf     Buffer that contains received TCP segment without an IP header.

+  @param[in]  Src      Source address of the segment, or the peer's IP address.

+  @param[in]  Dst      Destination address of the segment, or the local end's IP

+                       address.

+  @param[in]  Version  IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates

+                       IP6 stack.

+

+  @retval 0        The segment processed successfully. It is either accepted or

+                   discarded. But no connection is reset by the segment.

+  @retval -1       A connection is reset by the segment.

+

+**/

+INTN

+TcpInput (

+  IN NET_BUF         *Nbuf,

+  IN EFI_IP_ADDRESS  *Src,

+  IN EFI_IP_ADDRESS  *Dst,

+  IN UINT8           Version

+  );

+

+//

+// Functions in TcpTimer.c

+//

+

+/**

+  Close the TCP connection.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpClose (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Heart beat timer handler, queues the DPC at TPL_CALLBACK.

+

+  @param[in]  Event    Timer event signaled, ignored.

+  @param[in]  Context  Context of the timer event, ignored.

+

+**/

+VOID

+EFIAPI

+TcpTicking (

+  IN EFI_EVENT Event,

+  IN VOID      *Context

+  );

+

+/**

+  Enable a TCP timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Timer    The index of the timer to be enabled.

+  @param[in]       TimeOut  The timeout value of this timer.

+

+**/

+VOID

+TcpSetTimer (

+  IN OUT TCP_CB *Tcb,

+  IN     UINT16 Timer,

+  IN     UINT32 TimeOut

+  );

+

+/**

+  Clear one TCP timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Timer    The index of the timer to be cleared.

+

+**/

+VOID

+TcpClearTimer (

+  IN OUT TCP_CB *Tcb,

+  IN     UINT16 Timer

+  );

+

+/**

+  Clear all TCP timers.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpClearAllTimer (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Enable the window prober timer and set the timeout value.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpSetProbeTimer (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Enable the keepalive timer and set the timeout value.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpSetKeepaliveTimer (

+  IN OUT TCP_CB *Tcb

+  );

+

+//

+// Functions in TcpIo.c

+//

+

+/**

+  Packet receive callback function provided to IP_IO. Used to call

+  the proper function to handle the packet received by IP.

+

+  @param[in] Status        Result of the receive request.

+  @param[in] IcmpErr       Valid when Status is EFI_ICMP_ERROR.

+  @param[in] NetSession    The IP session for the received packet.

+  @param[in] Pkt           Packet received.

+  @param[in] Context       The data provided by the user for the received packet when

+                           the callback is registered in IP_IO_OPEN_DATA::RcvdContext.

+                           This is an optional parameter that may be NULL.

+

+**/

+VOID

+EFIAPI

+TcpRxCallback (

+  IN EFI_STATUS                       Status,

+  IN UINT8                            IcmpErr,

+  IN EFI_NET_SESSION_DATA             *NetSession,

+  IN NET_BUF                          *Pkt,

+  IN VOID                             *Context    OPTIONAL

+  );

+

+/**

+  Send the segment to IP via IpIo function.

+

+  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Nbuf               Pointer to the TCP segment to be sent.

+  @param[in]  Src                Source address of the TCP segment.

+  @param[in]  Dest               Destination address of the TCP segment.

+  @param[in]  Version            IP_VERSION_4 or IP_VERSION_6

+

+  @retval 0                      The segment was sent out successfully.

+  @retval -1                     The segment failed to be sent.

+

+**/

+INTN

+TcpSendIpPacket (

+  IN TCP_CB          *Tcb,

+  IN NET_BUF         *Nbuf,

+  IN EFI_IP_ADDRESS  *Src,

+  IN EFI_IP_ADDRESS  *Dest,

+  IN UINT8           Version

+  );

+

+/**

+  Refresh the remote peer's Neighbor Cache State if already exists.

+

+  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Neighbor           Source address of the TCP segment.

+  @param[in]  Timeout            Time in 100-ns units that this entry will remain

+                                 in the neighbor cache. A value of zero means that

+                                 the entry  is permanent. A value of non-zero means

+                                 that the entry is  dynamic and will be deleted

+                                 after Timeout.

+

+  @retval EFI_SUCCESS            Successfully updated the neighbor relationship.

+  @retval EFI_NOT_STARTED        The IpIo is not configured.

+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.

+  @retval EFI_NOT_FOUND          This entry is not in the neighbor table.

+

+**/

+EFI_STATUS

+Tcp6RefreshNeighbor (

+  IN TCP_CB          *Tcb,

+  IN EFI_IP_ADDRESS  *Neighbor,

+  IN UINT32          Timeout

+  );

+

+//

+// Functions in TcpDispatcher.c

+//

+

+/**

+  The procotol handler provided to the socket layer, used to

+  dispatch the socket level requests by calling the corresponding

+  TCP layer functions.

+

+  @param[in]  Sock               Pointer to the socket of this TCP instance.

+  @param[in]  Request            The code of this operation request.

+  @param[in]  Data               Pointer to the operation specific data passed in

+                                 together with the operation request. This is an

+                                 optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS            The socket request completed successfully.

+  @retval other                  The error status returned by the corresponding TCP

+                                 layer function.

+

+**/

+EFI_STATUS

+TcpDispatcher (

+  IN SOCKET                  *Sock,

+  IN UINT8                   Request,

+  IN VOID                    *Data    OPTIONAL

+  );

+

+#endif

diff --git a/NetworkPkg/TcpDxe/TcpInput.c b/NetworkPkg/TcpDxe/TcpInput.c
new file mode 100644
index 0000000..e63469a
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpInput.c
@@ -0,0 +1,1592 @@
+/** @file

+  TCP input process routines.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+/**

+  Check whether the sequence number of the incoming segment is acceptable.

+

+  @param[in]  Tcb Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Seg Pointer to the incoming segment.

+

+  @retval 1       The sequence number is acceptable.

+  @retval 0       The sequence number is not acceptable.

+

+**/

+INTN

+TcpSeqAcceptable (

+  IN TCP_CB  *Tcb,

+  IN TCP_SEG *Seg

+  )

+{

+  return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) &&

+          TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd));

+}

+

+/**

+  NewReno fast recovery defined in RFC3782.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Seg      Segment that triggers the fast recovery.

+

+**/

+VOID

+TcpFastRecover (

+  IN OUT TCP_CB  *Tcb,

+  IN     TCP_SEG *Seg

+  )

+{

+  UINT32  FlightSize;

+  UINT32  Acked;

+

+  //

+  // Step 1: Three duplicate ACKs and not in fast recovery

+  //

+  if (Tcb->CongestState != TCP_CONGEST_RECOVER) {

+

+    //

+    // Step 1A: Invoking fast retransmission.

+    //

+    FlightSize        = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);

+

+    Tcb->Ssthresh     = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss));

+    Tcb->Recover      = Tcb->SndNxt;

+

+    Tcb->CongestState = TCP_CONGEST_RECOVER;

+    TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);

+

+    //

+    // Step 2: Entering fast retransmission

+    //

+    TcpRetransmit (Tcb, Tcb->SndUna);

+    Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss;

+

+    DEBUG (

+      (EFI_D_INFO,

+      "TcpFastRecover: enter fast retransmission for TCB %p, recover point is %d\n",

+      Tcb,

+      Tcb->Recover)

+      );

+    return;

+  }

+

+  //

+  // During fast recovery, execute Step 3, 4, 5 of RFC3782

+  //

+  if (Seg->Ack == Tcb->SndUna) {

+

+    //

+    // Step 3: Fast Recovery,

+    // If this is a duplicated ACK, increse Cwnd by SMSS.

+    //

+

+    // Step 4 is skipped here only to be executed later

+    // by TcpToSendData

+    //

+    Tcb->CWnd += Tcb->SndMss;

+    DEBUG (

+      (EFI_D_INFO,

+      "TcpFastRecover: received another duplicated ACK (%d) for TCB %p\n",

+      Seg->Ack,

+      Tcb)

+      );

+

+  } else {

+

+    //

+    // New data is ACKed, check whether it is a

+    // full ACK or partial ACK

+    //

+    if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) {

+

+      //

+      // Step 5 - Full ACK:

+      // deflate the congestion window, and exit fast recovery

+      //

+      FlightSize        = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);

+

+      Tcb->CWnd         = MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss);

+

+      Tcb->CongestState = TCP_CONGEST_OPEN;

+      DEBUG (

+        (EFI_D_INFO,

+        "TcpFastRecover: received a full ACK(%d) for TCB %p, exit fast recovery\n",

+        Seg->Ack,

+        Tcb)

+        );

+

+    } else {

+

+      //

+      // Step 5 - Partial ACK:

+      // fast retransmit the first unacknowledge field

+      // , then deflate the CWnd

+      //

+      TcpRetransmit (Tcb, Seg->Ack);

+      Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna);

+

+      //

+      // Deflate the CWnd by the amount of new data

+      // ACKed by SEG.ACK. If more than one SMSS data

+      // is ACKed, add back SMSS byte to CWnd after

+      //

+      if (Acked >= Tcb->SndMss) {

+        Acked -= Tcb->SndMss;

+

+      }

+

+      Tcb->CWnd -= Acked;

+

+      DEBUG (

+        (EFI_D_INFO,

+        "TcpFastRecover: received a partial ACK(%d) for TCB %p\n",

+        Seg->Ack,

+        Tcb)

+        );

+

+    }

+  }

+}

+

+/**

+  NewReno fast loss recovery defined in RFC3792.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Seg      Segment that triggers the fast loss recovery.

+

+**/

+VOID

+TcpFastLossRecover (

+  IN OUT TCP_CB  *Tcb,

+  IN     TCP_SEG *Seg

+  )

+{

+  if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {

+

+    //

+    // New data is ACKed, check whether it is a

+    // full ACK or partial ACK

+    //

+    if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) {

+

+      //

+      // Full ACK: exit the loss recovery.

+      //

+      Tcb->LossTimes    = 0;

+      Tcb->CongestState = TCP_CONGEST_OPEN;

+

+      DEBUG (

+        (EFI_D_INFO,

+        "TcpFastLossRecover: received a full ACK(%d) for TCB %p\n",

+        Seg->Ack,

+        Tcb)

+        );

+

+    } else {

+

+      //

+      // Partial ACK:

+      // fast retransmit the first unacknowledge field.

+      //

+      TcpRetransmit (Tcb, Seg->Ack);

+      DEBUG (

+        (EFI_D_INFO,

+        "TcpFastLossRecover: received a partial ACK(%d) for TCB %p\n",

+        Seg->Ack,

+        Tcb)

+        );

+    }

+  }

+}

+

+/**

+  Compute the RTT as specified in RFC2988.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Measure  Currently measured RTT in heartbeats.

+

+**/

+VOID

+TcpComputeRtt (

+  IN OUT TCP_CB *Tcb,

+  IN     UINT32 Measure

+  )

+{

+  INT32 Var;

+

+  //

+  // Step 2.3: Compute the RTO for subsequent RTT measurement.

+  //

+  if (Tcb->SRtt != 0) {

+

+    Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT);

+

+    if (Var < 0) {

+      Var = -Var;

+    }

+

+    Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2;

+    Tcb->SRtt   = 7 * (Tcb->SRtt >> 3) + Measure;

+

+  } else {

+    //

+    // Step 2.2: compute the first RTT measure

+    //

+    Tcb->SRtt   = Measure << TCP_RTT_SHIFT;

+    Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1);

+  }

+

+  Tcb->Rto = (Tcb->SRtt + MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT;

+

+  //

+  // Step 2.4: Limit the RTO to at least 1 second

+  // Step 2.5: Limit the RTO to a maxium value that

+  // is at least 60 second

+  //

+  if (Tcb->Rto < TCP_RTO_MIN) {

+    Tcb->Rto = TCP_RTO_MIN;

+

+  } else if (Tcb->Rto > TCP_RTO_MAX) {

+    Tcb->Rto = TCP_RTO_MAX;

+

+  }

+

+  DEBUG (

+    (EFI_D_INFO,

+    "TcpComputeRtt: new RTT for TCB %p computed SRTT: %d RTTVAR: %d RTO: %d\n",

+    Tcb,

+    Tcb->SRtt,

+    Tcb->RttVar,

+    Tcb->Rto)

+    );

+

+}

+

+/**

+  Trim the data; SYN and FIN to fit into the window defined by Left and Right.

+

+  @param[in]  Nbuf     The buffer that contains a received TCP segment without an IP header.

+  @param[in]  Left     The sequence number of the window's left edge.

+  @param[in]  Right    The sequence number of the window's right edge.

+

+**/

+VOID

+TcpTrimSegment (

+  IN NET_BUF   *Nbuf,

+  IN TCP_SEQNO Left,

+  IN TCP_SEQNO Right

+  )

+{

+  TCP_SEG   *Seg;

+  TCP_SEQNO Urg;

+  UINT32    Drop;

+

+  Seg = TCPSEG_NETBUF (Nbuf);

+

+  //

+  // If the segment is completely out of window,

+  // truncate every thing, include SYN and FIN.

+  //

+  if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) {

+

+    TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);

+    TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);

+

+    Seg->Seq = Seg->End;

+    NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD);

+    return;

+  }

+

+  //

+  // Adjust the buffer header

+  //

+  if (TCP_SEQ_LT (Seg->Seq, Left)) {

+

+    Drop      = TCP_SUB_SEQ (Left, Seg->Seq);

+    Urg       = Seg->Seq + Seg->Urg;

+    Seg->Seq  = Left;

+

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {

+      TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);

+      Drop--;

+    }

+

+    //

+    // Adjust the urgent point

+    //

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) {

+

+      if (TCP_SEQ_LT (Urg, Seg->Seq)) {

+

+        TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);

+      } else {

+        Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq);

+      }

+    }

+

+    if (Drop != 0) {

+      NetbufTrim (Nbuf, Drop, NET_BUF_HEAD);

+    }

+  }

+

+  //

+  // Adjust the buffer tail

+  //

+  if (TCP_SEQ_GT (Seg->End, Right)) {

+

+    Drop      = TCP_SUB_SEQ (Seg->End, Right);

+    Seg->End  = Right;

+

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {

+      TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);

+      Drop--;

+    }

+

+    if (Drop != 0) {

+      NetbufTrim (Nbuf, Drop, NET_BUF_TAIL);

+    }

+  }

+

+  ASSERT (TcpVerifySegment (Nbuf) != 0);

+}

+

+/**

+  Trim off the data outside the tcb's receive window.

+

+  @param[in]  Tcb      Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Nbuf     Pointer to the NET_BUF containing the received tcp segment.

+

+**/

+VOID

+TcpTrimInWnd (

+  IN TCP_CB  *Tcb,

+  IN NET_BUF *Nbuf

+  )

+{

+  TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd);

+}

+

+/**

+  Process the data and FIN flag, and check whether to deliver

+  data to the socket layer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+  @retval 0        No error occurred to deliver data.

+  @retval -1       An error condition occurred. The proper response is to reset the

+                   connection.

+

+**/

+INTN

+TcpDeliverData (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  LIST_ENTRY      *Entry;

+  NET_BUF         *Nbuf;

+  TCP_SEQNO       Seq;

+  TCP_SEG         *Seg;

+  UINT32          Urgent;

+

+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));

+

+  //

+  // make sure there is some data queued,

+  // and TCP is in a proper state

+  //

+  if (IsListEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) {

+

+    return 0;

+  }

+

+  //

+  // Deliver data to the socket layer

+  //

+  Entry = Tcb->RcvQue.ForwardLink;

+  Seq   = Tcb->RcvNxt;

+

+  while (Entry != &Tcb->RcvQue) {

+    Nbuf  = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);

+    Seg   = TCPSEG_NETBUF (Nbuf);

+

+    ASSERT (TcpVerifySegment (Nbuf) != 0);

+    ASSERT (Nbuf->Tcp == NULL);

+

+    if (TCP_SEQ_GT (Seg->Seq, Seq)) {

+      break;

+    }

+

+    Entry       = Entry->ForwardLink;

+    Seq         = Seg->End;

+    Tcb->RcvNxt = Seq;

+

+    RemoveEntryList (&Nbuf->List);

+

+    //

+    // RFC793 Eighth step: process FIN in sequence

+    //

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {

+

+      //

+      // The peer sends to us junky data after FIN,

+      // reset the connection.

+      //

+      if (!IsListEmpty (&Tcb->RcvQue)) {

+        DEBUG (

+          (EFI_D_ERROR,

+          "TcpDeliverData: data received after FIN from peer of TCB %p, reset connection\n",

+          Tcb)

+          );

+

+        NetbufFree (Nbuf);

+        return -1;

+      }

+

+      DEBUG (

+        (EFI_D_INFO,

+        "TcpDeliverData: processing FIN from peer of TCB %p\n",

+        Tcb)

+        );

+

+      switch (Tcb->State) {

+      case TCP_SYN_RCVD:

+      case TCP_ESTABLISHED:

+

+        TcpSetState (Tcb, TCP_CLOSE_WAIT);

+        break;

+

+      case TCP_FIN_WAIT_1:

+

+        if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {

+

+          TcpSetState (Tcb, TCP_CLOSING);

+          break;

+        }

+

+      //

+      // fall through

+      //

+      case TCP_FIN_WAIT_2:

+

+        TcpSetState (Tcb, TCP_TIME_WAIT);

+        TcpClearAllTimer (Tcb);

+

+        if (Tcb->TimeWaitTimeout != 0) {

+

+          TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);

+        } else {

+

+          DEBUG (

+            (EFI_D_WARN,

+            "Connection closed immediately because app disables TIME_WAIT timer for %p\n",

+            Tcb)

+            );

+

+          TcpSendAck (Tcb);

+          TcpClose (Tcb);

+        }

+        break;

+

+      case TCP_CLOSE_WAIT:

+      case TCP_CLOSING:

+      case TCP_LAST_ACK:

+      case TCP_TIME_WAIT:

+        //

+        // The peer sends to us junk FIN byte. Discard

+        // the buffer then reset the connection

+        //

+        NetbufFree (Nbuf);

+        return -1;

+        break;

+      default:

+        break;

+      }

+

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);

+

+      Seg->End--;

+    }

+

+    //

+    // Don't delay the ack if PUSH flag is on.

+    //

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) {

+

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);

+    }

+

+    if (Nbuf->TotalSize != 0) {

+      Urgent = 0;

+

+      if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&

+          TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)

+          ) {

+

+        if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) {

+          Urgent = Nbuf->TotalSize;

+        } else {

+          Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1;

+        }

+      }

+

+      SockDataRcvd (Tcb->Sk, Nbuf, Urgent);

+    }

+

+    if (TCP_FIN_RCVD (Tcb->State)) {

+

+      SockNoMoreData (Tcb->Sk);

+    }

+

+    NetbufFree (Nbuf);

+  }

+

+  return 0;

+}

+

+/**

+  Store the data into the reassemble queue.

+

+  @param[in, out]  Tcb   Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Nbuf  Pointer to the buffer containing the data to be queued.

+

+**/

+VOID

+TcpQueueData (

+  IN OUT TCP_CB  *Tcb,

+  IN     NET_BUF *Nbuf

+  )

+{

+  TCP_SEG         *Seg;

+  LIST_ENTRY      *Head;

+  LIST_ENTRY      *Prev;

+  LIST_ENTRY      *Cur;

+  NET_BUF         *Node;

+

+  ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));

+

+  NET_GET_REF (Nbuf);

+

+  Seg   = TCPSEG_NETBUF (Nbuf);

+  Head  = &Tcb->RcvQue;

+

+  //

+  // Fast path to process normal case. That is,

+  // no out-of-order segments are received.

+  //

+  if (IsListEmpty (Head)) {

+

+    InsertTailList (Head, &Nbuf->List);

+    return;

+  }

+

+  //

+  // Find the point to insert the buffer

+  //

+  for (Prev = Head, Cur = Head->ForwardLink;

+       Cur != Head;

+       Prev = Cur, Cur = Cur->ForwardLink

+       ) {

+

+    Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);

+

+    if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) {

+      break;

+    }

+  }

+

+  //

+  // Check whether the current segment overlaps with the

+  // previous segment.

+  //

+  if (Prev != Head) {

+    Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);

+

+    if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) {

+

+      if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) {

+

+        NetbufFree (Nbuf);

+        return;

+      }

+

+      TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End);

+    }

+  }

+

+  InsertHeadList (Prev, &Nbuf->List);

+

+  TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);

+

+  //

+  // Check the segments after the insert point.

+  //

+  while (Cur != Head) {

+    Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);

+

+    if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) {

+

+      Cur = Cur->ForwardLink;

+

+      RemoveEntryList (&Node->List);

+      NetbufFree (Node);

+      continue;

+    }

+

+    if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) {

+

+      if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) {

+

+        RemoveEntryList (&Nbuf->List);

+        NetbufFree (Nbuf);

+        return;

+      }

+

+      TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq);

+      break;

+    }

+

+    Cur = Cur->ForwardLink;

+  }

+}

+

+

+/**

+  Adjust the send queue or the retransmit queue.

+

+  @param[in]  Tcb      Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Ack      The acknowledge seuqence number of the received segment.

+

+**/

+VOID

+TcpAdjustSndQue (

+  IN TCP_CB    *Tcb,

+  IN TCP_SEQNO Ack

+  )

+{

+  LIST_ENTRY      *Head;

+  LIST_ENTRY      *Cur;

+  NET_BUF         *Node;

+  TCP_SEG         *Seg;

+

+  Head  = &Tcb->SndQue;

+  Cur   = Head->ForwardLink;

+

+  while (Cur != Head) {

+    Node  = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);

+    Seg   = TCPSEG_NETBUF (Node);

+

+    if (TCP_SEQ_GEQ (Seg->Seq, Ack)) {

+      break;

+    }

+

+    //

+    // Remove completely ACKed segments

+    //

+    if (TCP_SEQ_LEQ (Seg->End, Ack)) {

+      Cur = Cur->ForwardLink;

+

+      RemoveEntryList (&Node->List);

+      NetbufFree (Node);

+      continue;

+    }

+

+    TcpTrimSegment (Node, Ack, Seg->End);

+    break;

+  }

+}

+

+/**

+  Process the received TCP segments.

+

+  @param[in]  Nbuf     Buffer that contains received a TCP segment without an IP header.

+  @param[in]  Src      Source address of the segment, or the peer's IP address.

+  @param[in]  Dst      Destination address of the segment, or the local end's IP

+                       address.

+  @param[in]  Version  IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates

+                       IP6 stack.

+

+  @retval 0        Segment  processed successfully. It is either accepted or

+                   discarded. However, no connection is reset by the segment.

+  @retval -1       A connection is reset by the segment.

+

+**/

+INTN

+TcpInput (

+  IN NET_BUF         *Nbuf,

+  IN EFI_IP_ADDRESS  *Src,

+  IN EFI_IP_ADDRESS  *Dst,

+  IN UINT8           Version

+  )

+{

+  TCP_CB      *Tcb;

+  TCP_CB      *Parent;

+  TCP_OPTION  Option;

+  TCP_HEAD    *Head;

+  INT32       Len;

+  TCP_SEG     *Seg;

+  TCP_SEQNO   Right;

+  TCP_SEQNO   Urg;

+  UINT16      Checksum;

+

+  ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));

+

+  NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);

+

+  Parent  = NULL;

+  Tcb     = NULL;

+

+  Head    = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);

+  ASSERT (Head != NULL);

+  Len     = Nbuf->TotalSize - (Head->HeadLen << 2);

+

+  if ((Head->HeadLen < 5) || (Len < 0)) {

+

+    DEBUG ((EFI_D_INFO, "TcpInput: received an mal-formated packet\n"));

+    goto DISCARD;

+  }

+

+  if (Version == IP_VERSION_4) {

+    Checksum = NetPseudoHeadChecksum (Src->Addr[0], Dst->Addr[0], 6, 0);

+  } else {

+    Checksum = NetIp6PseudoHeadChecksum (&Src->v6, &Dst->v6, 6, 0);

+  }

+

+  Checksum = TcpChecksum (Nbuf, Checksum);

+

+  if (Checksum != 0) {

+    DEBUG ((EFI_D_ERROR, "TcpInput: received a checksum error packet\n"));

+    goto DISCARD;

+  }

+

+  if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) {

+    Len++;

+  }

+

+  if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) {

+    Len++;

+  }

+

+  Tcb = TcpLocateTcb (

+          Head->DstPort,

+          Dst,

+          Head->SrcPort,

+          Src,

+          Version,

+          (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)

+          );

+

+  if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) {

+    DEBUG ((EFI_D_INFO, "TcpInput: send reset because no TCB find\n"));

+

+    Tcb = NULL;

+    goto SEND_RESET;

+  }

+

+  Seg = TcpFormatNetbuf (Tcb, Nbuf);

+

+  //

+  // RFC1122 recommended reaction to illegal option

+  // (in fact, an illegal option length) is reset.

+  //

+  if (TcpParseOption (Nbuf->Tcp, &Option) == -1) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "TcpInput: reset the peer because of mal-format option for Tcb %p\n",

+      Tcb)

+      );

+

+    goto SEND_RESET;

+  }

+

+  //

+  // From now on, the segment is headless

+  //

+  NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD);

+  Nbuf->Tcp = NULL;

+

+  //

+  // Process the segment in LISTEN state.

+  //

+  if (Tcb->State == TCP_LISTEN) {

+    //

+    // First step: Check RST

+    //

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {

+      DEBUG (

+        (EFI_D_WARN,

+        "TcpInput: discard a reset segment for TCB %p in listening\n",

+        Tcb)

+        );

+

+      goto DISCARD;

+    }

+

+    //

+    // Second step: Check ACK.

+    // Any ACK sent to TCP in LISTEN is reseted.

+    //

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {

+      DEBUG (

+        (EFI_D_WARN,

+        "TcpInput: send reset because of segment with ACK for TCB %p in listening\n",

+        Tcb)

+        );

+

+      goto SEND_RESET;

+    }

+

+    //

+    // Third step: Check SYN

+    //

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {

+      //

+      // create a child TCB to handle the data

+      //

+      Parent  = Tcb;

+

+      Tcb     = TcpCloneTcb (Parent);

+      if (Tcb == NULL) {

+        DEBUG (

+          (EFI_D_ERROR,

+          "TcpInput: discard a segment because failed to clone a child for TCB%p\n",

+          Tcb)

+          );

+

+        goto DISCARD;

+      }

+

+      DEBUG (

+        (EFI_D_INFO,

+        "TcpInput: create a child for TCB %p in listening\n",

+        Tcb)

+        );

+

+      //

+      // init the TCB structure

+      //

+      IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, Dst);

+      IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, Src);

+      Tcb->LocalEnd.Port  = Head->DstPort;

+      Tcb->RemoteEnd.Port = Head->SrcPort;

+

+      TcpInitTcbLocal (Tcb);

+      TcpInitTcbPeer (Tcb, Seg, &Option);

+

+      TcpSetState (Tcb, TCP_SYN_RCVD);

+      TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);

+      TcpTrimInWnd (Tcb, Nbuf);

+

+      goto StepSix;

+    }

+

+    goto DISCARD;

+

+  } else if (Tcb->State == TCP_SYN_SENT) {

+    //

+    // First step: Check ACK bit

+    //

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) {

+

+      DEBUG (

+        (EFI_D_WARN,

+        "TcpInput: send reset because of wrong ACK received for TCB %p in SYN_SENT\n",

+        Tcb)

+        );

+

+      goto SEND_RESET;

+    }

+

+    //

+    // Second step: Check RST bit

+    //

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {

+

+      if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {

+

+        DEBUG (

+          (EFI_D_WARN,

+          "TcpInput: connection reset by peer for TCB %p in SYN_SENT\n",

+          Tcb)

+          );

+

+        SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);

+        goto DROP_CONNECTION;

+      } else {

+

+        DEBUG (

+          (EFI_D_WARN,

+          "TcpInput: discard a reset segment because of no ACK for TCB %p in SYN_SENT\n",

+          Tcb)

+          );

+

+        goto DISCARD;

+      }

+    }

+

+    //

+    // Third step: Check security and precedence. Skipped

+    //

+

+    //

+    // Fourth step: Check SYN. Pay attention to sitimulatous open

+    //

+    if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {

+

+      TcpInitTcbPeer (Tcb, Seg, &Option);

+

+      if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {

+

+        Tcb->SndUna = Seg->Ack;

+      }

+

+      TcpClearTimer (Tcb, TCP_TIMER_REXMIT);

+

+      if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) {

+

+        TcpSetState (Tcb, TCP_ESTABLISHED);

+

+        TcpClearTimer (Tcb, TCP_TIMER_CONNECT);

+        TcpDeliverData (Tcb);

+

+        if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&

+            TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)

+            ) {

+

+          TcpComputeRtt (Tcb, Tcb->RttMeasure);

+          TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);

+        }

+

+        TcpTrimInWnd (Tcb, Nbuf);

+

+        TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);

+

+        DEBUG (

+          (EFI_D_INFO,

+          "TcpInput: connection established for TCB %p in SYN_SENT\n",

+          Tcb)

+          );

+

+        goto StepSix;

+      } else {

+        //

+        // Received a SYN segment without ACK, simultanous open.

+        //

+        TcpSetState (Tcb, TCP_SYN_RCVD);

+

+        ASSERT (Tcb->SndNxt == Tcb->Iss + 1);

+        TcpAdjustSndQue (Tcb, Tcb->SndNxt);

+

+        TcpTrimInWnd (Tcb, Nbuf);

+

+        DEBUG (

+          (EFI_D_WARN,

+          "TcpInput: simultanous open for TCB %p in SYN_SENT\n",

+          Tcb)

+          );

+

+        goto StepSix;

+      }

+    }

+

+    goto DISCARD;

+  }

+

+  //

+  // Process segment in SYN_RCVD or TCP_CONNECTED states

+  //

+

+  //

+  // Clear probe timer since the RecvWindow is opened.

+  //

+  if (Tcb->ProbeTimerOn && (Seg->Wnd != 0)) {

+    TcpClearTimer (Tcb, TCP_TIMER_PROBE);

+    Tcb->ProbeTimerOn = FALSE;

+  }

+

+  //

+  // First step: Check whether SEG.SEQ is acceptable

+  //

+  if (TcpSeqAcceptable (Tcb, Seg) == 0) {

+    DEBUG (

+      (EFI_D_WARN,

+      "TcpInput: sequence acceptance test failed for segment of TCB %p\n",

+      Tcb)

+      );

+

+    if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {

+      TcpSendAck (Tcb);

+    }

+

+    goto DISCARD;

+  }

+

+  if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) &&

+      (Tcb->RcvWl2 == Seg->End) &&

+      !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)

+        ) {

+

+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);

+  }

+

+  //

+  // Second step: Check the RST

+  //

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {

+

+    DEBUG ((EFI_D_WARN, "TcpInput: connection reset for TCB %p\n", Tcb));

+

+    if (Tcb->State == TCP_SYN_RCVD) {

+

+      SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED);

+

+      //

+      // This TCB comes from either a LISTEN TCB,

+      // or active open TCB with simultanous open.

+      // Do NOT signal user CONNECTION refused

+      // if it comes from a LISTEN TCB.

+      //

+    } else if ((Tcb->State == TCP_ESTABLISHED) ||

+             (Tcb->State == TCP_FIN_WAIT_1) ||

+             (Tcb->State == TCP_FIN_WAIT_2) ||

+             (Tcb->State == TCP_CLOSE_WAIT)

+            ) {

+

+      SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);

+

+    } else {

+    }

+

+    goto DROP_CONNECTION;

+  }

+

+  //

+  // Trim the data and flags.

+  //

+  TcpTrimInWnd (Tcb, Nbuf);

+

+  //

+  // Third step: Check security and precedence, Ignored

+  //

+

+  //

+  // Fourth step: Check the SYN bit.

+  //

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {

+

+    DEBUG (

+      (EFI_D_WARN,

+      "TcpInput: connection reset because received extra SYN for TCB %p\n",

+      Tcb)

+      );

+

+    SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);

+    goto RESET_THEN_DROP;

+  }

+  //

+  // Fifth step: Check the ACK

+  //

+  if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {

+    DEBUG (

+      (EFI_D_WARN,

+      "TcpInput: segment discard because of no ACK for connected TCB %p\n",

+      Tcb)

+      );

+

+    goto DISCARD;

+  } else {

+    if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick == 0) {

+      Tcp6RefreshNeighbor (Tcb, Src, TCP6_KEEP_NEIGHBOR_TIME * TICKS_PER_SECOND);

+      Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK;

+    }

+  }

+

+  if (Tcb->State == TCP_SYN_RCVD) {

+

+    if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) && TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) {

+

+      Tcb->SndWnd     = Seg->Wnd;

+      Tcb->SndWndMax  = MAX (Tcb->SndWnd, Tcb->SndWndMax);

+      Tcb->SndWl1     = Seg->Seq;

+      Tcb->SndWl2     = Seg->Ack;

+      TcpSetState (Tcb, TCP_ESTABLISHED);

+

+      TcpClearTimer (Tcb, TCP_TIMER_CONNECT);

+      TcpDeliverData (Tcb);

+

+      DEBUG (

+        (EFI_D_INFO,

+        "TcpInput: connection established  for TCB %p in SYN_RCVD\n",

+        Tcb)

+        );

+

+      //

+      // Continue the process as ESTABLISHED state

+      //

+    } else {

+      DEBUG (

+        (EFI_D_WARN,

+        "TcpInput: send reset because of wrong ACK for TCB %p in SYN_RCVD\n",

+        Tcb)

+        );

+

+      goto SEND_RESET;

+    }

+  }

+

+  if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) {

+

+    DEBUG (

+      (EFI_D_WARN,

+      "TcpInput: ignore the out-of-data ACK for connected TCB %p\n",

+      Tcb)

+      );

+

+    goto StepSix;

+

+  } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) {

+

+    DEBUG (

+      (EFI_D_WARN,

+      "TcpInput: discard segment for future ACK for connected TCB %p\n",

+      Tcb)

+      );

+

+    TcpSendAck (Tcb);

+    goto DISCARD;

+  }

+

+  //

+  // From now on: SND.UNA <= SEG.ACK <= SND.NXT.

+  //

+  if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) {

+    //

+    // update TsRecent as specified in page 16 RFC1323.

+    // RcvWl2 equals to the variable "LastAckSent"

+    // defined there.

+    //

+    if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) && TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) {

+

+      Tcb->TsRecent     = Option.TSVal;

+      Tcb->TsRecentAge  = mTcpTick;

+    }

+

+    TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr));

+

+  } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {

+

+    ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN);

+

+    TcpComputeRtt (Tcb, Tcb->RttMeasure);

+    TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);

+  }

+

+  if (Seg->Ack == Tcb->SndNxt) {

+

+    TcpClearTimer (Tcb, TCP_TIMER_REXMIT);

+  } else {

+

+    TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);

+  }

+

+  //

+  // Count duplicate acks.

+  //

+  if ((Seg->Ack == Tcb->SndUna) &&

+      (Tcb->SndUna != Tcb->SndNxt) &&

+      (Seg->Wnd == Tcb->SndWnd) &&

+      (0 == Len)

+      ) {

+

+    Tcb->DupAck++;

+  } else {

+

+    Tcb->DupAck = 0;

+  }

+

+  //

+  // Congestion avoidance, fast recovery and fast retransmission.

+  //

+  if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) ||

+      (Tcb->CongestState == TCP_CONGEST_LOSS)

+      ) {

+

+    if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {

+

+      if (Tcb->CWnd < Tcb->Ssthresh) {

+

+        Tcb->CWnd += Tcb->SndMss;

+      } else {

+

+        Tcb->CWnd += MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1);

+      }

+

+      Tcb->CWnd = MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale);

+    }

+

+    if (Tcb->CongestState == TCP_CONGEST_LOSS) {

+      TcpFastLossRecover (Tcb, Seg);

+    }

+  } else {

+

+    TcpFastRecover (Tcb, Seg);

+  }

+

+  if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {

+

+    TcpAdjustSndQue (Tcb, Seg->Ack);

+    Tcb->SndUna = Seg->Ack;

+

+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&

+        TCP_SEQ_LT (Tcb->SndUp, Seg->Ack)

+        ) {

+

+      TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);

+    }

+  }

+

+  //

+  // Update window info

+  //

+  if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) ||

+      ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))

+      ) {

+

+    Right = Seg->Ack + Seg->Wnd;

+

+    if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) {

+

+      if ((Tcb->SndWl1 == Seg->Seq) &&

+          (Tcb->SndWl2 == Seg->Ack) &&

+          (Len == 0)

+          ) {

+

+        goto NO_UPDATE;

+      }

+

+      DEBUG (

+        (EFI_D_WARN,

+        "TcpInput: peer shrinks the window for connected TCB %p\n",

+        Tcb)

+        );

+

+      if ((Tcb->CongestState == TCP_CONGEST_RECOVER) && (TCP_SEQ_LT (Right, Tcb->Recover))) {

+

+        Tcb->Recover = Right;

+      }

+

+      if ((Tcb->CongestState == TCP_CONGEST_LOSS) && (TCP_SEQ_LT (Right, Tcb->LossRecover))) {

+

+        Tcb->LossRecover = Right;

+      }

+

+      if (TCP_SEQ_LT (Right, Tcb->SndNxt)) {

+

+        Tcb->SndNxt = Right;

+

+        if (Right == Tcb->SndUna) {

+

+          TcpClearTimer (Tcb, TCP_TIMER_REXMIT);

+          TcpSetProbeTimer (Tcb);

+        }

+      }

+    }

+

+    Tcb->SndWnd     = Seg->Wnd;

+    Tcb->SndWndMax  = MAX (Tcb->SndWnd, Tcb->SndWndMax);

+    Tcb->SndWl1     = Seg->Seq;

+    Tcb->SndWl2     = Seg->Ack;

+  }

+

+NO_UPDATE:

+

+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) && (Tcb->SndUna == Tcb->SndNxt)) {

+

+    DEBUG (

+      (EFI_D_INFO,

+      "TcpInput: local FIN is ACKed by peer for connected TCB %p\n",

+      Tcb)

+      );

+

+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED);

+  }

+

+  //

+  // Transit the state if proper.

+  //

+  switch (Tcb->State) {

+  case TCP_FIN_WAIT_1:

+

+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {

+

+      TcpSetState (Tcb, TCP_FIN_WAIT_2);

+

+      TcpClearAllTimer (Tcb);

+      TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout);

+    }

+

+  case TCP_FIN_WAIT_2:

+

+    break;

+

+  case TCP_CLOSE_WAIT:

+    break;

+

+  case TCP_CLOSING:

+

+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {

+

+      TcpSetState (Tcb, TCP_TIME_WAIT);

+

+      TcpClearAllTimer (Tcb);

+

+      if (Tcb->TimeWaitTimeout != 0) {

+

+        TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);

+      } else {

+

+        DEBUG (

+          (EFI_D_WARN,

+          "Connection closed immediately because app disables TIME_WAIT timer for %p\n",

+          Tcb)

+          );

+

+        TcpClose (Tcb);

+      }

+    }

+    break;

+

+  case TCP_LAST_ACK:

+

+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {

+

+      TcpSetState (Tcb, TCP_CLOSED);

+    }

+

+    break;

+

+  case TCP_TIME_WAIT:

+

+    TcpSendAck (Tcb);

+

+    if (Tcb->TimeWaitTimeout != 0) {

+

+      TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);

+    } else {

+

+      DEBUG (

+        (EFI_D_WARN,

+        "Connection closed immediately because app disables TIME_WAIT timer for %p\n",

+        Tcb)

+        );

+

+      TcpClose (Tcb);

+    }

+    break;

+

+  default:

+    break;

+  }

+  //

+  // Sixth step: Check the URG bit.update the Urg point

+  // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact.

+  //

+StepSix:

+

+  Tcb->Idle = 0;

+  TcpSetKeepaliveTimer (Tcb);

+

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) && !TCP_FIN_RCVD (Tcb->State)) {

+

+    DEBUG (

+      (EFI_D_INFO,

+      "TcpInput: received urgent data from peer for connected TCB %p\n",

+      Tcb)

+      );

+

+    Urg = Seg->Seq + Seg->Urg;

+

+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && TCP_SEQ_GT (Urg, Tcb->RcvUp)) {

+

+      Tcb->RcvUp = Urg;

+    } else {

+

+      Tcb->RcvUp = Urg;

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG);

+    }

+  }

+  //

+  // Seventh step: Process the segment data

+  //

+  if (Seg->End != Seg->Seq) {

+

+    if (TCP_FIN_RCVD (Tcb->State)) {

+

+      DEBUG (

+        (EFI_D_WARN,

+        "TcpInput: connection reset because data is lost for connected TCB %p\n",

+        Tcb)

+        );

+

+      goto RESET_THEN_DROP;

+    }

+

+    if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) {

+      DEBUG (

+        (EFI_D_WARN,

+        "TcpInput: connection reset because data is lost for connected TCB %p\n",

+        Tcb)

+        );

+

+      goto RESET_THEN_DROP;

+    }

+

+    TcpQueueData (Tcb, Nbuf);

+    if (TcpDeliverData (Tcb) == -1) {

+      goto RESET_THEN_DROP;

+    }

+

+    if (!IsListEmpty (&Tcb->RcvQue)) {

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);

+    }

+  }

+

+  //

+  // Eighth step: check the FIN.

+  // This step is moved to TcpDeliverData. FIN will be

+  // processed in sequence there. Check the comments in

+  // the beginning of the file header for information.

+  //

+

+  //

+  // Tcb is a new child of the listening Parent,

+  // commit it.

+  //

+  if (Parent != NULL) {

+    Tcb->Parent = Parent;

+    TcpInsertTcb (Tcb);

+  }

+

+  if ((Tcb->State != TCP_CLOSED) &&

+      (TcpToSendData (Tcb, 0) == 0) &&

+      (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Nbuf->TotalSize != 0))

+      ) {

+

+    TcpToSendAck (Tcb);

+  }

+

+  NetbufFree (Nbuf);

+  return 0;

+

+RESET_THEN_DROP:

+  TcpSendReset (Tcb, Head, Len, Dst, Src, Version);

+

+DROP_CONNECTION:

+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));

+

+  NetbufFree (Nbuf);

+  TcpClose (Tcb);

+

+  return -1;

+

+SEND_RESET:

+

+  TcpSendReset (Tcb, Head, Len, Dst, Src, Version);

+

+DISCARD:

+

+  //

+  // Tcb is a child of Parent, and it doesn't survive

+  //

+  DEBUG ((EFI_D_WARN, "TcpInput: Discard a packet\n"));

+  NetbufFree (Nbuf);

+

+  if ((Parent != NULL) && (Tcb != NULL)) {

+

+    ASSERT (Tcb->Sk != NULL);

+    TcpClose (Tcb);

+  }

+

+  return 0;

+}

+

+/**

+  Process the received ICMP error messages for TCP.

+

+  @param[in]  Nbuf     The buffer that contains part of the TCP segment without an IP header

+                       truncated from the ICMP error packet.

+  @param[in]  IcmpErr  The ICMP error code interpreted from an ICMP error packet.

+  @param[in]  Src      Source address of the ICMP error message.

+  @param[in]  Dst      Destination address of the ICMP error message.

+  @param[in]  Version  IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates

+                       IP6 stack.

+

+**/

+VOID

+TcpIcmpInput (

+  IN NET_BUF         *Nbuf,

+  IN UINT8           IcmpErr,

+  IN EFI_IP_ADDRESS  *Src,

+  IN EFI_IP_ADDRESS  *Dst,

+  IN UINT8           Version

+  )

+{

+  TCP_HEAD         *Head;

+  TCP_CB           *Tcb;

+  TCP_SEQNO        Seq;

+  EFI_STATUS       IcmpErrStatus;

+  BOOLEAN          IcmpErrIsHard;

+  BOOLEAN          IcmpErrNotify;

+

+  Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);

+  ASSERT (Head != NULL);

+

+  Tcb = TcpLocateTcb (

+          Head->DstPort,

+          Dst,

+          Head->SrcPort,

+          Src,

+          Version,

+          FALSE

+          );

+  if (Tcb == NULL || Tcb->State == TCP_CLOSED) {

+

+    goto CLEAN_EXIT;

+  }

+

+  //

+  // Validate the sequence number.

+  //

+  Seq = NTOHL (Head->Seq);

+  if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) {

+

+    goto CLEAN_EXIT;

+  }

+

+  IcmpErrStatus = IpIoGetIcmpErrStatus (IcmpErr, Tcb->Sk->IpVersion, &IcmpErrIsHard, &IcmpErrNotify);

+

+  if (IcmpErrNotify) {

+

+    SOCK_ERROR (Tcb->Sk, IcmpErrStatus);

+  }

+

+  if (IcmpErrIsHard) {

+

+    TcpClose (Tcb);

+  }

+

+CLEAN_EXIT:

+

+  NetbufFree (Nbuf);

+}

diff --git a/NetworkPkg/TcpDxe/TcpIo.c b/NetworkPkg/TcpDxe/TcpIo.c
new file mode 100644
index 0000000..cecb6d1
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpIo.c
@@ -0,0 +1,190 @@
+/** @file

+  Implementation of I/O interfaces between TCP and IpIoLib.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+/**

+  Packet receive callback function provided to IP_IO, used to call

+  the proper function to handle the packet received by IP.

+

+  @param[in] Status        Result of the receive request.

+  @param[in] IcmpErr       Valid when Status is EFI_ICMP_ERROR.

+  @param[in] NetSession    The IP session for the received packet.

+  @param[in] Pkt           Packet received.

+  @param[in] Context       The data provided by the user for the received packet when

+                           the callback is registered in IP_IO_OPEN_DATA::RcvdContext.

+                           This is an optional parameter that may be NULL.

+

+**/

+VOID

+EFIAPI

+TcpRxCallback (

+  IN EFI_STATUS                       Status,

+  IN UINT8                            IcmpErr,

+  IN EFI_NET_SESSION_DATA             *NetSession,

+  IN NET_BUF                          *Pkt,

+  IN VOID                             *Context    OPTIONAL

+  )

+{

+  if (EFI_SUCCESS == Status) {

+    TcpInput (Pkt, &NetSession->Source, &NetSession->Dest, NetSession->IpVersion);

+  } else {

+    TcpIcmpInput (

+      Pkt,

+      IcmpErr,

+      &NetSession->Source,

+      &NetSession->Dest,

+      NetSession->IpVersion

+      );

+  }

+}

+

+/**

+  Send the segment to IP via IpIo function.

+

+  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Nbuf               Pointer to the TCP segment to be sent.

+  @param[in]  Src                Source address of the TCP segment.

+  @param[in]  Dest               Destination address of the TCP segment.

+  @param[in]  Version            IP_VERSION_4 or IP_VERSION_6

+

+  @retval 0                      The segment was sent out successfully.

+  @retval -1                     The segment failed to send.

+

+**/

+INTN

+TcpSendIpPacket (

+  IN TCP_CB          *Tcb,

+  IN NET_BUF         *Nbuf,

+  IN EFI_IP_ADDRESS  *Src,

+  IN EFI_IP_ADDRESS  *Dest,

+  IN UINT8           Version

+  )

+{

+  EFI_STATUS       Status;

+  IP_IO            *IpIo;

+  IP_IO_OVERRIDE   Override;

+  SOCKET           *Sock;

+  VOID             *IpSender;

+  TCP_PROTO_DATA  *TcpProto;

+

+  if (NULL == Tcb) {

+

+    IpIo     = NULL;

+    IpSender = IpIoFindSender (&IpIo, Version, Src);

+

+    if (IpSender == NULL) {

+      DEBUG ((EFI_D_WARN, "TcpSendIpPacket: No appropriate IpSender.\n"));

+      return -1;

+    }

+

+    if (Version == IP_VERSION_6) {

+      //

+      // It's tricky here. EFI IPv6 Spec don't allow an instance overriding the

+      // destination address if the dest is already specified through the

+      // configuration data. Here we get the IpIo we need and use the default IP

+      // instance in this IpIo to send the packet. The dest address is configured

+      // to be the unspecified address for the default IP instance.

+      //

+      IpSender = NULL;

+    }

+  } else {

+

+    Sock     = Tcb->Sk;

+    TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;

+    IpIo     = TcpProto->TcpService->IpIo;

+    IpSender = Tcb->IpInfo;

+

+    if (Version == IP_VERSION_6) {

+      //

+      // It's IPv6 and this TCP segment belongs to a solid TCB, in such case

+      // the destination address can't be overridden, so reset the Dest to NULL.

+      //

+      Dest = NULL;

+    }

+  }

+

+  ASSERT (Version == IpIo->IpVersion);

+

+  if (Version == IP_VERSION_4) {

+    Override.Ip4OverrideData.TypeOfService = 0;

+    Override.Ip4OverrideData.TimeToLive    = 255;

+    Override.Ip4OverrideData.DoNotFragment = FALSE;

+    Override.Ip4OverrideData.Protocol      = EFI_IP_PROTO_TCP;

+    ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));

+    CopyMem (&Override.Ip4OverrideData.SourceAddress, Src, sizeof (EFI_IPv4_ADDRESS));

+  } else {

+    Override.Ip6OverrideData.Protocol  = EFI_IP_PROTO_TCP;

+    Override.Ip6OverrideData.HopLimit  = 255;

+    Override.Ip6OverrideData.FlowLabel = 0;

+  }

+

+  Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override);

+

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "TcpSendIpPacket: return %r error\n", Status));

+    return -1;

+  }

+

+  return 0;

+}

+

+/**

+  Refresh the remote peer's Neighbor Cache State if already exists.

+

+  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Neighbor           Source address of the TCP segment.

+  @param[in]  Timeout            Time in 100-ns units that this entry will remain

+                                 in the neighbor cache. A value of zero means that

+                                 the entry  is permanent. A value of non-zero means

+                                 that the entry is dynamic and will be deleted

+                                 after Timeout.

+

+  @retval EFI_SUCCESS            Successfully updated the neighbor relationship.

+  @retval EFI_NOT_STARTED        The IpIo is not configured.

+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.

+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.

+  @retval EFI_NOT_FOUND          This entry is not in the neighbor table.

+

+**/

+EFI_STATUS

+Tcp6RefreshNeighbor (

+  IN TCP_CB          *Tcb,

+  IN EFI_IP_ADDRESS  *Neighbor,

+  IN UINT32          Timeout

+  )

+{

+  IP_IO            *IpIo;

+  SOCKET           *Sock;

+  TCP_PROTO_DATA  *TcpProto;

+

+  if (NULL == Tcb) {

+    IpIo = NULL;

+    IpIoFindSender (&IpIo, IP_VERSION_6, Neighbor);

+

+    if (IpIo == NULL) {

+      DEBUG ((EFI_D_WARN, "Tcp6AddNeighbor: No appropriate IpIo.\n"));

+      return EFI_NOT_STARTED;

+    }

+

+  } else {

+    Sock     = Tcb->Sk;

+    TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;

+    IpIo     = TcpProto->TcpService->IpIo;

+  }

+

+  return IpIoRefreshNeighbor (IpIo, Neighbor, Timeout);

+}

+

diff --git a/NetworkPkg/TcpDxe/TcpMain.c b/NetworkPkg/TcpDxe/TcpMain.c
new file mode 100644
index 0000000..55a6d5b
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpMain.c
@@ -0,0 +1,1074 @@
+/** @file

+  Implementation of EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+/**

+  Check the integrity of the data buffer.

+

+  @param[in]  DataLen              The total length of the data buffer.

+  @param[in]  FragmentCount        The fragment count of the fragment table.

+  @param[in]  FragmentTable        Pointer to the fragment table of the data

+                                   buffer.

+

+  @retval EFI_SUCCESS              The integrity check passed.

+  @retval EFI_INVALID_PARAMETER    The integrity check failed.

+

+**/

+EFI_STATUS

+TcpChkDataBuf (

+  IN UINT32                 DataLen,

+  IN UINT32                 FragmentCount,

+  IN EFI_TCP4_FRAGMENT_DATA *FragmentTable

+  )

+{

+  UINT32 Index;

+

+  UINT32 Len;

+

+  for (Index = 0, Len = 0; Index < FragmentCount; Index++) {

+    Len = Len + FragmentTable[Index].FragmentLength;

+  }

+

+  if (DataLen != Len) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Get the current operational status.

+

+  @param[in]   This                Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[out]  Tcp4State           Pointer to the buffer to receive the current TCP

+                                   state. Optional parameter that may be NULL.

+  @param[out]  Tcp4ConfigData      Pointer to the buffer to receive the current TCP

+                                   configuration. Optional parameter that may be NULL.

+  @param[out]  Ip4ModeData         Pointer to the buffer to receive the current

+                                   IPv4 configuration. Optional parameter that may be NULL.

+  @param[out]  MnpConfigData       Pointer to the buffer to receive the current MNP

+                                   configuration data indirectly used by the TCPv4

+                                   Instance. Optional parameter that may be NULL.

+  @param[out]  SnpModeData         Pointer to the buffer to receive the current SNP

+                                   configuration data indirectly used by the TCPv4

+                                   Instance. Optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS              The mode data was read.

+  @retval EFI_NOT_STARTED          No configuration data is available because this

+                                   instance hasn't been started.

+  @retval EFI_INVALID_PARAMETER    This is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4GetModeData (

+  IN CONST  EFI_TCP4_PROTOCOL                  *This,

+  OUT       EFI_TCP4_CONNECTION_STATE          *Tcp4State      OPTIONAL,

+  OUT       EFI_TCP4_CONFIG_DATA               *Tcp4ConfigData OPTIONAL,

+  OUT       EFI_IP4_MODE_DATA                  *Ip4ModeData    OPTIONAL,

+  OUT       EFI_MANAGED_NETWORK_CONFIG_DATA    *MnpConfigData  OPTIONAL,

+  OUT       EFI_SIMPLE_NETWORK_MODE            *SnpModeData    OPTIONAL

+  )

+{

+  TCP4_MODE_DATA  TcpMode;

+  SOCKET          *Sock;

+

+  if (NULL == This) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock                    = SOCK_FROM_THIS (This);

+

+  TcpMode.Tcp4State       = Tcp4State;

+  TcpMode.Tcp4ConfigData  = Tcp4ConfigData;

+  TcpMode.Ip4ModeData     = Ip4ModeData;

+  TcpMode.MnpConfigData   = MnpConfigData;

+  TcpMode.SnpModeData     = SnpModeData;

+

+  return SockGetMode (Sock, &TcpMode);

+}

+

+/**

+  Initialize or brutally reset the operational parameters for

+  this EFI TCPv4 instance.

+

+  @param[in]   This                Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]   TcpConfigData       Pointer to the configure data to configure the

+                                   instance. Optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS              The operational settings were set, changed, or

+                                   reset successfully.

+  @retval EFI_NO_MAPPING           When using a default address, configuration

+                                   (through DHCP, BOOTP, RARP, etc.) is not

+                                   finished.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_ACCESS_DENIED        Configuring TCP instance when it is already

+                                   configured.

+  @retval EFI_DEVICE_ERROR         An unexpected network or system error occurred.

+  @retval EFI_UNSUPPORTED          One or more of the control options are not

+                                   supported in the implementation.

+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough system resources.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Configure (

+  IN EFI_TCP4_PROTOCOL        * This,

+  IN EFI_TCP4_CONFIG_DATA     * TcpConfigData OPTIONAL

+  )

+{

+  EFI_TCP4_OPTION  *Option;

+  SOCKET           *Sock;

+  EFI_STATUS       Status;

+  IP4_ADDR         Ip;

+  IP4_ADDR         SubnetMask;

+

+  if (NULL == This) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Tcp protocol related parameter check will be conducted here

+  //

+  if (NULL != TcpConfigData) {

+

+    CopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));

+    if ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (TcpConfigData->AccessPoint.ActiveFlag && (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (!TcpConfigData->AccessPoint.UseDefaultAddress) {

+

+      CopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR));

+      CopyMem (&SubnetMask, &TcpConfigData->AccessPoint.SubnetMask, sizeof (IP4_ADDR));

+      if (!NetIp4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (SubnetMask))) {

+        return EFI_INVALID_PARAMETER;

+      }

+    }

+

+    Option = TcpConfigData->ControlOption;

+    if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {

+      return EFI_UNSUPPORTED;

+    }

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  if (NULL == TcpConfigData) {

+    return SockFlush (Sock);

+  }

+

+  Status = SockConfigure (Sock, TcpConfigData);

+

+  if (EFI_NO_MAPPING == Status) {

+    Sock->ConfigureState = SO_NO_MAPPING;

+  }

+

+  return Status;

+}

+

+/**

+  Add or delete routing entries.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  DeleteRoute          If TRUE, delete the specified route from routing

+                                   table; if FALSE, add the specified route to

+                                   routing table.

+  @param[in]  SubnetAddress        The destination network.

+  @param[in]  SubnetMask           The subnet mask for the destination network.

+  @param[in]  GatewayAddress       The gateway address for this route.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance has not been

+                                   configured.

+  @retval EFI_NO_MAPPING           When using a default address, configuration

+                                   (through DHCP, BOOTP, RARP, etc.) is not

+                                   finished.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to add the

+                                   entry to the routing table.

+  @retval EFI_NOT_FOUND            This route is not in the routing table.

+  @retval EFI_ACCESS_DENIED        This route is already in the routing table.

+  @retval EFI_UNSUPPORTED          The TCP driver does not support this operation.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Routes (

+  IN EFI_TCP4_PROTOCOL           *This,

+  IN BOOLEAN                     DeleteRoute,

+  IN EFI_IPv4_ADDRESS            *SubnetAddress,

+  IN EFI_IPv4_ADDRESS            *SubnetMask,

+  IN EFI_IPv4_ADDRESS            *GatewayAddress

+  )

+{

+  SOCKET          *Sock;

+  TCP4_ROUTE_INFO RouteInfo;

+

+  if (NULL == This) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock                      = SOCK_FROM_THIS (This);

+

+  RouteInfo.DeleteRoute     = DeleteRoute;

+  RouteInfo.SubnetAddress   = SubnetAddress;

+  RouteInfo.SubnetMask      = SubnetMask;

+  RouteInfo.GatewayAddress  = GatewayAddress;

+

+  return SockRoute (Sock, &RouteInfo);

+}

+

+/**

+  Initiate a non-blocking TCP connection request for an active TCP instance.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  ConnectionToken      Pointer to the connection token to return when

+                                   the TCP three way handshake finishes.

+

+  @retval EFI_SUCCESS              The connection request successfully

+                                   initiated.

+  @retval EFI_NOT_STARTED          This EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_ACCESS_DENIED        The instance is not configured as an active one,

+                                   or it is not in Tcp4StateClosed state.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     The driver can't allocate enough resources to

+                                   initiate the active open.

+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Connect (

+  IN EFI_TCP4_PROTOCOL           *This,

+  IN EFI_TCP4_CONNECTION_TOKEN   *ConnectionToken

+  )

+{

+  SOCKET  *Sock;

+

+  if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockConnect (Sock, ConnectionToken);

+}

+

+/**

+  Listen on the passive instance to accept an incoming connection request.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  ListenToken          Pointer to the listen token to return when

+                                   operation finishes.

+

+  @retval EFI_SUCCESS              The listen token was queued successfully.

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_ACCESS_DENIED        The instatnce is not a passive one or it is not

+                                   in Tcp4StateListen state or a same listen token

+                                   has already existed in the listen token queue of

+                                   this TCP instance.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to finish

+                                   the operation.

+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Accept (

+  IN EFI_TCP4_PROTOCOL             *This,

+  IN EFI_TCP4_LISTEN_TOKEN         *ListenToken

+  )

+{

+  SOCKET  *Sock;

+

+  if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockAccept (Sock, ListenToken);

+}

+

+/**

+  Queues outgoing data into the transmit queue

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  Token                Pointer to the completion token to queue to the

+                                   transmit queue.

+

+  @retval EFI_SUCCESS              The data has been queued for transmission.

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_NO_MAPPING           When using a default address, configuration

+                                   (DHCP, BOOTP, RARP, etc.) is not finished yet.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid

+  @retval EFI_ACCESS_DENIED        One or more of the following conditions is TRUE:

+                                   * A transmit completion token with the same

+                                   Token-> CompletionToken.Event was already in the

+                                   transmission queue. * The current instance is in

+                                   Tcp4StateClosed state. * The current instance is

+                                   a passive one and it is in Tcp4StateListen

+                                   state. * User has called Close() to disconnect

+                                   this connection.

+  @retval EFI_NOT_READY            The completion token could not be queued because

+                                   the transmit queue is full.

+  @retval EFI_OUT_OF_RESOURCES     Could not queue the transmit data because of a

+                                   resource shortage.

+  @retval EFI_NETWORK_UNREACHABLE  There is no route to the destination network or

+                                   address.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Transmit (

+  IN EFI_TCP4_PROTOCOL            *This,

+  IN EFI_TCP4_IO_TOKEN            *Token

+  )

+{

+  SOCKET      *Sock;

+  EFI_STATUS  Status;

+

+  if (NULL == This ||

+      NULL == Token ||

+      NULL == Token->CompletionToken.Event ||

+      NULL == Token->Packet.TxData ||

+      0 == Token->Packet.TxData->FragmentCount ||

+      0 == Token->Packet.TxData->DataLength

+      ) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = TcpChkDataBuf (

+            Token->Packet.TxData->DataLength,

+            Token->Packet.TxData->FragmentCount,

+            Token->Packet.TxData->FragmentTable

+            );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockSend (Sock, Token);

+}

+

+/**

+  Place an asynchronous receive request into the receiving queue.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  Token                Pointer to a token that is associated with the

+                                   receive data descriptor.

+

+  @retval EFI_SUCCESS              The receive completion token was cached

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_NO_MAPPING           When using a default address, configuration

+                                   (DHCP, BOOTP, RARP, etc.) is not finished yet.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     The receive completion token could not be queued

+                                   due to a lack of system resources.

+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.

+  @retval EFI_ACCESS_DENIED        One or more of the following conditions is TRUE:

+                                   * A receive completion token with the same

+                                   Token->CompletionToken.Event was already in the

+                                   receive queue. * The current instance is in

+                                   Tcp4StateClosed state. * The current instance is

+                                   a passive one and it is in Tcp4StateListen

+                                   state. * User has called Close() to disconnect

+                                   this connection.

+  @retval EFI_CONNECTION_FIN       The communication peer has closed the connection,

+                                   and there is no any buffered data in the receive

+                                   buffer of this instance.

+  @retval EFI_NOT_READY            The receive request could not be queued because

+                                   the receive queue is full.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Receive (

+  IN EFI_TCP4_PROTOCOL           *This,

+  IN EFI_TCP4_IO_TOKEN           *Token

+  )

+{

+  SOCKET      *Sock;

+  EFI_STATUS  Status;

+

+  if (NULL == This ||

+      NULL == Token ||

+      NULL == Token->CompletionToken.Event ||

+      NULL == Token->Packet.RxData ||

+      0 == Token->Packet.RxData->FragmentCount ||

+      0 == Token->Packet.RxData->DataLength

+      ) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = TcpChkDataBuf (

+            Token->Packet.RxData->DataLength,

+            Token->Packet.RxData->FragmentCount,

+            Token->Packet.RxData->FragmentTable

+            );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockRcv (Sock, Token);

+

+}

+

+/**

+  Disconnecting a TCP connection gracefully or reset a TCP connection.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  CloseToken           Pointer to the close token to return when

+                                   operation finishes.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_ACCESS_DENIED        One or more of the following are TRUE: *

+                                   Configure() has been called with TcpConfigData

+                                   set to NULL, and this function has not returned.

+                                   * Previous Close() call on this instance has not

+                                   finished.

+  @retval EFI_INVALID_PARAMETER    One ore more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to finish the

+                                   operation.

+  @retval EFI_DEVICE_ERROR         Any unexpected category error not belonging to those

+                                   listed above.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Close (

+  IN EFI_TCP4_PROTOCOL           *This,

+  IN EFI_TCP4_CLOSE_TOKEN        *CloseToken

+  )

+{

+  SOCKET  *Sock;

+

+  if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);

+}

+

+/**

+  Abort an asynchronous connection, listen, transmission or receive request.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  Token                Pointer to a token that has been issued by

+                                   Connect(), Accept(), Transmit() or Receive(). If

+                                   NULL, all pending tokens issued by the four

+                                   functions listed above will be aborted.

+

+  @retval EFI_UNSUPPORTED          The operation is not supported in the current

+                                   implementation.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Cancel (

+  IN EFI_TCP4_PROTOCOL             *This,

+  IN EFI_TCP4_COMPLETION_TOKEN     *Token OPTIONAL

+  )

+{

+  return EFI_UNSUPPORTED;

+}

+

+/**

+  Poll to receive incoming data and transmit outgoing segments.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+

+  @retval EFI_SUCCESS              Incoming or outgoing data was processed.

+  @retval EFI_INVALID_PARAMETER    This is NULL.

+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.

+  @retval EFI_NOT_READY            No incoming or outgoing data was processed.

+  @retval EFI_TIMEOUT              Data was dropped out of the transmission or

+                                   receive queue. Consider increasing the polling

+                                   rate.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Poll (

+  IN EFI_TCP4_PROTOCOL        *This

+  )

+{

+  SOCKET      *Sock;

+  EFI_STATUS  Status;

+

+  if (NULL == This) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock   = SOCK_FROM_THIS (This);

+

+  Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);

+

+  return Status;

+}

+

+/**

+  Get the current operational status.

+

+  The GetModeData() function copies the current operational settings of this EFI TCPv6

+  Protocol instance into user-supplied buffers. This function can also be used to retrieve

+  the operational setting of underlying drivers such as IPv6, MNP, or SNP.

+

+  @param[in]  This              Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[out] Tcp6State         The buffer in which the current TCP state is

+                                returned. Optional parameter that may be NULL.

+  @param[out] Tcp6ConfigData    The buffer in which the current TCP configuration

+                                is returned. Optional parameter that may be NULL.

+  @param[out] Ip6ModeData       The buffer in which the current IPv6 configuration

+                                data used by the TCP instance is returned.

+                                Optional parameter that may be NULL.

+  @param[out] MnpConfigData     The buffer in which the current MNP configuration

+                                data indirectly used by the TCP instance is returned.

+                                Optional parameter that may be NULL.

+  @param[out] SnpModeData       The buffer in which the current SNP mode data

+                                indirectly used by the TCP instance is returned.

+                                Optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS           The mode data was read.

+  @retval EFI_NOT_STARTED       No configuration data is available because this instance hasn't

+                                been started.

+  @retval EFI_INVALID_PARAMETER This is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6GetModeData (

+  IN  EFI_TCP6_PROTOCOL                  *This,

+  OUT EFI_TCP6_CONNECTION_STATE          *Tcp6State      OPTIONAL,

+  OUT EFI_TCP6_CONFIG_DATA               *Tcp6ConfigData OPTIONAL,

+  OUT EFI_IP6_MODE_DATA                  *Ip6ModeData    OPTIONAL,

+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA    *MnpConfigData  OPTIONAL,

+  OUT EFI_SIMPLE_NETWORK_MODE            *SnpModeData    OPTIONAL

+  )

+{

+  TCP6_MODE_DATA  TcpMode;

+  SOCKET          *Sock;

+

+  if (NULL == This) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock                   = SOCK_FROM_THIS (This);

+

+  TcpMode.Tcp6State      = Tcp6State;

+  TcpMode.Tcp6ConfigData = Tcp6ConfigData;

+  TcpMode.Ip6ModeData    = Ip6ModeData;

+  TcpMode.MnpConfigData  = MnpConfigData;

+  TcpMode.SnpModeData    = SnpModeData;

+

+  return SockGetMode (Sock, &TcpMode);

+}

+

+/**

+  Initialize or brutally reset the operational parameters for this EFI TCPv6 instance.

+

+  The Configure() function does the following:

+  - Initialize this TCP instance, i.e., initialize the communication end settings and

+    specify active open or passive open for an instance.

+  - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush

+    transmission and receiving buffer directly without informing the communication peer.

+

+  No other TCPv6 Protocol operation except Poll() can be executed by this instance until

+  it is configured properly. For an active TCP instance, after a proper configuration it

+  may call Connect() to initiate a three-way handshake. For a passive TCP instance,

+  its state transits to Tcp6StateListen after configuration, and Accept() may be

+  called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL,

+  the instance is reset. The resetting process will be done brutally, the state machine will

+  be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed,

+  and no traffic is allowed through this instance.

+

+  @param[in] This               Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] Tcp6ConfigData     Pointer to the configure data to configure the instance.

+                                If Tcp6ConfigData is set to NULL, the instance is reset.

+

+  @retval EFI_SUCCESS           The operational settings were set, changed, or reset

+                                successfully.

+  @retval EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source

+                                address for this instance, but no source address was available for

+                                use.

+  @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE:

+                                - This is NULL.

+                                - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor

+                                  one of the configured IP addresses in the underlying IPv6 driver.

+                                - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast

+                                  IPv6 address.

+                                - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or

+                                  Tcp6ConfigData->AccessPoint.RemotePort is zero when

+                                  Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE.

+                                - A same access point has been configured in other TCP

+                                  instance properly.

+  @retval EFI_ACCESS_DENIED     Configuring a TCP instance when it is configured without

+                                calling Configure() with NULL to reset it.

+  @retval EFI_UNSUPPORTED       One or more of the control options are not supported in

+                                the implementation.

+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough system resources when

+                                executing Configure().

+  @retval EFI_DEVICE_ERROR      An unexpected network or system error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Configure (

+  IN EFI_TCP6_PROTOCOL        *This,

+  IN EFI_TCP6_CONFIG_DATA     *Tcp6ConfigData OPTIONAL

+  )

+{

+  EFI_TCP6_OPTION   *Option;

+  SOCKET            *Sock;

+  EFI_STATUS        Status;

+  EFI_IPv6_ADDRESS  *Ip;

+

+  if (NULL == This) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Tcp protocol related parameter check will be conducted here

+  //

+  if (NULL != Tcp6ConfigData) {

+

+    Ip = &Tcp6ConfigData->AccessPoint.RemoteAddress;

+    if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (Tcp6ConfigData->AccessPoint.ActiveFlag &&

+        (0 == Tcp6ConfigData->AccessPoint.RemotePort || NetIp6IsUnspecifiedAddr (Ip))

+        ) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    Ip = &Tcp6ConfigData->AccessPoint.StationAddress;

+    if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    Option = Tcp6ConfigData->ControlOption;

+    if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {

+      return EFI_UNSUPPORTED;

+    }

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  if (NULL == Tcp6ConfigData) {

+    return SockFlush (Sock);

+  }

+

+  Status = SockConfigure (Sock, Tcp6ConfigData);

+

+  if (EFI_NO_MAPPING == Status) {

+    Sock->ConfigureState = SO_NO_MAPPING;

+  }

+

+  return Status;

+}

+

+/**

+  Initiate a nonblocking TCP connection request for an active TCP instance.

+

+  The Connect() function will initiate an active open to the remote peer configured

+  in a current TCP instance if it is configured active. If the connection succeeds or

+  fails due to any error, the ConnectionToken->CompletionToken.Event will be signaled

+  and ConnectionToken->CompletionToken.Status will be updated accordingly. This

+  function can only be called for the TCP instance in the Tcp6StateClosed state. The

+  instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS.

+  If a TCP three-way handshake succeeds, its state will become Tcp6StateEstablished.

+  Otherwise, the state will return to Tcp6StateClosed.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] ConnectionToken     Pointer to the connection token to return when the TCP three

+                                 way handshake finishes.

+

+  @retval EFI_SUCCESS            The connection request successfully initiated and the state of

+                                 this TCP instance has been changed to Tcp6StateSynSent.

+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_ACCESS_DENIED      One or more of the following conditions are TRUE:

+                                 - This instance is not configured as an active one.

+                                 - This instance is not in Tcp6StateClosed state.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 - This is NULL.

+                                 - ConnectionToken is NULL.

+                                 - ConnectionToken->CompletionToken.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   The driver can't allocate enough resources to initiate the active open.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Connect (

+  IN EFI_TCP6_PROTOCOL           *This,

+  IN EFI_TCP6_CONNECTION_TOKEN   *ConnectionToken

+  )

+{

+  SOCKET  *Sock;

+

+  if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockConnect (Sock, ConnectionToken);

+}

+

+/**

+  Listen on the passive instance to accept an incoming connection request. This is a

+  nonblocking operation.

+

+  The Accept() function initiates an asynchronous accept request to wait for an incoming

+  connection on the passive TCP instance. If a remote peer successfully establishes a

+  connection with this instance, a new TCP instance will be created and its handle will

+  be returned in ListenToken->NewChildHandle. The newly created instance is configured

+  by inheriting the passive instance's configuration and is ready for use upon return.

+  The new instance is in the Tcp6StateEstablished state.

+

+  The ListenToken->CompletionToken.Event will be signaled when a new connection is

+  accepted, when a user aborts the listen or when a connection is reset.

+

+  This function only can be called when a current TCP instance is in Tcp6StateListen state.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] ListenToken         Pointer to the listen token to return when operation finishes.

+

+

+  @retval EFI_SUCCESS            The listen token queued successfully.

+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_ACCESS_DENIED      One or more of the following are TRUE:

+                                 - This instance is not a passive instance.

+                                 - This instance is not in Tcp6StateListen state.

+                                 - The same listen token has already existed in the listen

+                                   token queue of this TCP instance.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 - This is NULL.

+                                 - ListenToken is NULL.

+                                 - ListentToken->CompletionToken.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   Could not allocate enough resource to finish the operation.

+  @retval EFI_DEVICE_ERROR       Any unexpected error not belonging to a category listed above.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Accept (

+  IN EFI_TCP6_PROTOCOL             *This,

+  IN EFI_TCP6_LISTEN_TOKEN         *ListenToken

+  )

+{

+  SOCKET  *Sock;

+

+  if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockAccept (Sock, ListenToken);

+}

+

+/**

+  Queues outgoing data into the transmit queue.

+

+  The Transmit() function queues a sending request to this TCP instance along with the

+  user data. The status of the token is updated and the event in the token will be

+  signaled once the data is sent out or an error occurs.

+

+  @param[in] This                 Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] Token                Pointer to the completion token to queue to the transmit queue.

+

+  @retval EFI_SUCCESS             The data has been queued for transmission.

+  @retval EFI_NOT_STARTED         This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_NO_MAPPING          The underlying IPv6 driver was responsible for choosing a

+                                  source address for this instance, but no source address was

+                                  available for use.

+  @retval EFI_INVALID_PARAMETER   One or more of the following are TRUE:

+                                  - This is NULL.

+                                  - Token is NULL.

+                                  - Token->CompletionToken.Event is NULL.

+                                  - Token->Packet.TxData is NULL.

+                                  - Token->Packet.FragmentCount is zero.

+                                  - Token->Packet.DataLength is not equal to the sum of fragment lengths.

+  @retval EFI_ACCESS_DENIED       One or more of the following conditions are TRUE:

+                                  - A transmit completion token with the same Token->

+                                    CompletionToken.Event was already in the

+                                    transmission queue.

+                                  - The current instance is in Tcp6StateClosed state.

+                                  - The current instance is a passive one and it is in

+                                    Tcp6StateListen state.

+                                  - User has called Close() to disconnect this connection.

+  @retval EFI_NOT_READY           The completion token could not be queued because the

+                                  transmit queue is full.

+  @retval EFI_OUT_OF_RESOURCES    Could not queue the transmit data because of resource

+                                  shortage.

+  @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Transmit (

+  IN EFI_TCP6_PROTOCOL            *This,

+  IN EFI_TCP6_IO_TOKEN            *Token

+  )

+{

+  SOCKET      *Sock;

+  EFI_STATUS  Status;

+

+  if (NULL == This ||

+      NULL == Token ||

+      NULL == Token->CompletionToken.Event ||

+      NULL == Token->Packet.TxData ||

+      0 == Token->Packet.TxData->FragmentCount ||

+      0 == Token->Packet.TxData->DataLength

+      ) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = TcpChkDataBuf (

+            Token->Packet.TxData->DataLength,

+            Token->Packet.TxData->FragmentCount,

+            (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.TxData->FragmentTable

+            );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockSend (Sock, Token);

+}

+

+/**

+  Places an asynchronous receive request into the receiving queue.

+

+  The Receive() function places a completion token into the receive packet queue. This

+  function is always asynchronous. The caller must allocate the Token->CompletionToken.Event

+  and the FragmentBuffer used to receive data. The caller also must fill the DataLength that

+  represents the whole length of all FragmentBuffer. When the receive operation completes, the

+  EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData

+  fields, and the Token->CompletionToken.Event is signaled. If data obtained, the data and its length

+  will be copied into the FragmentTable; at the same time the full length of received data will

+  be recorded in the DataLength fields. Providing a proper notification function and context

+  for the event enables the user to receive the notification and receiving status. That

+  notification function is guaranteed to not be re-entered.

+

+  @param[in] This               Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] Token              Pointer to a token that is associated with the receive data

+                                descriptor.

+

+  @retval EFI_SUCCESS            The receive completion token was cached.

+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_NO_MAPPING         The underlying IPv6 driver was responsible for choosing a source

+                                 address for this instance, but no source address was available for use.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token->CompletionToken.Event is NULL.

+                                 - Token->Packet.RxData is NULL.

+                                 - Token->Packet.RxData->DataLength is 0.

+                                 - The Token->Packet.RxData->DataLength is not the

+                                   sum of all FragmentBuffer length in FragmentTable.

+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued due to a lack of

+                                 system resources (usually memory).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+                                 The EFI TCPv6 Protocol instance has been reset to startup defaults.

+  @retval EFI_ACCESS_DENIED      One or more of the following conditions is TRUE:

+                                 - A receive completion token with the same Token->CompletionToken.Event

+                                   was already in the receive queue.

+                                 - The current instance is in Tcp6StateClosed state.

+                                 - The current instance is a passive one and it is in

+                                   Tcp6StateListen state.

+                                 - User has called Close() to disconnect this connection.

+  @retval EFI_CONNECTION_FIN     The communication peer has closed the connection and there is no

+                                 buffered data in the receive buffer of this instance.

+  @retval EFI_NOT_READY          The receive request could not be queued because the receive queue is full.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Receive (

+  IN EFI_TCP6_PROTOCOL           *This,

+  IN EFI_TCP6_IO_TOKEN           *Token

+  )

+{

+  SOCKET      *Sock;

+  EFI_STATUS  Status;

+

+  if (NULL == This ||

+      NULL == Token ||

+      NULL == Token->CompletionToken.Event ||

+      NULL == Token->Packet.RxData ||

+      0 == Token->Packet.RxData->FragmentCount ||

+      0 == Token->Packet.RxData->DataLength

+      ) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = TcpChkDataBuf (

+            Token->Packet.RxData->DataLength,

+            Token->Packet.RxData->FragmentCount,

+            (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.RxData->FragmentTable

+            );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockRcv (Sock, Token);

+}

+

+/**

+  Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a

+  nonblocking operation.

+

+  Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered

+  transmission data will be sent by the TCP driver, and the current instance will have a graceful close

+  working flow described as RFC 793 if AbortOnClose is set to FALSE. Otherwise, a rest packet

+  will be sent by TCP driver to fast disconnect this connection. When the close operation completes

+  successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous

+  operations are signaled, and any buffers used for TCP network traffic are flushed.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] CloseToken          Pointer to the close token to return when operation finishes.

+

+  @retval EFI_SUCCESS            The Close() was called successfully.

+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_ACCESS_DENIED      One or more of the following are TRUE:

+                                 - CloseToken or CloseToken->CompletionToken.Event is already in use.

+                                 - Previous Close() call on this instance has not finished.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 - This is NULL.

+                                 - CloseToken is NULL.

+                                 - CloseToken->CompletionToken.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   Could not allocate enough resource to finish the operation.

+  @retval EFI_DEVICE_ERROR       Any unexpected error not belonging to error categories given above.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Close (

+  IN EFI_TCP6_PROTOCOL           *This,

+  IN EFI_TCP6_CLOSE_TOKEN        *CloseToken

+  )

+{

+  SOCKET  *Sock;

+

+  if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock = SOCK_FROM_THIS (This);

+

+  return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);

+}

+

+/**

+  Abort an asynchronous connection, listen, transmission, or receive request.

+

+  The Cancel() function aborts a pending connection, listen, transmit, or

+  receive request.

+

+  If Token is not NULL and the token is in the connection, listen, transmission,

+  or receive queue when it is being cancelled, its Token->Status will be set

+  to EFI_ABORTED, and then Token->Event will be signaled.

+

+  If the token is not in one of the queues, which usually means that the

+  asynchronous operation has completed, EFI_NOT_FOUND is returned.

+

+  If Token is NULL all asynchronous token issued by Connect(), Accept(),

+  Transmit(), and Receive() will be aborted.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] Token               Pointer to a token that has been issued by

+                                 EFI_TCP6_PROTOCOL.Connect(),

+                                 EFI_TCP6_PROTOCOL.Accept(),

+                                 EFI_TCP6_PROTOCOL.Transmit() or

+                                 EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending

+                                 tokens issued by above four functions will be aborted. Type

+                                 EFI_TCP6_COMPLETION_TOKEN is defined in

+                                 EFI_TCP_PROTOCOL.Connect().

+

+  @retval EFI_UNSUPPORTED        The implementation does not support this function.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Cancel (

+  IN EFI_TCP6_PROTOCOL           *This,

+  IN EFI_TCP6_COMPLETION_TOKEN   *Token OPTIONAL

+  )

+{

+  return EFI_UNSUPPORTED;

+}

+

+/**

+  Poll to receive incoming data and transmit outgoing segments.

+

+  The Poll() function increases the rate that data is moved between the network

+  and application, and can be called when the TCP instance is created successfully.

+  Its use is optional.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+

+  @retval EFI_SUCCESS            Incoming or outgoing data was processed.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+  @retval EFI_NOT_READY          No incoming or outgoing data is processed.

+  @retval EFI_TIMEOUT            Data was dropped out of the transmission or receive queue.

+                                 Consider increasing the polling rate.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Poll (

+  IN EFI_TCP6_PROTOCOL        *This

+  )

+{

+  SOCKET      *Sock;

+  EFI_STATUS  Status;

+

+  if (NULL == This) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Sock   = SOCK_FROM_THIS (This);

+

+  Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);

+

+  return Status;

+}

+

diff --git a/NetworkPkg/TcpDxe/TcpMain.h b/NetworkPkg/TcpDxe/TcpMain.h
new file mode 100644
index 0000000..fabffc2
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpMain.h
@@ -0,0 +1,758 @@
+/** @file

+  Declaration of protocol interfaces in EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.

+  It is the common head file for all Tcp*.c in TCP driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _TCP_MAIN_H_

+#define _TCP_MAIN_H_

+

+#include <Protocol/ServiceBinding.h>

+#include <Protocol/DriverBinding.h>

+#include <Library/IpIoLib.h>

+#include <Library/DevicePathLib.h>

+

+#include "Socket.h"

+#include "TcpProto.h"

+#include "TcpDriver.h"

+#include "TcpFunc.h"

+

+extern UINT16                        mTcp4RandomPort;

+extern UINT16                        mTcp6RandomPort;

+extern CHAR16                        *mTcpStateName[];

+extern EFI_COMPONENT_NAME_PROTOCOL   gTcpComponentName;

+extern EFI_COMPONENT_NAME2_PROTOCOL  gTcpComponentName2;

+

+extern LIST_ENTRY                    mTcpRunQue;

+extern LIST_ENTRY                    mTcpListenQue;

+extern TCP_SEQNO                     mTcpGlobalIss;

+extern UINT32                        mTcpTick;

+

+///

+/// 30 seconds.

+///

+#define TCP6_KEEP_NEIGHBOR_TIME    30

+///

+/// 5 seconds, since 1 tick equals 200ms.

+///

+#define TCP6_REFRESH_NEIGHBOR_TICK 25

+

+#define TCP_EXPIRE_TIME            65535

+

+///

+/// The implementation selects the initial send sequence number and the unit to

+/// be added when it is increased.

+///

+#define TCP_BASE_ISS               0x4d7e980b

+#define TCP_ISS_INCREMENT_1        2048

+#define TCP_ISS_INCREMENT_2        100

+

+typedef union {

+  EFI_TCP4_CONFIG_DATA  Tcp4CfgData;

+  EFI_TCP6_CONFIG_DATA  Tcp6CfgData;

+} TCP_CONFIG_DATA;

+

+typedef union {

+  EFI_TCP4_ACCESS_POINT  Tcp4Ap;

+  EFI_TCP6_ACCESS_POINT  Tcp6Ap;

+} TCP_ACCESS_POINT;

+

+typedef struct _TCP4_MODE_DATA {

+  EFI_TCP4_CONNECTION_STATE       *Tcp4State;

+  EFI_TCP4_CONFIG_DATA            *Tcp4ConfigData;

+  EFI_IP4_MODE_DATA               *Ip4ModeData;

+  EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;

+  EFI_SIMPLE_NETWORK_MODE         *SnpModeData;

+} TCP4_MODE_DATA;

+

+typedef struct _TCP6_MODE_DATA {

+  EFI_TCP6_CONNECTION_STATE       *Tcp6State;

+  EFI_TCP6_CONFIG_DATA            *Tcp6ConfigData;

+  EFI_IP6_MODE_DATA               *Ip6ModeData;

+  EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;

+  EFI_SIMPLE_NETWORK_MODE         *SnpModeData;

+} TCP6_MODE_DATA;

+

+typedef struct _TCP4_ROUTE_INFO {

+  BOOLEAN           DeleteRoute;

+  EFI_IPv4_ADDRESS  *SubnetAddress;

+  EFI_IPv4_ADDRESS  *SubnetMask;

+  EFI_IPv4_ADDRESS  *GatewayAddress;

+} TCP4_ROUTE_INFO;

+

+//

+// EFI_TCP4_PROTOCOL definitions.

+//

+

+/**

+  Get the current operational status.

+

+  @param[in]   This                Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[out]  Tcp4State           Pointer to the buffer to receive the current TCP

+                                   state. Optional parameter that may be NULL.

+  @param[out]  Tcp4ConfigData      Pointer to the buffer to receive the current TCP

+                                   configuration. Optional parameter that may be NULL.

+  @param[out]  Ip4ModeData         Pointer to the buffer to receive the current

+                                   IPv4 configuration. Optional parameter that may be NULL.

+  @param[out]  MnpConfigData       Pointer to the buffer to receive the current MNP

+                                   configuration data indirectly used by the TCPv4

+                                   Instance. Optional parameter that may be NULL.

+  @param[out]  SnpModeData         Pointer to the buffer to receive the current SNP

+                                   configuration data indirectly used by the TCPv4

+                                   Instance. Optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS              The mode data was read.

+  @retval EFI_NOT_STARTED          No configuration data is available because this

+                                   instance hasn't been started.

+  @retval EFI_INVALID_PARAMETER    This is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4GetModeData (

+  IN CONST  EFI_TCP4_PROTOCOL                  *This,

+  OUT       EFI_TCP4_CONNECTION_STATE          *Tcp4State      OPTIONAL,

+  OUT       EFI_TCP4_CONFIG_DATA               *Tcp4ConfigData OPTIONAL,

+  OUT       EFI_IP4_MODE_DATA                  *Ip4ModeData    OPTIONAL,

+  OUT       EFI_MANAGED_NETWORK_CONFIG_DATA    *MnpConfigData  OPTIONAL,

+  OUT       EFI_SIMPLE_NETWORK_MODE            *SnpModeData    OPTIONAL

+  );

+

+/**

+  Initialize or brutally reset the operational parameters for

+  this EFI TCPv4 instance.

+

+  @param[in]   This                Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]   TcpConfigData       Pointer to the configure data to configure the

+                                   instance. Optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS              The operational settings are set, changed, or

+                                   reset successfully.

+  @retval EFI_NO_MAPPING           When using a default address, configuration

+                                   (through DHCP, BOOTP, RARP, etc.) is not

+                                   finished.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_ACCESS_DENIED        Configuring the TCP instance when it is already

+                                   configured.

+  @retval EFI_DEVICE_ERROR         An unexpected network or system error occurred.

+  @retval EFI_UNSUPPORTED          One or more of the control options are not

+                                   supported in the implementation.

+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough system resources.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Configure (

+  IN EFI_TCP4_PROTOCOL        * This,

+  IN EFI_TCP4_CONFIG_DATA     * TcpConfigData OPTIONAL

+  );

+

+/**

+  Add or delete routing entries.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  DeleteRoute          If TRUE, delete the specified route from routing

+                                   table; if FALSE, add the specified route to

+                                   routing table.

+  @param[in]  SubnetAddress        The destination network.

+  @param[in]  SubnetMask           The subnet mask for the destination network.

+  @param[in]  GatewayAddress       The gateway address for this route.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance has not been

+                                   configured.

+  @retval EFI_NO_MAPPING           When using a default address, configuration

+                                   (through DHCP, BOOTP, RARP, etc.) is not

+                                   finished.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to add the

+                                   entry to the routing table.

+  @retval EFI_NOT_FOUND            This route is not in the routing table.

+  @retval EFI_ACCESS_DENIED        This route is already in the routing table.

+  @retval EFI_UNSUPPORTED          The TCP driver does not support this operation.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Routes (

+  IN EFI_TCP4_PROTOCOL           *This,

+  IN BOOLEAN                     DeleteRoute,

+  IN EFI_IPv4_ADDRESS            *SubnetAddress,

+  IN EFI_IPv4_ADDRESS            *SubnetMask,

+  IN EFI_IPv4_ADDRESS            *GatewayAddress

+  );

+

+/**

+  Initiate a nonblocking TCP connection request for an active TCP instance.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  ConnectionToken      Pointer to the connection token to return when

+                                   the TCP three way handshake finishes.

+

+  @retval EFI_SUCCESS              The connection request is successfully

+                                   initiated.

+  @retval EFI_NOT_STARTED          This EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_ACCESS_DENIED        The instance is not configured as an active one

+                                   or it is not in Tcp4StateClosed state.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     The driver can't allocate enough resources to

+                                   initiate the active open.

+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Connect (

+  IN EFI_TCP4_PROTOCOL           *This,

+  IN EFI_TCP4_CONNECTION_TOKEN   *ConnectionToken

+  );

+

+/**

+  Listen on the passive instance to accept an incoming connection request.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  ListenToken          Pointer to the listen token to return when

+                                   operation finishes.

+

+  @retval EFI_SUCCESS              The listen token has been queued successfully.

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_ACCESS_DENIED        The instatnce is not a passive one or it is not

+                                   in Tcp4StateListen state, or a same listen token

+                                   has already existed in the listen token queue of

+                                   this TCP instance.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to finish

+                                   the operation.

+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Accept (

+  IN EFI_TCP4_PROTOCOL             *This,

+  IN EFI_TCP4_LISTEN_TOKEN         *ListenToken

+  );

+

+/**

+  Queues outgoing data into the transmit queue

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance

+  @param[in]  Token                Pointer to the completion token to queue to the

+                                   transmit queue

+

+  @retval EFI_SUCCESS              The data has been queued for transmission

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_NO_MAPPING           When using a default address, configuration

+                                   (DHCP, BOOTP, RARP, etc.) is not finished yet.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid

+  @retval EFI_ACCESS_DENIED        One or more of the following conditions is TRUE:

+                                   * A transmit completion token with the same

+                                   Token-> CompletionToken.Event was already in the

+                                   transmission queue. * The current instance is in

+                                   Tcp4StateClosed state * The current instance is

+                                   a passive one and it is in Tcp4StateListen

+                                   state. * User has called Close() to disconnect

+                                   this connection.

+  @retval EFI_NOT_READY            The completion token could not be queued because

+                                   the transmit queue is full.

+  @retval EFI_OUT_OF_RESOURCES     Could not queue the transmit data because of a

+                                   resource shortage.

+  @retval EFI_NETWORK_UNREACHABLE  There is no route to the destination network or

+                                   address.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Transmit (

+  IN EFI_TCP4_PROTOCOL            *This,

+  IN EFI_TCP4_IO_TOKEN            *Token

+  );

+

+/**

+  Place an asynchronous receive request into the receiving queue.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  Token                Pointer to a token that is associated with the

+                                   receive data descriptor.

+

+  @retval EFI_SUCCESS              The receive completion token was cached.

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_NO_MAPPING           When using a default address, configuration

+                                   (DHCP, BOOTP, RARP, etc.) is not finished yet.

+  @retval EFI_INVALID_PARAMETER    One or more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     The receive completion token could not be queued

+                                   due to a lack of system resources.

+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.

+  @retval EFI_ACCESS_DENIED        One or more of the following conditions is TRUE:

+                                   * A receive completion token with the same

+                                   Token->CompletionToken.Event was already in the

+                                   receive queue. * The current instance is in

+                                   Tcp4StateClosed state. * The current instance is

+                                   a passive one and it is in Tcp4StateListen

+                                   state. * User has called Close() to disconnect

+                                   this connection.

+  @retval EFI_CONNECTION_FIN       The communication peer has closed the connection

+                                   and there is no buffered data in the receive

+                                   buffer of this instance.

+  @retval EFI_NOT_READY            The receive request could not be queued because

+                                   the receive queue is full.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Receive (

+  IN EFI_TCP4_PROTOCOL           *This,

+  IN EFI_TCP4_IO_TOKEN           *Token

+  );

+

+/**

+  Disconnecting a TCP connection gracefully or reset a TCP connection.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  CloseToken           Pointer to the close token to return when

+                                   operation finishes.

+

+  @retval EFI_SUCCESS              The operation completed successfully.

+  @retval EFI_NOT_STARTED          The EFI_TCP4_PROTOCOL instance hasn't been

+                                   configured.

+  @retval EFI_ACCESS_DENIED        One or more of the following are TRUE: *

+                                   Configure() has been called with TcpConfigData

+                                   set to NULL and this function has not returned.

+                                   * Previous Close() call on this instance has not

+                                   finished.

+  @retval EFI_INVALID_PARAMETER    One ore more parameters are invalid.

+  @retval EFI_OUT_OF_RESOURCES     Could not allocate enough resources to finish the

+                                   operation.

+  @retval EFI_DEVICE_ERROR         Any unexpected error not belonging to the error

+                                   categories given above.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Close (

+  IN EFI_TCP4_PROTOCOL           *This,

+  IN EFI_TCP4_CLOSE_TOKEN        *CloseToken

+  );

+

+/**

+  Abort an asynchronous connection, listen, transmission or receive request.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+  @param[in]  Token                Pointer to a token that has been issued by

+                                   Connect(), Accept(), Transmit() or Receive(). If

+                                   NULL, all pending tokens issued by the above four

+                                   functions will be aborted.

+

+  @retval EFI_UNSUPPORTED          The operation is not supported in the current

+                                   implementation.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Cancel (

+  IN EFI_TCP4_PROTOCOL             *This,

+  IN EFI_TCP4_COMPLETION_TOKEN     *Token OPTIONAL

+  );

+

+/**

+  Poll to receive incoming data and transmit outgoing segments.

+

+  @param[in]  This                 Pointer to the EFI_TCP4_PROTOCOL instance.

+

+  @retval EFI_SUCCESS              Incoming or outgoing data was processed.

+  @retval EFI_INVALID_PARAMETER    This is NULL.

+  @retval EFI_DEVICE_ERROR         An unexpected system or network error occurred.

+  @retval EFI_NOT_READY            No incoming or outgoing data was processed.

+  @retval EFI_TIMEOUT              Data was dropped out of the transmission or

+                                   receive queue. Consider increasing the polling

+                                   rate.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp4Poll (

+  IN EFI_TCP4_PROTOCOL        *This

+  );

+

+//

+// EFI_TCP6_PROTOCOL definitions.

+//

+

+/**

+  Get the current operational status.

+

+  The GetModeData() function copies the current operational settings of this EFI TCPv6

+  Protocol instance into user-supplied buffers. This function can also be used to retrieve

+  the operational setting of underlying drivers such as IPv6, MNP, or SNP.

+

+  @param[in]  This              Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[out] Tcp6State         The buffer in which the current TCP state is

+                                returned. Optional parameter that may be NULL.

+  @param[out] Tcp6ConfigData    The buffer in which the current TCP configuration

+                                is returned. Optional parameter that may be NULL.

+  @param[out] Ip6ModeData       The buffer in which the current IPv6 configuration

+                                data used by the TCP instance is returned.

+                                Optional parameter that may be NULL.

+  @param[out] MnpConfigData     The buffer in which the current MNP configuration

+                                data used indirectly by the TCP instance is returned.

+                                Optional parameter that may be NULL.

+  @param[out] SnpModeData       The buffer in which the current SNP mode data

+                                 used indirectly by the TCP instance is returned.

+                                Optional parameter that may be NULL.

+

+  @retval EFI_SUCCESS           The mode data was read.

+  @retval EFI_NOT_STARTED       No configuration data is available because this instance hasn't

+                                been started.

+  @retval EFI_INVALID_PARAMETER This is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6GetModeData (

+  IN  EFI_TCP6_PROTOCOL                  *This,

+  OUT EFI_TCP6_CONNECTION_STATE          *Tcp6State      OPTIONAL,

+  OUT EFI_TCP6_CONFIG_DATA               *Tcp6ConfigData OPTIONAL,

+  OUT EFI_IP6_MODE_DATA                  *Ip6ModeData    OPTIONAL,

+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA    *MnpConfigData  OPTIONAL,

+  OUT EFI_SIMPLE_NETWORK_MODE            *SnpModeData    OPTIONAL

+  );

+

+/**

+  Initialize or brutally reset the operational parameters for this EFI TCPv6 instance.

+

+  The Configure() function does the following:

+  - Initialize this TCP instance, i.e., initialize the communication end settings and

+    specify active open or passive open for an instance.

+  - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush

+    transmission and receiving buffer directly without informing the communication peer.

+

+  No other TCPv6 Protocol operation except Poll() can be executed by this instance until

+  it is configured properly. For an active TCP instance, after a proper configuration it

+  may call Connect() to initiates the three-way handshake. For a passive TCP instance,

+  its state will transit to Tcp6StateListen after configuration, and Accept() may be

+  called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL,

+  the instance is reset. Resetting process will be done brutally, the state machine will

+  be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed,

+  and no traffic is allowed through this instance.

+

+  @param[in] This               Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] Tcp6ConfigData     Pointer to the configure data to configure the instance.

+                                If Tcp6ConfigData is set to NULL, the instance is reset.

+

+  @retval EFI_SUCCESS           The operational settings were set, changed, or reset

+                                successfully.

+  @retval EFI_NO_MAPPING        The underlying IPv6 driver was responsible for choosing a source

+                                address for this instance, but no source address was available for

+                                use.

+  @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE:

+                                - This is NULL.

+                                - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor

+                                  one of the configured IP addresses in the underlying IPv6 driver.

+                                - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast

+                                  IPv6 address.

+                                - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or

+                                  Tcp6ConfigData->AccessPoint.RemotePort is zero when

+                                  Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE.

+                                - A same access point has been configured in other TCP

+                                  instance properly.

+  @retval EFI_ACCESS_DENIED     Configuring TCP instance when it is configured without

+                                calling Configure() with NULL to reset it.

+  @retval EFI_UNSUPPORTED       One or more of the control options are not supported in

+                                the implementation.

+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough system resources when

+                                executing Configure().

+  @retval EFI_DEVICE_ERROR      An unexpected network or system error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Configure (

+  IN EFI_TCP6_PROTOCOL        *This,

+  IN EFI_TCP6_CONFIG_DATA     *Tcp6ConfigData OPTIONAL

+  );

+

+/**

+  Initiate a nonblocking TCP connection request for an active TCP instance.

+

+  The Connect() function will initiate an active open to the remote peer configured

+  in current TCP instance if it is configured active. If the connection succeeds or

+  fails due to an error, the ConnectionToken->CompletionToken.Event will be signaled,

+  and ConnectionToken->CompletionToken.Status will be updated accordingly. This

+  function can only be called for the TCP instance in Tcp6StateClosed state. The

+  instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS.

+  If TCP three-way handshake succeeds, its state will become Tcp6StateEstablished;

+  otherwise, the state will return to Tcp6StateClosed.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] ConnectionToken     Pointer to the connection token to return when the TCP

+                                 three-way handshake finishes.

+

+  @retval EFI_SUCCESS            The connection request successfully initiated and the state of

+                                 this TCP instance has been changed to Tcp6StateSynSent.

+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_ACCESS_DENIED      One or more of the following conditions are TRUE:

+                                 - This instance is not configured as an active instance.

+                                 - This instance is not in Tcp6StateClosed state.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 - This is NULL.

+                                 - ConnectionToken is NULL.

+                                 - ConnectionToken->CompletionToken.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   The driver can't allocate enough resources to initiate the active open.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Connect (

+  IN EFI_TCP6_PROTOCOL           *This,

+  IN EFI_TCP6_CONNECTION_TOKEN   *ConnectionToken

+  );

+

+/**

+  Listen on the passive instance to accept an incoming connection request. This is a

+  nonblocking operation.

+

+  The Accept() function initiates an asynchronous accept request to wait for an incoming

+  connection on the passive TCP instance. If a remote peer successfully establishes a

+  connection with this instance, a new TCP instance will be created and its handle will

+  be returned in ListenToken->NewChildHandle. The newly created instance is configured

+  by inheriting the passive instance's configuration, and is ready for use upon return.

+  The new instance is in the Tcp6StateEstablished state.

+

+  The ListenToken->CompletionToken.Event will be signaled when a new connection is

+  accepted, user aborts the listen or connection is reset.

+

+  This function only can be called when the current TCP instance is in Tcp6StateListen state.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] ListenToken         Pointer to the listen token to return when the operation finishes.

+

+

+  @retval EFI_SUCCESS            The listen token was been queued successfully.

+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_ACCESS_DENIED      One or more of the following are TRUE:

+                                 - This instance is not a passive instance.

+                                 - This instance is not in Tcp6StateListen state.

+                                 - The same listen token has already existed in the listen

+                                   token queue of this TCP instance.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 - This is NULL.

+                                 - ListenToken is NULL.

+                                 - ListentToken->CompletionToken.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   Could not allocate enough resources to finish the operation.

+  @retval EFI_DEVICE_ERROR       Any unexpected error not belonging to the error

+                                 categories given above.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Accept (

+  IN EFI_TCP6_PROTOCOL             *This,

+  IN EFI_TCP6_LISTEN_TOKEN         *ListenToken

+  );

+

+/**

+  Queues outgoing data into the transmit queue.

+

+  The Transmit() function queues a sending request to this TCP instance along with the

+  user data. The status of the token is updated and the event in the token will be

+  signaled once the data is sent out or some error occurs.

+

+  @param[in] This                 Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] Token                Pointer to the completion token to queue to the transmit queue.

+

+  @retval EFI_SUCCESS             The data has been queued for transmission.

+  @retval EFI_NOT_STARTED         This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_NO_MAPPING          The underlying IPv6 driver was responsible for choosing a

+                                  source address for this instance, but no source address was

+                                  available for use.

+  @retval EFI_INVALID_PARAMETER   One or more of the following are TRUE:

+                                  - This is NULL.

+                                  - Token is NULL.

+                                  - Token->CompletionToken.Event is NULL.

+                                  - Token->Packet.TxData is NULL.

+                                  - Token->Packet.FragmentCount is zero.

+                                  - Token->Packet.DataLength is not equal to the sum of fragment lengths.

+  @retval EFI_ACCESS_DENIED       One or more of the following conditions are TRUE:

+                                  - A transmit completion token with the same Token->

+                                    CompletionToken.Event was already in the

+                                    transmission queue.

+                                  - The current instance is in Tcp6StateClosed state.

+                                  - The current instance is a passive one and it is in

+                                    Tcp6StateListen state.

+                                  - User has called Close() to disconnect this connection.

+  @retval EFI_NOT_READY           The completion token could not be queued because the

+                                  transmit queue is full.

+  @retval EFI_OUT_OF_RESOURCES    Could not queue the transmit data because of a resource

+                                  shortage.

+  @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Transmit (

+  IN EFI_TCP6_PROTOCOL            *This,

+  IN EFI_TCP6_IO_TOKEN            *Token

+  );

+

+/**

+  Places an asynchronous receive request into the receiving queue.

+

+  The Receive() function places a completion token into the receive packet queue. This

+  function is always asynchronous. The caller must allocate the Token->CompletionToken.Event

+  and the FragmentBuffer used to receive data. The caller also must fill the DataLength, which

+  represents the whole length of all FragmentBuffer. When the receive operation completes, the

+  EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData

+  fields, and the Token->CompletionToken.Event is signaled. If data is obtained, the data and its length

+  will be copied into the FragmentTable. At the same time the full length of received data will

+  be recorded in the DataLength fields. Providing a proper notification function and context

+  for the event enables the user to receive the notification and receiving status. That

+  notification function is guaranteed to not be re-entered.

+

+  @param[in] This               Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] Token              Pointer to a token that is associated with the receive data

+                                descriptor.

+

+  @retval EFI_SUCCESS            The receive completion token was cached.

+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_NO_MAPPING         The underlying IPv6 driver was responsible for choosing a source

+                                 address for this instance, but no source address was available for use.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 - This is NULL.

+                                 - Token is NULL.

+                                 - Token->CompletionToken.Event is NULL.

+                                 - Token->Packet.RxData is NULL.

+                                 - Token->Packet.RxData->DataLength is 0.

+                                 - The Token->Packet.RxData->DataLength is not the

+                                   sum of all FragmentBuffer length in FragmentTable.

+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued due to a lack of

+                                 system resources (usually memory).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+                                 The EFI TCPv6 Protocol instance has been reset to startup defaults.

+  @retval EFI_ACCESS_DENIED      One or more of the following conditions is TRUE:

+                                 - A receive completion token with the same Token->CompletionToken.Event

+                                   was already in the receive queue.

+                                 - The current instance is in Tcp6StateClosed state.

+                                 - The current instance is a passive one and it is in

+                                   Tcp6StateListen state.

+                                 - The user has called Close() to disconnect this connection.

+  @retval EFI_CONNECTION_FIN     The communication peer has closed the connection, and there is no

+                                 buffered data in the receive buffer of this instance.

+  @retval EFI_NOT_READY          The receive request could not be queued because the receive queue is full.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Receive (

+  IN EFI_TCP6_PROTOCOL           *This,

+  IN EFI_TCP6_IO_TOKEN           *Token

+  );

+

+/**

+  Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a

+  nonblocking operation.

+

+  Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered

+  transmission data will be sent by the TCP driver, and the current instance will have a graceful close

+  working flow described as RFC 793 if AbortOnClose is set to FALSE, otherwise, a rest packet

+  will be sent by TCP driver to fast disconnect this connection. When the close operation completes

+  successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous

+  operations are signaled, and any buffers used for TCP network traffic are flushed.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] CloseToken          Pointer to the close token to return when operation finishes.

+

+  @retval EFI_SUCCESS            The Close() was called successfully.

+  @retval EFI_NOT_STARTED        This EFI TCPv6 Protocol instance has not been configured.

+  @retval EFI_ACCESS_DENIED      One or more of the following are TRUE:

+                                 - CloseToken or CloseToken->CompletionToken.Event is already in use.

+                                 - Previous Close() call on this instance has not finished.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 - This is NULL.

+                                 - CloseToken is NULL.

+                                 - CloseToken->CompletionToken.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   Could not allocate enough resources to finish the operation.

+  @retval EFI_DEVICE_ERROR       Any unexpected error not belonging to the error categories given above.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Close (

+  IN EFI_TCP6_PROTOCOL           *This,

+  IN EFI_TCP6_CLOSE_TOKEN        *CloseToken

+  );

+

+/**

+  Abort an asynchronous connection, listen, transmission or receive request.

+

+  The Cancel() function aborts a pending connection, listen, transmit or

+  receive request.

+

+  If Token is not NULL and the token is in the connection, listen, transmission

+  or receive queue when it is being cancelled, its Token->Status will be set

+  to EFI_ABORTED and then Token->Event will be signaled.

+

+  If the token is not in one of the queues, which usually means that the

+  asynchronous operation has completed, EFI_NOT_FOUND is returned.

+

+  If Token is NULL all asynchronous token issued by Connect(), Accept(),

+  Transmit() and Receive() will be aborted.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+  @param[in] Token               Pointer to a token that has been issued by

+                                 EFI_TCP6_PROTOCOL.Connect(),

+                                 EFI_TCP6_PROTOCOL.Accept(),

+                                 EFI_TCP6_PROTOCOL.Transmit() or

+                                 EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending

+                                 tokens issued by above four functions will be aborted. Type

+                                 EFI_TCP6_COMPLETION_TOKEN is defined in

+                                 EFI_TCP_PROTOCOL.Connect().

+

+  @retval EFI_UNSUPPORTED        The implementation does not support this function.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Cancel (

+  IN EFI_TCP6_PROTOCOL           *This,

+  IN EFI_TCP6_COMPLETION_TOKEN   *Token OPTIONAL

+  );

+

+/**

+  Poll to receive incoming data and transmit outgoing segments.

+

+  The Poll() function increases the rate that data is moved between the network

+  and application and can be called when the TCP instance is created successfully.

+  Its use is optional.

+

+  @param[in] This                Pointer to the EFI_TCP6_PROTOCOL instance.

+

+  @retval EFI_SUCCESS            Incoming or outgoing data was processed.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+  @retval EFI_NOT_READY          No incoming or outgoing data is processed.

+  @retval EFI_TIMEOUT            Data was dropped out of the transmission or receive queue.

+                                 Consider increasing the polling rate.

+

+**/

+EFI_STATUS

+EFIAPI

+Tcp6Poll (

+  IN EFI_TCP6_PROTOCOL        *This

+  );

+

+#endif

diff --git a/NetworkPkg/TcpDxe/TcpMisc.c b/NetworkPkg/TcpDxe/TcpMisc.c
new file mode 100644
index 0000000..492ec35
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpMisc.c
@@ -0,0 +1,1281 @@
+/** @file

+  Misc support routines for TCP driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+LIST_ENTRY      mTcpRunQue = {

+  &mTcpRunQue,

+  &mTcpRunQue

+};

+

+LIST_ENTRY      mTcpListenQue = {

+  &mTcpListenQue,

+  &mTcpListenQue

+};

+

+TCP_SEQNO       mTcpGlobalIss = TCP_BASE_ISS;

+

+CHAR16          *mTcpStateName[] = {

+  L"TCP_CLOSED",

+  L"TCP_LISTEN",

+  L"TCP_SYN_SENT",

+  L"TCP_SYN_RCVD",

+  L"TCP_ESTABLISHED",

+  L"TCP_FIN_WAIT_1",

+  L"TCP_FIN_WAIT_2",

+  L"TCP_CLOSING",

+  L"TCP_TIME_WAIT",

+  L"TCP_CLOSE_WAIT",

+  L"TCP_LAST_ACK"

+};

+

+

+/**

+  Initialize the Tcb local related members.

+

+  @param[in, out]  Tcb               Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpInitTcbLocal (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  //

+  // Compute the checksum of the fixed parts of pseudo header

+  //

+  if (Tcb->Sk->IpVersion == IP_VERSION_4) {

+    Tcb->HeadSum = NetPseudoHeadChecksum (

+                    Tcb->LocalEnd.Ip.Addr[0],

+                    Tcb->RemoteEnd.Ip.Addr[0],

+                    0x06,

+                    0

+                    );

+  } else {

+    Tcb->HeadSum = NetIp6PseudoHeadChecksum (

+                    &Tcb->LocalEnd.Ip.v6,

+                    &Tcb->RemoteEnd.Ip.v6,

+                    0x06,

+                    0

+                    );

+  }

+

+  Tcb->Iss    = TcpGetIss ();

+  Tcb->SndUna = Tcb->Iss;

+  Tcb->SndNxt = Tcb->Iss;

+

+  Tcb->SndWl2 = Tcb->Iss;

+  Tcb->SndWnd = 536;

+

+  Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk);

+

+  //

+  // First window size is never scaled

+  //

+  Tcb->RcvWndScale  = 0;

+

+  Tcb->ProbeTimerOn = FALSE;

+}

+

+/**

+  Initialize the peer related members.

+

+  @param[in, out]  Tcb    Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Seg    Pointer to the segment that contains the peer's intial info.

+  @param[in]       Opt    Pointer to the options announced by the peer.

+

+**/

+VOID

+TcpInitTcbPeer (

+  IN OUT TCP_CB     *Tcb,

+  IN     TCP_SEG    *Seg,

+  IN     TCP_OPTION *Opt

+  )

+{

+  UINT16  RcvMss;

+

+  ASSERT ((Tcb != NULL) && (Seg != NULL) && (Opt != NULL));

+  ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN));

+

+  Tcb->SndWnd     = Seg->Wnd;

+  Tcb->SndWndMax  = Tcb->SndWnd;

+  Tcb->SndWl1     = Seg->Seq;

+

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {

+    Tcb->SndWl2 = Seg->Ack;

+  } else {

+    Tcb->SndWl2 = Tcb->Iss + 1;

+  }

+

+  if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) {

+    Tcb->SndMss = (UINT16) MAX (64, Opt->Mss);

+

+    RcvMss      = TcpGetRcvMss (Tcb->Sk);

+    if (Tcb->SndMss > RcvMss) {

+      Tcb->SndMss = RcvMss;

+    }

+

+  } else {

+    //

+    // One end doesn't support MSS option, use default.

+    //

+    Tcb->RcvMss = 536;

+  }

+

+  Tcb->CWnd   = Tcb->SndMss;

+

+  Tcb->Irs    = Seg->Seq;

+  Tcb->RcvNxt = Tcb->Irs + 1;

+

+  Tcb->RcvWl2 = Tcb->RcvNxt;

+

+  if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) {

+

+    Tcb->SndWndScale  = Opt->WndScale;

+

+    Tcb->RcvWndScale  = TcpComputeScale (Tcb);

+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS);

+

+  } else {

+    //

+    // One end doesn't support window scale option. use zero.

+    //

+    Tcb->RcvWndScale = 0;

+  }

+

+  if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) {

+

+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS);

+    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS);

+

+    //

+    // Compute the effective SndMss per RFC1122

+    // section 4.2.2.6. If timestamp option is

+    // enabled, it will always occupy 12 bytes.

+    //

+    Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN;

+  }

+}

+

+/**

+  Check whether one IP address equals the other.

+

+  @param[in]   Ip1     Pointer to IP address to be checked.

+  @param[in]   Ip2     Pointer to IP address to be checked.

+  @param[in]   Version IP_VERSION_4 indicates the IP address is an IPv4 address,

+                       IP_VERSION_6 indicates the IP address is an IPv6 address.

+

+  @retval      TRUE    Ip1 equals Ip2.

+  @retval      FALSE   Ip1 does not equal Ip2.

+

+**/

+BOOLEAN

+TcpIsIpEqual (

+  IN EFI_IP_ADDRESS  *Ip1,

+  IN EFI_IP_ADDRESS  *Ip2,

+  IN UINT8           Version

+  )

+{

+  ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));

+

+  if (Version == IP_VERSION_4) {

+    return (BOOLEAN) (Ip1->Addr[0] == Ip2->Addr[0]);

+  } else {

+    return (BOOLEAN) EFI_IP6_EQUAL (&Ip1->v6, &Ip2->v6);

+  }

+}

+

+/**

+  Check whether one IP address is filled with ZERO.

+

+  @param[in]   Ip      Pointer to the IP address to be checked.

+  @param[in]   Version IP_VERSION_4 indicates the IP address is an IPv4 address,

+                       IP_VERSION_6 indicates the IP address is an IPv6 address.

+

+  @retval      TRUE    Ip is all zero address.

+  @retval      FALSE   Ip is not all zero address.

+

+**/

+BOOLEAN

+TcpIsIpZero (

+  IN EFI_IP_ADDRESS *Ip,

+  IN UINT8          Version

+  )

+{

+  ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));

+

+  if (Version == IP_VERSION_4) {

+    return (BOOLEAN) (Ip->Addr[0] == 0);

+  } else {

+    return (BOOLEAN) ((Ip->Addr[0] == 0) && (Ip->Addr[1] == 0) &&

+      (Ip->Addr[2] == 0) && (Ip->Addr[3] == 0));

+  }

+}

+

+/**

+  Locate a listen TCB that matchs the Local and Remote.

+

+  @param[in]  Local    Pointer to the local (IP, Port).

+  @param[in]  Remote   Pointer to the remote (IP, Port).

+  @param[in]  Version  IP_VERSION_4 indicates TCP is running on IP4 stack,

+                       IP_VERSION_6 indicates TCP is running on IP6 stack.

+

+  @return  Pointer to the TCP_CB with the least number of wildcards,

+           if NULL no match is found.

+

+**/

+TCP_CB *

+TcpLocateListenTcb (

+  IN TCP_PEER    *Local,

+  IN TCP_PEER    *Remote,

+  IN UINT8       Version

+  )

+{

+  LIST_ENTRY      *Entry;

+  TCP_CB          *Node;

+  TCP_CB          *Match;

+  INTN            Last;

+  INTN            Cur;

+

+  Last  = 4;

+  Match = NULL;

+

+  NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {

+    Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    if ((Version != Node->Sk->IpVersion) ||

+        (Local->Port != Node->LocalEnd.Port) ||

+        !TCP_PEER_MATCH (Remote, &Node->RemoteEnd, Version) ||

+        !TCP_PEER_MATCH (Local, &Node->LocalEnd, Version)

+          ) {

+

+      continue;

+    }

+

+    //

+    // Compute the number of wildcard

+    //

+    Cur = 0;

+    if (TcpIsIpZero (&Node->RemoteEnd.Ip, Version)) {

+      Cur++;

+    }

+

+    if (Node->RemoteEnd.Port == 0) {

+      Cur++;

+    }

+

+    if (TcpIsIpZero (&Node->LocalEnd.Ip, Version)) {

+      Cur++;

+    }

+

+    if (Cur < Last) {

+      if (Cur == 0) {

+        return Node;

+      }

+

+      Last  = Cur;

+      Match = Node;

+    }

+  }

+

+  return Match;

+}

+

+/**

+  Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.

+

+  @param[in]  Addr     Pointer to the IP address needs to match.

+  @param[in]  Port     The port number needs to match.

+  @param[in]  Version  IP_VERSION_4 indicates TCP is running on IP4 stack,

+                       IP_VERSION_6 indicates TCP is running on IP6 stack.

+

+

+  @retval     TRUE     The Tcb which matches the <Addr Port> pair exists.

+  @retval     FALSE    Otherwise

+

+**/

+BOOLEAN

+TcpFindTcbByPeer (

+  IN EFI_IP_ADDRESS  *Addr,

+  IN TCP_PORTNO      Port,

+  IN UINT8           Version

+  )

+{

+  TCP_PORTNO      LocalPort;

+  LIST_ENTRY      *Entry;

+  TCP_CB          *Tcb;

+

+  ASSERT ((Addr != NULL) && (Port != 0));

+

+  LocalPort = HTONS (Port);

+

+  NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {

+    Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    if ((Version == Tcb->Sk->IpVersion) &&

+      TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&

+        (LocalPort == Tcb->LocalEnd.Port)

+        ) {

+

+      return TRUE;

+    }

+  }

+

+  NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {

+    Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    if ((Version == Tcb->Sk->IpVersion) &&

+      TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&

+        (LocalPort == Tcb->LocalEnd.Port)

+        ) {

+

+      return TRUE;

+    }

+  }

+

+  return FALSE;

+}

+

+/**

+  Locate the TCP_CB related to the socket pair.

+

+  @param[in]  LocalPort      The local port number.

+  @param[in]  LocalIp        The local IP address.

+  @param[in]  RemotePort     The remote port number.

+  @param[in]  RemoteIp       The remote IP address.

+  @param[in]  Version        IP_VERSION_4 indicates TCP is running on IP4 stack,

+                             IP_VERSION_6 indicates TCP is running on IP6 stack.

+  @param[in]  Syn            If TRUE, the listen sockets are searched.

+

+  @return Pointer to the related TCP_CB. If NULL, no match is found.

+

+**/

+TCP_CB *

+TcpLocateTcb (

+  IN TCP_PORTNO      LocalPort,

+  IN EFI_IP_ADDRESS  *LocalIp,

+  IN TCP_PORTNO      RemotePort,

+  IN EFI_IP_ADDRESS  *RemoteIp,

+  IN UINT8           Version,

+  IN BOOLEAN         Syn

+  )

+{

+  TCP_PEER        Local;

+  TCP_PEER        Remote;

+  LIST_ENTRY      *Entry;

+  TCP_CB          *Tcb;

+

+  Local.Port  = LocalPort;

+  Remote.Port = RemotePort;

+

+  CopyMem (&Local.Ip, LocalIp, sizeof (EFI_IP_ADDRESS));

+  CopyMem (&Remote.Ip, RemoteIp, sizeof (EFI_IP_ADDRESS));

+

+  //

+  // First check for exact match.

+  //

+  NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {

+    Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    if ((Version == Tcb->Sk->IpVersion) &&

+        TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd, Version) &&

+        TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd, Version)

+          ) {

+

+      RemoveEntryList (&Tcb->List);

+      InsertHeadList (&mTcpRunQue, &Tcb->List);

+

+      return Tcb;

+    }

+  }

+

+  //

+  // Only check the listen queue when the SYN flag is on.

+  //

+  if (Syn) {

+    return TcpLocateListenTcb (&Local, &Remote, Version);

+  }

+

+  return NULL;

+}

+

+/**

+  Insert a Tcb into the proper queue.

+

+  @param[in]  Tcb               Pointer to the TCP_CB to be inserted.

+

+  @retval 0                     The Tcb was inserted successfully.

+  @retval -1                    Error condition occurred.

+

+**/

+INTN

+TcpInsertTcb (

+  IN TCP_CB *Tcb

+  )

+{

+  LIST_ENTRY       *Entry;

+  LIST_ENTRY       *Head;

+  TCP_CB           *Node;

+  TCP_PROTO_DATA  *TcpProto;

+

+  ASSERT (

+    (Tcb != NULL) &&

+    (

+    (Tcb->State == TCP_LISTEN) ||

+    (Tcb->State == TCP_SYN_SENT) ||

+    (Tcb->State == TCP_SYN_RCVD) ||

+    (Tcb->State == TCP_CLOSED)

+    )

+    );

+

+  if (Tcb->LocalEnd.Port == 0) {

+    return -1;

+  }

+

+  Head = &mTcpRunQue;

+

+  if (Tcb->State == TCP_LISTEN) {

+    Head = &mTcpListenQue;

+  }

+

+  //

+  // Check that the Tcb isn't already on the list.

+  //

+  NET_LIST_FOR_EACH (Entry, Head) {

+    Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd, Tcb->Sk->IpVersion) &&

+        TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd, Tcb->Sk->IpVersion)

+          ) {

+

+      return -1;

+    }

+  }

+

+  InsertHeadList (Head, &Tcb->List);

+

+  TcpProto = (TCP_PROTO_DATA *) Tcb->Sk->ProtoReserved;

+  TcpSetVariableData (TcpProto->TcpService);

+

+  return 0;

+}

+

+/**

+  Clone a TCP_CB from Tcb.

+

+  @param[in]  Tcb                   Pointer to the TCP_CB to be cloned.

+

+  @return Pointer to the new cloned TCP_CB; if NULL, error condition occurred.

+

+**/

+TCP_CB *

+TcpCloneTcb (

+  IN TCP_CB *Tcb

+  )

+{

+  TCP_CB             *Clone;

+

+  Clone = AllocateZeroPool (sizeof (TCP_CB));

+

+  if (Clone == NULL) {

+    return NULL;

+  }

+

+  CopyMem (Clone, Tcb, sizeof (TCP_CB));

+

+  //

+  // Increase the reference count of the shared IpInfo.

+  //

+  NET_GET_REF (Tcb->IpInfo);

+

+  InitializeListHead (&Clone->List);

+  InitializeListHead (&Clone->SndQue);

+  InitializeListHead (&Clone->RcvQue);

+

+  Clone->Sk = SockClone (Tcb->Sk);

+  if (Clone->Sk == NULL) {

+    DEBUG ((EFI_D_ERROR, "TcpCloneTcb: failed to clone a sock\n"));

+    FreePool (Clone);

+    return NULL;

+  }

+

+  ((TCP_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;

+

+  return Clone;

+}

+

+/**

+  Compute an ISS to be used by a new connection.

+

+  @return The resulting ISS.

+

+**/

+TCP_SEQNO

+TcpGetIss (

+  VOID

+  )

+{

+  mTcpGlobalIss += TCP_ISS_INCREMENT_1;

+  return mTcpGlobalIss;

+}

+

+/**

+  Get the local mss.

+

+  @param[in]  Sock        Pointer to the socket to get mss.

+

+  @return The mss size.

+

+**/

+UINT16

+TcpGetRcvMss (

+  IN SOCKET  *Sock

+  )

+{

+  EFI_IP4_MODE_DATA      Ip4Mode;

+  EFI_IP6_MODE_DATA      Ip6Mode;

+  EFI_IP4_PROTOCOL       *Ip4;

+  EFI_IP6_PROTOCOL       *Ip6;

+  TCP_PROTO_DATA         *TcpProto;

+

+  ASSERT (Sock != NULL);

+

+  ZeroMem (&Ip4Mode, sizeof (EFI_IP4_MODE_DATA));

+  ZeroMem (&Ip6Mode, sizeof (EFI_IP6_MODE_DATA));

+

+  TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;

+

+  if (Sock->IpVersion == IP_VERSION_4) {

+    Ip4 = TcpProto->TcpService->IpIo->Ip.Ip4;

+    ASSERT (Ip4 != NULL);

+    Ip4->GetModeData (Ip4, &Ip4Mode, NULL, NULL);

+

+    return (UINT16) (Ip4Mode.MaxPacketSize - sizeof (TCP_HEAD));

+  } else {

+    Ip6 = TcpProto->TcpService->IpIo->Ip.Ip6;

+    ASSERT (Ip6 != NULL);

+    Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL);

+

+    return (UINT16) (Ip6Mode.MaxPacketSize - sizeof (TCP_HEAD));

+  }

+}

+

+/**

+  Set the Tcb's state.

+

+  @param[in]  Tcb                   Pointer to the TCP_CB of this TCP instance.

+  @param[in]  State                 The state to be set.

+

+**/

+VOID

+TcpSetState (

+  IN TCP_CB *Tcb,

+  IN UINT8  State

+  )

+{

+  ASSERT (Tcb->State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));

+  ASSERT (State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));

+

+  DEBUG (

+    (EFI_D_INFO,

+    "Tcb (%p) state %s --> %s\n",

+    Tcb,

+    mTcpStateName[Tcb->State],

+    mTcpStateName[State])

+    );

+

+  Tcb->State = State;

+

+  switch (State) {

+  case TCP_ESTABLISHED:

+

+    SockConnEstablished (Tcb->Sk);

+

+    if (Tcb->Parent != NULL) {

+      //

+      // A new connection is accepted by a listening socket. Install

+      // the device path.

+      //

+      TcpInstallDevicePath (Tcb->Sk);

+    }

+

+    break;

+

+  case TCP_CLOSED:

+

+    SockConnClosed (Tcb->Sk);

+

+    break;

+  default:

+    break;

+  }

+}

+

+/**

+  Compute the TCP segment's checksum.

+

+  @param[in]  Nbuf       Pointer to the buffer that contains the TCP segment.

+  @param[in]  HeadSum    The checksum value of the fixed part of pseudo header.

+

+  @return The checksum value.

+

+**/

+UINT16

+TcpChecksum (

+  IN NET_BUF *Nbuf,

+  IN UINT16  HeadSum

+  )

+{

+  UINT16  Checksum;

+

+  Checksum  = NetbufChecksum (Nbuf);

+  Checksum  = NetAddChecksum (Checksum, HeadSum);

+

+  Checksum = NetAddChecksum (

+              Checksum,

+              HTONS ((UINT16) Nbuf->TotalSize)

+              );

+

+  return (UINT16) (~Checksum);

+}

+

+/**

+  Translate the information from the head of the received TCP

+  segment Nbuf contents and fill it into a TCP_SEG structure.

+

+  @param[in]       Tcb           Pointer to the TCP_CB of this TCP instance.

+  @param[in, out]  Nbuf          Pointer to the buffer contains the TCP segment.

+

+  @return Pointer to the TCP_SEG that contains the translated TCP head information.

+

+**/

+TCP_SEG *

+TcpFormatNetbuf (

+  IN     TCP_CB  *Tcb,

+  IN OUT NET_BUF *Nbuf

+  )

+{

+  TCP_SEG   *Seg;

+  TCP_HEAD  *Head;

+

+  Seg       = TCPSEG_NETBUF (Nbuf);

+  Head      = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);

+  ASSERT (Head != NULL);

+

+  Nbuf->Tcp = Head;

+

+  Seg->Seq  = NTOHL (Head->Seq);

+  Seg->Ack  = NTOHL (Head->Ack);

+  Seg->End  = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));

+

+  Seg->Urg  = NTOHS (Head->Urg);

+  Seg->Wnd  = (NTOHS (Head->Wnd) << Tcb->SndWndScale);

+  Seg->Flag = Head->Flag;

+

+  //

+  // SYN and FIN flag occupy one sequence space each.

+  //

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {

+    //

+    // RFC requires that the initial window not be scaled.

+    //

+    Seg->Wnd = NTOHS (Head->Wnd);

+    Seg->End++;

+  }

+

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {

+    Seg->End++;

+  }

+

+  return Seg;

+}

+

+/**

+  Initialize an active connection.

+

+  @param[in, out]  Tcb          Pointer to the TCP_CB that wants to initiate a

+                                connection.

+

+**/

+VOID

+TcpOnAppConnect (

+  IN OUT TCP_CB  *Tcb

+  )

+{

+  TcpInitTcbLocal (Tcb);

+  TcpSetState (Tcb, TCP_SYN_SENT);

+

+  TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);

+  TcpToSendData (Tcb, 1);

+}

+

+/**

+  Initiate the connection close procedure, called when

+  applications want to close the connection.

+

+  @param[in, out]  Tcb          Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpOnAppClose (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  ASSERT (Tcb != NULL);

+

+  if (!IsListEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk) != 0) {

+

+    DEBUG (

+      (EFI_D_WARN,

+      "TcpOnAppClose: connection reset because data is lost for TCB %p\n",

+      Tcb)

+      );

+

+    TcpResetConnection (Tcb);

+    TcpClose (Tcb);

+    return;

+  }

+

+  switch (Tcb->State) {

+  case TCP_CLOSED:

+  case TCP_LISTEN:

+  case TCP_SYN_SENT:

+    TcpSetState (Tcb, TCP_CLOSED);

+    break;

+

+  case TCP_SYN_RCVD:

+  case TCP_ESTABLISHED:

+    TcpSetState (Tcb, TCP_FIN_WAIT_1);

+    break;

+

+  case TCP_CLOSE_WAIT:

+    TcpSetState (Tcb, TCP_LAST_ACK);

+    break;

+  default:

+    break;

+  }

+

+  TcpToSendData (Tcb, 1);

+}

+

+/**

+  Check whether the application's newly delivered data can be sent out.

+

+  @param[in, out]  Tcb          Pointer to the TCP_CB of this TCP instance.

+

+  @retval 0                     The data has been sent out successfully.

+  @retval -1                    The Tcb is not in a state that data is permitted to

+                                be sent out.

+

+**/

+INTN

+TcpOnAppSend (

+  IN OUT TCP_CB *Tcb

+  )

+{

+

+  switch (Tcb->State) {

+  case TCP_CLOSED:

+    return -1;

+

+  case TCP_LISTEN:

+    return -1;

+

+  case TCP_SYN_SENT:

+  case TCP_SYN_RCVD:

+    return 0;

+

+  case TCP_ESTABLISHED:

+  case TCP_CLOSE_WAIT:

+    TcpToSendData (Tcb, 0);

+    return 0;

+

+  case TCP_FIN_WAIT_1:

+  case TCP_FIN_WAIT_2:

+  case TCP_CLOSING:

+  case TCP_LAST_ACK:

+  case TCP_TIME_WAIT:

+    return -1;

+

+  default:

+    break;

+  }

+

+  return 0;

+}

+

+/**

+  Application has consumed some data. Check whether

+  to send a window update ack or a delayed ack.

+

+  @param[in]  Tcb        Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpOnAppConsume (

+  IN TCP_CB *Tcb

+  )

+{

+  UINT32 TcpOld;

+

+  switch (Tcb->State) {

+  case TCP_ESTABLISHED:

+    TcpOld = TcpRcvWinOld (Tcb);

+    if (TcpRcvWinNow (Tcb) > TcpOld) {

+

+      if (TcpOld < Tcb->RcvMss) {

+

+        DEBUG (

+          (EFI_D_INFO,

+          "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",

+          Tcb)

+          );

+

+        TcpSendAck (Tcb);

+      } else if (Tcb->DelayedAck == 0) {

+

+        DEBUG (

+          (EFI_D_INFO,

+          "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n",

+          Tcb)

+          );

+

+        Tcb->DelayedAck = 1;

+      }

+    }

+

+    break;

+

+  default:

+    break;

+  }

+}

+

+/**

+  Abort the connection by sending a reset segment. Called

+  when the application wants to abort the connection.

+

+  @param[in]  Tcb                   Pointer to the TCP_CB of the TCP instance.

+

+**/

+VOID

+TcpOnAppAbort (

+  IN TCP_CB *Tcb

+  )

+{

+  DEBUG (

+    (EFI_D_WARN,

+    "TcpOnAppAbort: connection reset issued by application for TCB %p\n",

+    Tcb)

+    );

+

+  switch (Tcb->State) {

+  case TCP_SYN_RCVD:

+  case TCP_ESTABLISHED:

+  case TCP_FIN_WAIT_1:

+  case TCP_FIN_WAIT_2:

+  case TCP_CLOSE_WAIT:

+    TcpResetConnection (Tcb);

+    break;

+  default:

+    break;

+  }

+

+  TcpSetState (Tcb, TCP_CLOSED);

+}

+

+/**

+  Reset the connection related with Tcb.

+

+  @param[in]  Tcb         Pointer to the TCP_CB of the connection to be reset.

+

+**/

+VOID

+TcpResetConnection (

+  IN TCP_CB *Tcb

+  )

+{

+  NET_BUF   *Nbuf;

+  TCP_HEAD  *Nhead;

+

+  Nbuf = NetbufAlloc (TCP_MAX_HEAD);

+

+  if (Nbuf == NULL) {

+    return ;

+  }

+

+  Nhead = (TCP_HEAD *) NetbufAllocSpace (

+                        Nbuf,

+                        sizeof (TCP_HEAD),

+                        NET_BUF_TAIL

+                        );

+

+  ASSERT (Nhead != NULL);

+

+  Nbuf->Tcp       = Nhead;

+

+  Nhead->Flag     = TCP_FLG_RST;

+  Nhead->Seq      = HTONL (Tcb->SndNxt);

+  Nhead->Ack      = HTONL (Tcb->RcvNxt);

+  Nhead->SrcPort  = Tcb->LocalEnd.Port;

+  Nhead->DstPort  = Tcb->RemoteEnd.Port;

+  Nhead->HeadLen  = (UINT8) (sizeof (TCP_HEAD) >> 2);

+  Nhead->Res      = 0;

+  Nhead->Wnd      = HTONS (0xFFFF);

+  Nhead->Checksum = 0;

+  Nhead->Urg      = 0;

+  Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);

+

+  TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);

+

+  NetbufFree (Nbuf);

+}

+

+/**

+  Set the Tcp variable data.

+

+  @param[in]  TcpService        Tcp service data.

+

+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.

+  @retval other                 Set variable failed.

+

+**/

+EFI_STATUS

+TcpSetVariableData (

+  IN TCP_SERVICE_DATA  *TcpService

+  )

+{

+  EFI_GUID                *ServiceBindingGuid;

+  UINT32                  NumConfiguredInstance;

+  LIST_ENTRY              *Entry;

+  TCP_CB                  *TcpPcb;

+  TCP_PROTO_DATA          *TcpProto;

+  UINTN                   VariableDataSize;

+  EFI_TCP4_VARIABLE_DATA  *Tcp4VariableData;

+  EFI_TCP4_SERVICE_POINT  *Tcp4ServicePoint;

+  EFI_TCP6_VARIABLE_DATA  *Tcp6VariableData;

+  EFI_TCP6_SERVICE_POINT  *Tcp6ServicePoint;

+  VOID                    *VariableData;

+  CHAR16                  *NewMacString;

+  EFI_STATUS              Status;

+

+  if (TcpService->IpVersion == IP_VERSION_4) {

+    ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;

+  } else {

+    ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;

+  }

+

+  NumConfiguredInstance = 0;

+  Tcp4VariableData      = NULL;

+  Tcp6VariableData      = NULL;

+

+  //

+  // Go through the running queue to count the instances.

+  //

+  NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {

+    TcpPcb    = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    TcpProto  = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;

+

+    if (TcpProto->TcpService == TcpService) {

+      //

+      // This tcp instance belongs to the TcpService.

+      //

+      NumConfiguredInstance++;

+    }

+  }

+

+  //

+  // Go through the listening queue to count the instances.

+  //

+  NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {

+    TcpPcb    = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    TcpProto  = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;

+

+    if (TcpProto->TcpService == TcpService) {

+      //

+      // This tcp instance belongs to the TcpService.

+      //

+      NumConfiguredInstance++;

+    }

+  }

+

+  Tcp4ServicePoint = NULL;

+  Tcp6ServicePoint = NULL;

+

+  //

+  // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child,

+  // we should add extra buffers for the service points only if the number of configured

+  // children is more than one.

+  //

+  if (TcpService->IpVersion == IP_VERSION_4) {

+    VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA);

+

+    if (NumConfiguredInstance > 1) {

+      VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1);

+    }

+

+    Tcp4VariableData = AllocateZeroPool (VariableDataSize);

+    if (Tcp4VariableData == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    Tcp4VariableData->DriverHandle  = TcpService->DriverBindingHandle;

+    Tcp4VariableData->ServiceCount  = NumConfiguredInstance;

+

+    Tcp4ServicePoint                = &Tcp4VariableData->Services[0];

+    VariableData                    = Tcp4VariableData;

+  } else {

+    VariableDataSize = sizeof (EFI_TCP6_VARIABLE_DATA);

+

+    if (NumConfiguredInstance > 1) {

+      VariableDataSize += sizeof (EFI_TCP6_SERVICE_POINT) * (NumConfiguredInstance - 1);

+    }

+

+    Tcp6VariableData = AllocateZeroPool (VariableDataSize);

+    if (Tcp6VariableData == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    Tcp6VariableData->DriverHandle  = TcpService->DriverBindingHandle;

+    Tcp6VariableData->ServiceCount  = NumConfiguredInstance;

+

+    Tcp6ServicePoint                = &Tcp6VariableData->Services[0];

+    VariableData                    = Tcp6VariableData;

+  }

+

+  //

+  // Go through the running queue to fill the service points.

+  //

+  NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {

+    TcpPcb    = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    TcpProto  = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;

+

+    if (TcpProto->TcpService == TcpService) {

+      //

+      // This tcp instance belongs to the TcpService.

+      //

+      if (TcpService->IpVersion == IP_VERSION_4) {

+        Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;

+        CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));

+        Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);

+        CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));

+        Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);

+

+        Tcp4ServicePoint++;

+      } else {

+        Tcp6ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;

+        IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);

+        Tcp6ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);

+        IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);

+        Tcp6ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);

+

+        Tcp6ServicePoint++;

+      }

+    }

+  }

+

+  //

+  // Go through the listening queue to fill the service points.

+  //

+  NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {

+    TcpPcb    = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    TcpProto  = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;

+

+    if (TcpProto->TcpService == TcpService) {

+      //

+      // This tcp instance belongs to the TcpService.

+      //

+      if (TcpService->IpVersion == IP_VERSION_4) {

+        Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;

+        CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));

+        Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);

+        CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));

+        Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);

+

+        Tcp4ServicePoint++;

+      } else {

+        Tcp6ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;

+        IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);

+        Tcp6ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);

+        IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);

+        Tcp6ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);

+

+        Tcp6ServicePoint++;

+      }

+    }

+  }

+

+  //

+  // Get the mac string.

+  //

+  Status = NetLibGetMacString (

+             TcpService->ControllerHandle,

+             TcpService->DriverBindingHandle,

+             &NewMacString

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  if (TcpService->MacString != NULL) {

+    //

+    // The variable is set already. We're going to update it.

+    //

+    if (StrCmp (TcpService->MacString, NewMacString) != 0) {

+      //

+      // The mac address is changed. Delete the previous variable first.

+      //

+      gRT->SetVariable (

+             TcpService->MacString,

+             ServiceBindingGuid,

+             EFI_VARIABLE_BOOTSERVICE_ACCESS,

+             0,

+             NULL

+             );

+    }

+

+    FreePool (TcpService->MacString);

+  }

+

+  TcpService->MacString = NewMacString;

+

+  Status = gRT->SetVariable (

+                  TcpService->MacString,

+                  ServiceBindingGuid,

+                  EFI_VARIABLE_BOOTSERVICE_ACCESS,

+                  VariableDataSize,

+                  VariableData

+                  );

+

+ON_ERROR:

+

+  FreePool (VariableData);

+

+  return Status;

+}

+

+/**

+  Clear the variable and free the resource.

+

+  @param[in]  TcpService            Tcp service data.

+

+**/

+VOID

+TcpClearVariableData (

+  IN TCP_SERVICE_DATA  *TcpService

+  )

+{

+  EFI_GUID  *ServiceBindingGuid;

+

+  ASSERT (TcpService->MacString != NULL);

+

+  if (TcpService->IpVersion == IP_VERSION_4) {

+    ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;

+  } else {

+    ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;

+  }

+

+  gRT->SetVariable (

+         TcpService->MacString,

+         ServiceBindingGuid,

+         EFI_VARIABLE_BOOTSERVICE_ACCESS,

+         0,

+         NULL

+         );

+

+  FreePool (TcpService->MacString);

+  TcpService->MacString = NULL;

+}

+

+/**

+  Install the device path protocol on the TCP instance.

+

+  @param[in]  Sock          Pointer to the socket representing the TCP instance.

+

+  @retval EFI_SUCCESS           The device path protocol was installed.

+  @retval other                 Failed to install the device path protocol.

+

+**/

+EFI_STATUS

+TcpInstallDevicePath (

+  IN SOCKET *Sock

+  )

+{

+  TCP_PROTO_DATA           *TcpProto;

+  TCP_SERVICE_DATA         *TcpService;

+  TCP_CB                   *Tcb;

+  IPv4_DEVICE_PATH         Ip4DPathNode;

+  IPv6_DEVICE_PATH         Ip6DPathNode;

+  EFI_DEVICE_PATH_PROTOCOL *DevicePath;

+  EFI_STATUS               Status;

+  TCP_PORTNO               LocalPort;

+  TCP_PORTNO               RemotePort;

+

+  TcpProto   = (TCP_PROTO_DATA *) Sock->ProtoReserved;

+  TcpService = TcpProto->TcpService;

+  Tcb        = TcpProto->TcpPcb;

+

+  LocalPort = NTOHS (Tcb->LocalEnd.Port);

+  RemotePort = NTOHS (Tcb->RemoteEnd.Port);

+  if (Sock->IpVersion == IP_VERSION_4) {

+    NetLibCreateIPv4DPathNode (

+      &Ip4DPathNode,

+      TcpService->ControllerHandle,

+      Tcb->LocalEnd.Ip.Addr[0],

+      LocalPort,

+      Tcb->RemoteEnd.Ip.Addr[0],

+      RemotePort,

+      EFI_IP_PROTO_TCP,

+      Tcb->UseDefaultAddr

+      );

+

+    DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode;

+  } else {

+    NetLibCreateIPv6DPathNode (

+      &Ip6DPathNode,

+      TcpService->ControllerHandle,

+      &Tcb->LocalEnd.Ip.v6,

+      LocalPort,

+      &Tcb->RemoteEnd.Ip.v6,

+      RemotePort,

+      EFI_IP_PROTO_TCP

+      );

+

+    DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip6DPathNode;

+  }

+

+  Sock->DevicePath = AppendDevicePathNode (Sock->ParentDevicePath, DevicePath);

+  if (Sock->DevicePath == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = gBS->InstallProtocolInterface (

+                  &Sock->SockHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  EFI_NATIVE_INTERFACE,

+                  Sock->DevicePath

+                  );

+  if (EFI_ERROR (Status)) {

+    FreePool (Sock->DevicePath);

+    Sock->DevicePath = NULL;

+  }

+

+  return Status;

+}

+

diff --git a/NetworkPkg/TcpDxe/TcpOption.c b/NetworkPkg/TcpDxe/TcpOption.c
new file mode 100644
index 0000000..bacce10
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpOption.c
@@ -0,0 +1,374 @@
+/** @file

+  Routines to process TCP option.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+/**

+  Get a UINT16 value from buffer.

+

+  @param[in] Buf              Pointer to input buffer.

+

+  @return                     The UINT16 value obtained from the buffer.

+

+**/

+UINT16

+TcpGetUint16 (

+  IN UINT8 *Buf

+  )

+{

+  UINT16  Value;

+  CopyMem (&Value, Buf, sizeof (UINT16));

+  return NTOHS (Value);

+}

+

+/**

+  Get a UINT32 value from buffer.

+

+  @param[in] Buf              Pointer to input buffer.

+

+  @return                     The UINT32 value obtained from the buffer.

+

+**/

+UINT32

+TcpGetUint32 (

+  IN UINT8 *Buf

+  )

+{

+  UINT32  Value;

+  CopyMem (&Value, Buf, sizeof (UINT32));

+  return NTOHL (Value);

+}

+

+/**

+  Put a UINT32 value in buffer.

+

+  @param[out] Buf             Pointer to the buffer.

+  @param[in] Data             The UINT32 Date to put in the buffer.

+

+**/

+VOID

+TcpPutUint32 (

+     OUT UINT8  *Buf,

+  IN     UINT32 Data

+  )

+{

+  Data = HTONL (Data);

+  CopyMem (Buf, &Data, sizeof (UINT32));

+}

+

+/**

+  Compute the window scale value according to the given buffer size.

+

+  @param[in]  Tcb Pointer to the TCP_CB of this TCP instance.

+

+  @return         The scale value.

+

+**/

+UINT8

+TcpComputeScale (

+  IN TCP_CB *Tcb

+  )

+{

+  UINT8   Scale;

+  UINT32  BufSize;

+

+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));

+

+  BufSize = GET_RCV_BUFFSIZE (Tcb->Sk);

+

+  Scale   = 0;

+  while ((Scale < TCP_OPTION_MAX_WS) && ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) {

+

+    Scale++;

+  }

+

+  return Scale;

+}

+

+/**

+  Build the TCP option in three-way handshake.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Nbuf    Pointer to the buffer to store the options.

+

+  @return             The total length of the TCP option field.

+

+**/

+UINT16

+TcpSynBuildOption (

+  IN TCP_CB  *Tcb,

+  IN NET_BUF *Nbuf

+  )

+{

+  UINT8   *Data;

+  UINT16  Len;

+

+  ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));

+

+  Len = 0;

+

+  //

+  // Add a timestamp option if not disabled by the application

+  // and it is the first SYN segment, or the peer has sent

+  // us its timestamp.

+  //

+  if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) &&

+      (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||

+        TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS))

+      ) {

+

+    Data = NetbufAllocSpace (

+             Nbuf,

+             TCP_OPTION_TS_ALIGNED_LEN,

+             NET_BUF_HEAD

+             );

+

+    ASSERT (Data != NULL);

+    Len += TCP_OPTION_TS_ALIGNED_LEN;

+

+    TcpPutUint32 (Data, TCP_OPTION_TS_FAST);

+    TcpPutUint32 (Data + 4, mTcpTick);

+    TcpPutUint32 (Data + 8, 0);

+  }

+

+  //

+  // Build window scale option, only when configured

+  // to send WS option, and either we are doing active

+  // open or we have received WS option from peer.

+  //

+  if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) &&

+      (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||

+        TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS))

+      ) {

+

+    Data = NetbufAllocSpace (

+             Nbuf,

+             TCP_OPTION_WS_ALIGNED_LEN,

+             NET_BUF_HEAD

+             );

+

+    ASSERT (Data != NULL);

+

+    Len += TCP_OPTION_WS_ALIGNED_LEN;

+    TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb));

+  }

+

+  //

+  // Build the MSS option.

+  //

+  Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1);

+  ASSERT (Data != NULL);

+

+  Len += TCP_OPTION_MSS_LEN;

+  TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss);

+

+  return Len;

+}

+

+/**

+  Build the TCP option in synchronized states.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Nbuf    Pointer to the buffer to store the options.

+

+  @return             The total length of the TCP option field.

+

+**/

+UINT16

+TcpBuildOption (

+  IN TCP_CB  *Tcb,

+  IN NET_BUF *Nbuf

+  )

+{

+  UINT8   *Data;

+  UINT16  Len;

+

+  ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));

+  Len = 0;

+

+  //

+  // Build the Timestamp option.

+  //

+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) &&

+      !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST)

+      ) {

+

+    Data = NetbufAllocSpace (

+            Nbuf,

+            TCP_OPTION_TS_ALIGNED_LEN,

+            NET_BUF_HEAD

+            );

+

+    ASSERT (Data != NULL);

+    Len += TCP_OPTION_TS_ALIGNED_LEN;

+

+    TcpPutUint32 (Data, TCP_OPTION_TS_FAST);

+    TcpPutUint32 (Data + 4, mTcpTick);

+    TcpPutUint32 (Data + 8, Tcb->TsRecent);

+  }

+

+  return Len;

+}

+

+/**

+  Parse the supported options.

+

+  @param[in]       Tcp     Pointer to the TCP_CB of this TCP instance.

+  @param[in, out]  Option  Pointer to the TCP_OPTION used to store the

+                           successfully pasrsed options.

+

+  @retval          0       The options are successfully pasrsed.

+  @retval          -1      Ilegal option was found.

+

+**/

+INTN

+TcpParseOption (

+  IN     TCP_HEAD   *Tcp,

+  IN OUT TCP_OPTION *Option

+  )

+{

+  UINT8 *Head;

+  UINT8 TotalLen;

+  UINT8 Cur;

+  UINT8 Type;

+  UINT8 Len;

+

+  ASSERT ((Tcp != NULL) && (Option != NULL));

+

+  Option->Flag  = 0;

+

+  TotalLen      = (UINT8) ((Tcp->HeadLen << 2) - sizeof (TCP_HEAD));

+  if (TotalLen <= 0) {

+    return 0;

+  }

+

+  Head = (UINT8 *) (Tcp + 1);

+

+  //

+  // Fast process of the timestamp option.

+  //

+  if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) {

+

+    Option->TSVal = TcpGetUint32 (Head + 4);

+    Option->TSEcr = TcpGetUint32 (Head + 8);

+    Option->Flag  = TCP_OPTION_RCVD_TS;

+

+    return 0;

+  }

+  //

+  // Slow path to process the options.

+  //

+  Cur = 0;

+

+  while (Cur < TotalLen) {

+    Type = Head[Cur];

+

+    switch (Type) {

+    case TCP_OPTION_MSS:

+      Len = Head[Cur + 1];

+

+      if ((Len != TCP_OPTION_MSS_LEN) || (TotalLen - Cur < TCP_OPTION_MSS_LEN)) {

+

+        return -1;

+      }

+

+      Option->Mss = TcpGetUint16 (&Head[Cur + 2]);

+      TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS);

+

+      Cur += TCP_OPTION_MSS_LEN;

+      break;

+

+    case TCP_OPTION_WS:

+      Len = Head[Cur + 1];

+

+      if ((Len != TCP_OPTION_WS_LEN) || (TotalLen - Cur < TCP_OPTION_WS_LEN)) {

+

+        return -1;

+      }

+

+      Option->WndScale = (UINT8) MIN (14, Head[Cur + 2]);

+      TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS);

+

+      Cur += TCP_OPTION_WS_LEN;

+      break;

+

+    case TCP_OPTION_TS:

+      Len = Head[Cur + 1];

+

+      if ((Len != TCP_OPTION_TS_LEN) || (TotalLen - Cur < TCP_OPTION_TS_LEN)) {

+

+        return -1;

+      }

+

+      Option->TSVal = TcpGetUint32 (&Head[Cur + 2]);

+      Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]);

+      TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS);

+

+      Cur += TCP_OPTION_TS_LEN;

+      break;

+

+    case TCP_OPTION_NOP:

+      Cur++;

+      break;

+

+    case TCP_OPTION_EOP:

+      Cur = TotalLen;

+      break;

+

+    default:

+      Len = Head[Cur + 1];

+

+      if ((TotalLen - Cur) < Len || Len < 2) {

+        return -1;

+      }

+

+      Cur = (UINT8) (Cur + Len);

+      break;

+    }

+

+  }

+

+  return 0;

+}

+

+/**

+  Check the segment against PAWS.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  TSVal   The timestamp value.

+

+  @retval     1       The segment passed the PAWS check.

+  @retval     0       The segment failed to pass the PAWS check.

+

+**/

+UINT32

+TcpPawsOK (

+  IN TCP_CB *Tcb,

+  IN UINT32 TSVal

+  )

+{

+  //

+  // PAWS as defined in RFC1323, buggy...

+  //

+  if (TCP_TIME_LT (TSVal, Tcb->TsRecent) &&

+      TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick)

+      ) {

+

+    return 0;

+

+  }

+

+  return 1;

+}

diff --git a/NetworkPkg/TcpDxe/TcpOption.h b/NetworkPkg/TcpDxe/TcpOption.h
new file mode 100644
index 0000000..0ccadb9
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpOption.h
@@ -0,0 +1,145 @@
+/** @file

+  Tcp option's routine header file.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _TCP_OPTION_H_

+#define _TCP_OPTION_H_

+

+//

+// Supported TCP option types and their length.

+//

+#define TCP_OPTION_EOP             0  ///< End Of oPtion

+#define TCP_OPTION_NOP             1  ///< No-Option.

+#define TCP_OPTION_MSS             2  ///< Maximum Segment Size

+#define TCP_OPTION_WS              3  ///< Window scale

+#define TCP_OPTION_TS              8  ///< Timestamp

+#define TCP_OPTION_MSS_LEN         4  ///< Length of MSS option

+#define TCP_OPTION_WS_LEN          3  ///< Length of window scale option

+#define TCP_OPTION_TS_LEN          10 ///< Length of timestamp option

+#define TCP_OPTION_WS_ALIGNED_LEN  4  ///< Length of window scale option, aligned

+#define TCP_OPTION_TS_ALIGNED_LEN  12 ///< Length of timestamp option, aligned

+

+//

+// recommend format of timestamp window scale

+// option for fast process.

+//

+#define TCP_OPTION_TS_FAST ((TCP_OPTION_NOP << 24) | \

+                            (TCP_OPTION_NOP << 16) | \

+                            (TCP_OPTION_TS << 8)   | \

+                            (TCP_OPTION_TS_LEN))

+

+#define TCP_OPTION_WS_FAST   ((TCP_OPTION_NOP << 24) | \

+                              (TCP_OPTION_WS << 16)  | \

+                              (TCP_OPTION_WS_LEN << 8))

+

+#define TCP_OPTION_MSS_FAST  ((TCP_OPTION_MSS << 24) | (TCP_OPTION_MSS_LEN << 16))

+

+//

+// Other misc definations

+//

+#define TCP_OPTION_RCVD_MSS        0x01

+#define TCP_OPTION_RCVD_WS         0x02

+#define TCP_OPTION_RCVD_TS         0x04

+#define TCP_OPTION_MAX_WS          14      ///< Maxium window scale value

+#define TCP_OPTION_MAX_WIN         0xffff  ///< Max window size in TCP header

+

+///

+/// The structure to store the parse option value.

+/// ParseOption only parses the options, doesn't process them.

+///

+typedef struct _TCP_OPTION {

+  UINT8   Flag;     ///< Flag such as TCP_OPTION_RCVD_MSS

+  UINT8   WndScale; ///< The WndScale received

+  UINT16  Mss;      ///< The Mss received

+  UINT32  TSVal;    ///< The TSVal field in a timestamp option

+  UINT32  TSEcr;    ///< The TSEcr field in a timestamp option

+} TCP_OPTION;

+

+/**

+  Compute the window scale value according to the given buffer size.

+

+  @param[in]  Tcb Pointer to the TCP_CB of this TCP instance.

+

+  @return         The scale value.

+

+**/

+UINT8

+TcpComputeScale (

+  IN TCP_CB *Tcb

+  );

+

+/**

+  Build the TCP option in three-way handshake.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Nbuf    Pointer to the buffer to store the options.

+

+  @return             The total length of the TCP option field.

+

+**/

+UINT16

+TcpSynBuildOption (

+  IN TCP_CB  *Tcb,

+  IN NET_BUF *Nbuf

+  );

+

+/**

+  Build the TCP option in synchronized states.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Nbuf    Pointer to the buffer to store the options.

+

+  @return             The total length of the TCP option field.

+

+**/

+UINT16

+TcpBuildOption (

+  IN TCP_CB  *Tcb,

+  IN NET_BUF *Nbuf

+  );

+

+/**

+  Parse the supported options.

+

+  @param[in]       Tcp     Pointer to the TCP_CB of this TCP instance.

+  @param[in, out]  Option  Pointer to the TCP_OPTION used to store the

+                           successfully pasrsed options.

+

+  @retval          0       The options successfully pasrsed.

+  @retval          -1      Ilegal option was found.

+

+**/

+INTN

+TcpParseOption (

+  IN     TCP_HEAD   *Tcp,

+  IN OUT TCP_OPTION *Option

+  );

+

+/**

+  Check the segment against PAWS.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  TSVal   The timestamp value.

+

+  @retval     1       The segment passed the PAWS check.

+  @retval     0       The segment failed to pass the PAWS check.

+

+**/

+UINT32

+TcpPawsOK (

+  IN TCP_CB *Tcb,

+  IN UINT32 TSVal

+  );

+

+#endif

diff --git a/NetworkPkg/TcpDxe/TcpOutput.c b/NetworkPkg/TcpDxe/TcpOutput.c
new file mode 100644
index 0000000..c038213
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpOutput.c
@@ -0,0 +1,1219 @@
+/** @file

+  TCP output process routines.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+UINT8  mTcpOutFlag[] = {

+  0,                          // TCP_CLOSED

+  0,                          // TCP_LISTEN

+  TCP_FLG_SYN,                // TCP_SYN_SENT

+  TCP_FLG_SYN | TCP_FLG_ACK,  // TCP_SYN_RCVD

+  TCP_FLG_ACK,                // TCP_ESTABLISHED

+  TCP_FLG_FIN | TCP_FLG_ACK,  // TCP_FIN_WAIT_1

+  TCP_FLG_ACK,                // TCP_FIN_WAIT_2

+  TCP_FLG_ACK | TCP_FLG_FIN,  // TCP_CLOSING

+  TCP_FLG_ACK,                // TCP_TIME_WAIT

+  TCP_FLG_ACK,                // TCP_CLOSE_WAIT

+  TCP_FLG_FIN | TCP_FLG_ACK   // TCP_LAST_ACK

+};

+

+/**

+  Compute the sequence space left in the old receive window.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+  @return The sequence space left in the old receive window.

+

+**/

+UINT32

+TcpRcvWinOld (

+  IN TCP_CB *Tcb

+  )

+{

+  UINT32  OldWin;

+

+  OldWin = 0;

+

+  if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) {

+

+    OldWin = TCP_SUB_SEQ (

+              Tcb->RcvWl2 + Tcb->RcvWnd,

+              Tcb->RcvNxt

+              );

+  }

+

+  return OldWin;

+}

+

+/**

+  Compute the current receive window.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+  @return The size of the current receive window, in bytes.

+

+**/

+UINT32

+TcpRcvWinNow (

+  IN TCP_CB *Tcb

+  )

+{

+  SOCKET  *Sk;

+  UINT32  Win;

+  UINT32  Increase;

+  UINT32  OldWin;

+

+  Sk = Tcb->Sk;

+  ASSERT (Sk != NULL);

+

+  OldWin    = TcpRcvWinOld (Tcb);

+

+  Win       = SockGetFreeSpace (Sk, SOCK_RCV_BUF);

+

+  Increase  = 0;

+  if (Win > OldWin) {

+    Increase = Win - OldWin;

+  }

+

+  //

+  // Receiver's SWS: don't advertise a bigger window

+  // unless it can be increased by at least one Mss or

+  // half of the receive buffer.

+  //

+  if ((Increase > Tcb->SndMss) || (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) {

+

+    return Win;

+  }

+

+  return OldWin;

+}

+

+/**

+  Compute the value to fill in the window size field of the outgoing segment.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Syn     The flag to indicate whether the outgoing segment

+                           is a SYN segment.

+

+  @return The value of the local receive window size used to fill the outgoing segment.

+

+**/

+UINT16

+TcpComputeWnd (

+  IN OUT TCP_CB  *Tcb,

+  IN     BOOLEAN Syn

+  )

+{

+  UINT32  Wnd;

+

+  //

+  // RFC requires that initial window not be scaled

+  //

+  if (Syn) {

+

+    Wnd = GET_RCV_BUFFSIZE (Tcb->Sk);

+  } else {

+

+    Wnd         = TcpRcvWinNow (Tcb);

+

+    Tcb->RcvWnd = Wnd;

+  }

+

+  Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff);

+  return NTOHS ((UINT16) Wnd);

+}

+

+/**

+  Get the maximum SndNxt.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+  @return The sequence number of the maximum SndNxt.

+

+**/

+TCP_SEQNO

+TcpGetMaxSndNxt (

+  IN TCP_CB *Tcb

+  )

+{

+  LIST_ENTRY      *Entry;

+  NET_BUF         *Nbuf;

+

+  if (IsListEmpty (&Tcb->SndQue)) {

+    return Tcb->SndNxt;

+  }

+

+  Entry = Tcb->SndQue.BackLink;

+  Nbuf  = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);

+

+  ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt));

+  return TCPSEG_NETBUF (Nbuf)->End;

+}

+

+/**

+  Compute how much data to send.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Force   If TRUE, to ignore the sender's SWS avoidance algorithm and send

+                      out data by force.

+

+  @return The length of the data can be sent. If 0, no data can be sent.

+

+**/

+UINT32

+TcpDataToSend (

+  IN TCP_CB *Tcb,

+  IN INTN   Force

+  )

+{

+  SOCKET  *Sk;

+  UINT32  Win;

+  UINT32  Len;

+  UINT32  Left;

+  UINT32  Limit;

+

+  Sk = Tcb->Sk;

+  ASSERT (Sk != NULL);

+

+  //

+  // TCP should NOT send data beyond the send window

+  // and congestion window. The right edge of send

+  // window is defined as SND.WL2 + SND.WND. The right

+  // edge of congestion window is defined as SND.UNA +

+  // CWND.

+  //

+  Win   = 0;

+  Limit = Tcb->SndWl2 + Tcb->SndWnd;

+

+  if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) {

+

+    Limit = Tcb->SndUna + Tcb->CWnd;

+  }

+

+  if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) {

+    Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt);

+  }

+

+  //

+  // The data to send contains two parts: the data on the

+  // socket send queue, and the data on the TCB's send

+  // buffer. The later can be non-zero if the peer shrinks

+  // its advertised window.

+  //

+  Left  = GET_SND_DATASIZE (Sk) + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt);

+

+  Len   = MIN (Win, Left);

+

+  if (Len > Tcb->SndMss) {

+    Len = Tcb->SndMss;

+  }

+

+  if ((Force != 0)|| (Len == 0 && Left == 0)) {

+    return Len;

+  }

+

+  if (Len == 0 && Left != 0) {

+    goto SetPersistTimer;

+  }

+

+  //

+  // Sender's SWS avoidance: Don't send a small segment unless

+  // a)A full-sized segment can be sent,

+  // b)At least one-half of the maximum sized windows that

+  // the other end has ever advertised.

+  // c)It can send everything it has, and either it isn't

+  // expecting an ACK, or the Nagle algorithm is disabled.

+  //

+  if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) {

+

+    return Len;

+  }

+

+  if ((Len == Left) &&

+      ((Tcb->SndNxt == Tcb->SndUna) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))

+      ) {

+

+    return Len;

+  }

+

+  //

+  // RFC1122 suggests to set a timer when SWSA forbids TCP

+  // sending more data, and combines it with a probe timer.

+  //

+SetPersistTimer:

+  if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {

+

+    DEBUG (

+      (EFI_D_WARN,

+      "TcpDataToSend: enter persistent state for TCB %p\n",

+      Tcb)

+      );

+

+    if (!Tcb->ProbeTimerOn) {

+      TcpSetProbeTimer (Tcb);

+    }

+  }

+

+  return 0;

+}

+

+/**

+  Build the TCP header of the TCP segment and transmit the segment by IP.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Nbuf    Pointer to the buffer containing the segment to be

+                           sent out.

+

+  @retval 0       The segment was sent out successfully.

+  @retval -1      An error condition occurred.

+

+**/

+INTN

+TcpTransmitSegment (

+  IN OUT TCP_CB  *Tcb,

+  IN     NET_BUF *Nbuf

+  )

+{

+  UINT16    Len;

+  TCP_HEAD  *Head;

+  TCP_SEG   *Seg;

+  BOOLEAN   Syn;

+  UINT32    DataLen;

+

+  ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0));

+

+  DataLen = Nbuf->TotalSize;

+

+  Seg     = TCPSEG_NETBUF (Nbuf);

+  Syn     = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN);

+

+  if (Syn) {

+

+    Len = TcpSynBuildOption (Tcb, Nbuf);

+  } else {

+

+    Len = TcpBuildOption (Tcb, Nbuf);

+  }

+

+  ASSERT ((Len % 4 == 0) && (Len <= 40));

+

+  Len += sizeof (TCP_HEAD);

+

+  Head = (TCP_HEAD *) NetbufAllocSpace (

+                        Nbuf,

+                        sizeof (TCP_HEAD),

+                        NET_BUF_HEAD

+                        );

+

+  ASSERT (Head != NULL);

+

+  Nbuf->Tcp       = Head;

+

+  Head->SrcPort   = Tcb->LocalEnd.Port;

+  Head->DstPort   = Tcb->RemoteEnd.Port;

+  Head->Seq       = NTOHL (Seg->Seq);

+  Head->Ack       = NTOHL (Tcb->RcvNxt);

+  Head->HeadLen   = (UINT8) (Len >> 2);

+  Head->Res       = 0;

+  Head->Wnd       = TcpComputeWnd (Tcb, Syn);

+  Head->Checksum  = 0;

+

+  //

+  // Check whether to set the PSH flag.

+  //

+  TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH);

+

+  if (DataLen != 0) {

+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) &&

+        TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)

+        ) {

+

+      TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);

+      TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);

+

+    } else if ((Seg->End == Tcb->SndNxt) && (GET_SND_DATASIZE (Tcb->Sk) == 0)) {

+

+      TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);

+    }

+  }

+

+  //

+  // Check whether to set the URG flag and the urgent pointer.

+  //

+  TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);

+

+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) {

+

+    TCP_SET_FLG (Seg->Flag, TCP_FLG_URG);

+

+    if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) {

+

+      Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq);

+    } else {

+

+      Seg->Urg = (UINT16) MIN (

+                            TCP_SUB_SEQ (Tcb->SndUp,

+                            Seg->Seq),

+                            0xffff

+                            );

+    }

+  }

+

+  Head->Flag      = Seg->Flag;

+  Head->Urg       = NTOHS (Seg->Urg);

+  Head->Checksum  = TcpChecksum (Nbuf, Tcb->HeadSum);

+

+  //

+  // Update the TCP session's control information.

+  //

+  Tcb->RcvWl2 = Tcb->RcvNxt;

+  if (Syn) {

+    Tcb->RcvWnd = NTOHS (Head->Wnd);

+  }

+

+  //

+  // Clear the delayedack flag.

+  //

+  Tcb->DelayedAck = 0;

+

+  return TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);

+}

+

+/**

+  Get a segment from the Tcb's SndQue.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Seq     The sequence number of the segment.

+  @param[in]  Len     The maximum length of the segment.

+

+  @return Pointer to the segment. If NULL, some error occurred.

+

+**/

+NET_BUF *

+TcpGetSegmentSndQue (

+  IN TCP_CB    *Tcb,

+  IN TCP_SEQNO Seq,

+  IN UINT32    Len

+  )

+{

+  LIST_ENTRY      *Head;

+  LIST_ENTRY      *Cur;

+  NET_BUF         *Node;

+  TCP_SEG         *Seg;

+  NET_BUF         *Nbuf;

+  TCP_SEQNO       End;

+  UINT8           *Data;

+  UINT8           Flag;

+  INT32           Offset;

+  INT32           CopyLen;

+

+  ASSERT ((Tcb != NULL) && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0));

+

+  //

+  // Find the segment that contains the Seq.

+  //

+  Head  = &Tcb->SndQue;

+

+  Node  = NULL;

+  Seg   = NULL;

+

+  NET_LIST_FOR_EACH (Cur, Head) {

+    Node  = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);

+    Seg   = TCPSEG_NETBUF (Node);

+

+    if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) {

+

+      break;

+    }

+  }

+

+  if ((Cur == Head) || (Seg == NULL) || (Node == NULL)) {

+    return NULL;

+  }

+

+  //

+  // Return the buffer if it can be returned without

+  // adjustment:

+  //

+  if ((Seg->Seq == Seq) &&

+      TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&

+      !NET_BUF_SHARED (Node)

+      ) {

+

+    NET_GET_REF (Node);

+    return Node;

+  }

+

+  //

+  // Create a new buffer and copy data there.

+  //

+  Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);

+

+  if (Nbuf == NULL) {

+    return NULL;

+  }

+

+  NetbufReserve (Nbuf, TCP_MAX_HEAD);

+

+  Flag  = Seg->Flag;

+  End   = Seg->End;

+

+  if (TCP_SEQ_LT (Seq + Len, Seg->End)) {

+    End = Seq + Len;

+  }

+

+  CopyLen = TCP_SUB_SEQ (End, Seq);

+  Offset  = TCP_SUB_SEQ (Seq, Seg->Seq);

+

+  //

+  // If SYN is set and out of the range, clear the flag.

+  // Becuase the sequence of the first byte is SEG.SEQ+1,

+  // adjust Offset by -1. If SYN is in the range, copy

+  // one byte less.

+  //

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {

+

+    if (TCP_SEQ_LT (Seg->Seq, Seq)) {

+

+      TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);

+      Offset--;

+    } else {

+

+      CopyLen--;

+    }

+  }

+

+  //

+  // If FIN is set and in the range, copy one byte less,

+  // and if it is out of the range, clear the flag.

+  //

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {

+

+    if (Seg->End == End) {

+

+      CopyLen--;

+    } else {

+

+      TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);

+    }

+  }

+

+  ASSERT (CopyLen >= 0);

+

+  //

+  // Copy data to the segment

+  //

+  if (CopyLen != 0) {

+    Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);

+    ASSERT (Data != NULL);

+

+    if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {

+      goto OnError;

+    }

+  }

+

+  CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));

+

+  TCPSEG_NETBUF (Nbuf)->Seq   = Seq;

+  TCPSEG_NETBUF (Nbuf)->End   = End;

+  TCPSEG_NETBUF (Nbuf)->Flag  = Flag;

+

+  return Nbuf;

+

+OnError:

+  NetbufFree (Nbuf);

+  return NULL;

+}

+

+/**

+  Get a segment from the Tcb's socket buffer.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Seq     The sequence number of the segment.

+  @param[in]  Len     The maximum length of the segment.

+

+  @return Pointer to the segment. If NULL, some error occurred.

+

+**/

+NET_BUF *

+TcpGetSegmentSock (

+  IN TCP_CB    *Tcb,

+  IN TCP_SEQNO Seq,

+  IN UINT32    Len

+  )

+{

+  NET_BUF *Nbuf;

+  UINT8   *Data;

+  UINT32  DataGet;

+

+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));

+

+  Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);

+

+  if (Nbuf == NULL) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "TcpGetSegmentSock: failed to allocate a netbuf for TCB %p\n",

+      Tcb)

+      );

+

+    return NULL;

+  }

+

+  NetbufReserve (Nbuf, TCP_MAX_HEAD);

+

+  DataGet = 0;

+

+  if (Len != 0) {

+    //

+    // copy data to the segment.

+    //

+    Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);

+    ASSERT (Data != NULL);

+

+    DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);

+  }

+

+  NET_GET_REF (Nbuf);

+

+  TCPSEG_NETBUF (Nbuf)->Seq = Seq;

+  TCPSEG_NETBUF (Nbuf)->End = Seq + Len;

+

+  InsertTailList (&(Tcb->SndQue), &(Nbuf->List));

+

+  if (DataGet != 0) {

+

+    SockDataSent (Tcb->Sk, DataGet);

+  }

+

+  return Nbuf;

+}

+

+/**

+  Get a segment starting from sequence Seq of a maximum

+  length of Len.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Seq     The sequence number of the segment.

+  @param[in]  Len     The maximum length of the segment.

+

+  @return Pointer to the segment. If NULL, some error occurred.

+

+**/

+NET_BUF *

+TcpGetSegment (

+  IN TCP_CB    *Tcb,

+  IN TCP_SEQNO Seq,

+  IN UINT32    Len

+  )

+{

+  NET_BUF *Nbuf;

+

+  ASSERT (Tcb != NULL);

+

+  //

+  // Compare the SndNxt with the max sequence number sent.

+  //

+  if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {

+

+    Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);

+  } else {

+

+    Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);

+  }

+

+  ASSERT (TcpVerifySegment (Nbuf) != 0);

+  return Nbuf;

+}

+

+/**

+  Retransmit the segment from sequence Seq.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]  Seq     The sequence number of the segment to be retransmitted.

+

+  @retval 0       Retransmission succeeded.

+  @retval -1      Error condition occurred.

+

+**/

+INTN

+TcpRetransmit (

+  IN TCP_CB    *Tcb,

+  IN TCP_SEQNO Seq

+  )

+{

+  NET_BUF *Nbuf;

+  UINT32  Len;

+

+  //

+  // Compute the maxium length of retransmission. It is

+  // limited by three factors:

+  // 1. Less than SndMss

+  // 2. Must in the current send window

+  // 3. Will not change the boundaries of queued segments.

+  //

+  if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {

+    DEBUG (

+      (EFI_D_WARN,

+      "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n",

+      Tcb)

+      );

+

+    return 0;

+  }

+

+  Len   = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);

+  Len   = MIN (Len, Tcb->SndMss);

+

+  Nbuf  = TcpGetSegmentSndQue (Tcb, Seq, Len);

+  if (Nbuf == NULL) {

+    return -1;

+  }

+

+  ASSERT (TcpVerifySegment (Nbuf) != 0);

+

+  if (TcpTransmitSegment (Tcb, Nbuf) != 0) {

+    goto OnError;

+  }

+

+  //

+  // The retransmitted buffer may be on the SndQue,

+  // trim TCP head because all the buffers on SndQue

+  // are headless.

+  //

+  ASSERT (Nbuf->Tcp != NULL);

+  NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);

+  Nbuf->Tcp = NULL;

+

+  NetbufFree (Nbuf);

+  return 0;

+

+OnError:

+  if (Nbuf != NULL) {

+    NetbufFree (Nbuf);

+  }

+

+  return -1;

+}

+

+/**

+  Verify that all the segments in SndQue are in good shape.

+

+  @param[in]  Head    Pointer to the head node of the SndQue.

+

+  @retval     0       At least one segment is broken.

+  @retval     1       All segments in the specific queue are in good shape.

+

+**/

+INTN

+TcpCheckSndQue (

+  IN LIST_ENTRY     *Head

+  )

+{

+  LIST_ENTRY      *Entry;

+  NET_BUF         *Nbuf;

+  TCP_SEQNO       Seq;

+

+  if (IsListEmpty (Head)) {

+    return 1;

+  }

+  //

+  // Initialize the Seq.

+  //

+  Entry = Head->ForwardLink;

+  Nbuf  = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);

+  Seq   = TCPSEG_NETBUF (Nbuf)->Seq;

+

+  NET_LIST_FOR_EACH (Entry, Head) {

+    Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);

+

+    if (TcpVerifySegment (Nbuf) == 0) {

+      return 0;

+    }

+

+    //

+    // All the node in the SndQue should has:

+    // SEG.SEQ = LAST_SEG.END

+    //

+    if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {

+      return 0;

+    }

+

+    Seq = TCPSEG_NETBUF (Nbuf)->End;

+  }

+

+  return 1;

+}

+

+/**

+  Check whether to send data/SYN/FIN and piggyback an ACK.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Force   If TRUE, ignore the sender's SWS avoidance algorithm

+                           and send out data by force.

+

+  @return The number of bytes sent.

+

+**/

+INTN

+TcpToSendData (

+  IN OUT TCP_CB *Tcb,

+  IN     INTN   Force

+  )

+{

+  UINT32    Len;

+  INTN      Sent;

+  UINT8     Flag;

+  NET_BUF   *Nbuf;

+  TCP_SEG   *Seg;

+  TCP_SEQNO Seq;

+  TCP_SEQNO End;

+

+  ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN));

+

+  Sent = 0;

+

+  if ((Tcb->State == TCP_CLOSED) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {

+

+    return 0;

+  }

+

+  do {

+    //

+    // Compute how much data can be sent

+    //

+    Len   = TcpDataToSend (Tcb, Force);

+    Seq   = Tcb->SndNxt;

+

+    ASSERT ((Tcb->State) < (sizeof (mTcpOutFlag) / sizeof (mTcpOutFlag[0])));

+    Flag  = mTcpOutFlag[Tcb->State];

+

+    if ((Flag & TCP_FLG_SYN) != 0) {

+

+      Seq = Tcb->Iss;

+      Len = 0;

+    }

+

+    //

+    // Only send a segment without data if SYN or

+    // FIN is set.

+    //

+    if ((Len == 0) && ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) {

+      return Sent;

+    }

+

+    Nbuf = TcpGetSegment (Tcb, Seq, Len);

+

+    if (Nbuf == NULL) {

+      DEBUG (

+        (EFI_D_ERROR,

+        "TcpToSendData: failed to get a segment for TCB %p\n",

+        Tcb)

+        );

+

+      goto OnError;

+    }

+

+    Seg = TCPSEG_NETBUF (Nbuf);

+

+    //

+    // Set the TcpSeg in Nbuf.

+    //

+    Len = Nbuf->TotalSize;

+    End = Seq + Len;

+    if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {

+      End++;

+    }

+

+    if ((Flag & TCP_FLG_FIN) != 0) {

+      //

+      // Send FIN if all data is sent, and FIN is

+      // in the window

+      //

+      if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&

+          (GET_SND_DATASIZE (Tcb->Sk) == 0) &&

+          TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)

+            ) {

+        DEBUG (

+          (EFI_D_INFO,

+          "TcpToSendData: send FIN to peer for TCB %p in state %s\n",

+          Tcb,

+          mTcpStateName[Tcb->State])

+          );

+

+        End++;

+      } else {

+        TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);

+      }

+    }

+

+    Seg->Seq  = Seq;

+    Seg->End  = End;

+    Seg->Flag = Flag;

+

+    ASSERT (TcpVerifySegment (Nbuf) != 0);

+    ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0);

+

+    //

+    // Don't send an empty segment here.

+    //

+    if (Seg->End == Seg->Seq) {

+      DEBUG (

+        (EFI_D_WARN,

+        "TcpToSendData: created a empty segment for TCB %p, free it now\n",

+        Tcb)

+        );

+

+      NetbufFree (Nbuf);

+      return Sent;

+    }

+

+    if (TcpTransmitSegment (Tcb, Nbuf) != 0) {

+      NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);

+      Nbuf->Tcp = NULL;

+

+      if ((Flag & TCP_FLG_FIN) != 0)  {

+        TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);

+      }

+

+      goto OnError;

+    }

+

+    Sent += TCP_SUB_SEQ (End, Seq);

+

+    //

+    // All the buffers in the SndQue are headless.

+    //

+    ASSERT (Nbuf->Tcp != NULL);

+

+    NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);

+    Nbuf->Tcp = NULL;

+

+    NetbufFree (Nbuf);

+

+    //

+    // Update the status in TCB.

+    //

+    Tcb->DelayedAck = 0;

+

+    if ((Flag & TCP_FLG_FIN) != 0) {

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);

+    }

+

+    if (TCP_SEQ_GT (End, Tcb->SndNxt)) {

+      Tcb->SndNxt = End;

+    }

+

+    if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {

+      TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);

+    }

+

+    //

+    // Enable RTT measurement only if not in retransmit.

+    // Karn's algorithm requires not to update RTT when in loss.

+    //

+    if ((Tcb->CongestState == TCP_CONGEST_OPEN) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {

+

+      DEBUG (

+        (EFI_D_INFO,

+        "TcpToSendData: set RTT measure sequence %d for TCB %p\n",

+        Seq,

+        Tcb)

+        );

+

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);

+      Tcb->RttSeq     = Seq;

+      Tcb->RttMeasure = 0;

+    }

+

+  } while (Len == Tcb->SndMss);

+

+  return Sent;

+

+OnError:

+  if (Nbuf != NULL) {

+    NetbufFree (Nbuf);

+  }

+

+  return Sent;

+}

+

+/**

+  Send an ACK immediately.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpSendAck (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  NET_BUF *Nbuf;

+  TCP_SEG *Seg;

+

+  Nbuf = NetbufAlloc (TCP_MAX_HEAD);

+

+  if (Nbuf == NULL) {

+    return;

+  }

+

+  NetbufReserve (Nbuf, TCP_MAX_HEAD);

+

+  Seg       = TCPSEG_NETBUF (Nbuf);

+  Seg->Seq  = Tcb->SndNxt;

+  Seg->End  = Tcb->SndNxt;

+  Seg->Flag = TCP_FLG_ACK;

+

+  if (TcpTransmitSegment (Tcb, Nbuf) == 0) {

+    TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);

+    Tcb->DelayedAck = 0;

+  }

+

+  NetbufFree (Nbuf);

+}

+

+/**

+  Send a zero probe segment. It can be used by keepalive and zero window probe.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+  @retval 0       The zero probe segment was sent out successfully.

+  @retval other   An error condition occurred.

+

+**/

+INTN

+TcpSendZeroProbe (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  NET_BUF *Nbuf;

+  TCP_SEG *Seg;

+  INTN     Result;

+

+  Nbuf = NetbufAlloc (TCP_MAX_HEAD);

+

+  if (Nbuf == NULL) {

+    return -1;

+  }

+

+  NetbufReserve (Nbuf, TCP_MAX_HEAD);

+

+  //

+  // SndNxt-1 is out of window. The peer should respond

+  // with an ACK.

+  //

+  Seg       = TCPSEG_NETBUF (Nbuf);

+  Seg->Seq  = Tcb->SndNxt - 1;

+  Seg->End  = Tcb->SndNxt - 1;

+  Seg->Flag = TCP_FLG_ACK;

+

+  Result    = TcpTransmitSegment (Tcb, Nbuf);

+  NetbufFree (Nbuf);

+

+  return Result;

+}

+

+/**

+  Check whether to send an ACK or delayed ACK.

+

+  @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpToSendAck (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  UINT32 TcpNow;

+

+  //

+  // Generally, TCP should send a delayed ACK unless:

+  //   1. ACK at least every other FULL sized segment received.

+  //   2. Packets received out of order.

+  //   3. Receiving window is open.

+  //

+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Tcb->DelayedAck >= 1)) {

+    TcpSendAck (Tcb);

+    return;

+  }

+

+  TcpNow = TcpRcvWinNow (Tcb);

+

+  if (TcpNow > TcpRcvWinOld (Tcb)) {

+    TcpSendAck (Tcb);

+    return;

+  }

+

+  DEBUG (

+    (EFI_D_INFO,

+    "TcpToSendAck: scheduled a delayed ACK for TCB %p\n",

+    Tcb)

+    );

+

+  //

+  // Schedule a delayed ACK.

+  //

+  Tcb->DelayedAck++;

+}

+

+/**

+  Send a RESET segment in response to the segment received.

+

+  @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance. May be NULL.

+  @param[in]  Head    TCP header of the segment that triggers the reset.

+  @param[in]  Len     Length of the segment that triggers the reset.

+  @param[in]  Local   Local IP address.

+  @param[in]  Remote  Remote peer's IP address.

+  @param[in]  Version IP_VERSION_4 indicates TCP is running on IP4 stack,

+                      IP_VERSION_6 indicates TCP is running on IP6 stack.

+

+  @retval     0       A reset was sent or there is no need to send it.

+  @retval     -1      No reset is sent.

+

+**/

+INTN

+TcpSendReset (

+  IN TCP_CB          *Tcb,

+  IN TCP_HEAD        *Head,

+  IN INT32           Len,

+  IN EFI_IP_ADDRESS  *Local,

+  IN EFI_IP_ADDRESS  *Remote,

+  IN UINT8           Version

+  )

+{

+  NET_BUF   *Nbuf;

+  TCP_HEAD  *Nhead;

+  UINT16    HeadSum;

+

+  //

+  // Don't respond to a Reset with reset.

+  //

+  if ((Head->Flag & TCP_FLG_RST) != 0) {

+    return 0;

+  }

+

+  Nbuf = NetbufAlloc (TCP_MAX_HEAD);

+

+  if (Nbuf == NULL) {

+    return -1;

+  }

+

+  Nhead = (TCP_HEAD *) NetbufAllocSpace (

+                        Nbuf,

+                        sizeof (TCP_HEAD),

+                        NET_BUF_TAIL

+                        );

+

+  ASSERT (Nhead != NULL);

+

+  Nbuf->Tcp   = Nhead;

+  Nhead->Flag = TCP_FLG_RST;

+

+  //

+  // Derive Seq/ACK from the segment if no TCB

+  // is associated with it, otherwise derive from the Tcb.

+  //

+  if (Tcb == NULL) {

+

+    if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {

+      Nhead->Seq  = Head->Ack;

+      Nhead->Ack  = 0;

+    } else {

+      Nhead->Seq = 0;

+      TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);

+      Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);

+    }

+  } else {

+

+    Nhead->Seq  = HTONL (Tcb->SndNxt);

+    Nhead->Ack  = HTONL (Tcb->RcvNxt);

+    TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);

+  }

+

+  Nhead->SrcPort  = Head->DstPort;

+  Nhead->DstPort  = Head->SrcPort;

+  Nhead->HeadLen  = (UINT8) (sizeof (TCP_HEAD) >> 2);

+  Nhead->Res      = 0;

+  Nhead->Wnd      = HTONS (0xFFFF);

+  Nhead->Checksum = 0;

+  Nhead->Urg      = 0;

+

+  if (Version == IP_VERSION_4) {

+    HeadSum = NetPseudoHeadChecksum (Local->Addr[0], Remote->Addr[0], 6, 0);

+  } else {

+    HeadSum = NetIp6PseudoHeadChecksum (&Local->v6, &Remote->v6, 6, 0);

+  }

+

+  Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);

+

+  TcpSendIpPacket (Tcb, Nbuf, Local, Remote, Version);

+

+  NetbufFree (Nbuf);

+

+  return 0;

+}

+

+/**

+  Verify that the segment is in good shape.

+

+  @param[in]  Nbuf    The buffer that contains the segment to be checked.

+

+  @retval     0       The segment is broken.

+  @retval     1       The segment is in good shape.

+

+**/

+INTN

+TcpVerifySegment (

+  IN NET_BUF *Nbuf

+  )

+{

+  TCP_HEAD  *Head;

+  TCP_SEG   *Seg;

+  UINT32    Len;

+

+  if (Nbuf == NULL) {

+    return 1;

+  }

+

+  NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);

+

+  Seg   = TCPSEG_NETBUF (Nbuf);

+  Len   = Nbuf->TotalSize;

+  Head  = Nbuf->Tcp;

+

+  if (Head != NULL) {

+    if (Head->Flag != Seg->Flag) {

+      return 0;

+    }

+

+    Len -= (Head->HeadLen << 2);

+  }

+

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {

+    Len++;

+  }

+

+  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {

+    Len++;

+  }

+

+  if (Seg->Seq + Len != Seg->End) {

+    return 0;

+  }

+

+  return 1;

+}

+

diff --git a/NetworkPkg/TcpDxe/TcpProto.h b/NetworkPkg/TcpDxe/TcpProto.h
new file mode 100644
index 0000000..88dfbb9
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpProto.h
@@ -0,0 +1,342 @@
+/** @file

+  TCP protocol header file.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _TCP_PROTO_H_

+#define _TCP_PROTO_H_

+

+///

+/// Tcp states don't change their order. It is used as an

+/// index to mTcpOutFlag and other macros.

+///

+#define TCP_CLOSED       0

+#define TCP_LISTEN       1

+#define TCP_SYN_SENT     2

+#define TCP_SYN_RCVD     3

+#define TCP_ESTABLISHED  4

+#define TCP_FIN_WAIT_1   5

+#define TCP_FIN_WAIT_2   6

+#define TCP_CLOSING      7

+#define TCP_TIME_WAIT    8

+#define TCP_CLOSE_WAIT   9

+#define TCP_LAST_ACK     10

+

+

+///

+/// Flags in the TCP header

+///

+#define TCP_FLG_FIN      0x01

+#define TCP_FLG_SYN      0x02

+#define TCP_FLG_RST      0x04

+#define TCP_FLG_PSH      0x08

+#define TCP_FLG_ACK      0x10

+#define TCP_FLG_URG      0x20

+

+ //

+ // mask for all the flags

+ //

+#define TCP_FLG_FLAG     0x3F

+

+

+#define TCP_CONNECT_REFUSED      (-1) ///< TCP error status

+#define TCP_CONNECT_RESET        (-2) ///< TCP error status

+#define TCP_CONNECT_CLOSED       (-3) ///< TCP error status

+

+//

+// Current congestion status as suggested by RFC3782.

+//

+#define TCP_CONGEST_RECOVER      1  ///< During the NewReno fast recovery.

+#define TCP_CONGEST_LOSS         2  ///< Retxmit because of retxmit time out.

+#define TCP_CONGEST_OPEN         3  ///< TCP is opening its congestion window.

+

+//

+// TCP control flags

+//

+#define TCP_CTRL_NO_NAGLE        0x0001 ///< Disable Nagle algorithm

+#define TCP_CTRL_NO_KEEPALIVE    0x0002 ///< Disable keepalive timer.

+#define TCP_CTRL_NO_WS           0x0004 ///< Disable window scale option.

+#define TCP_CTRL_RCVD_WS         0x0008 ///< Received a wnd scale option in syn.

+#define TCP_CTRL_NO_TS           0x0010 ///< Disable Timestamp option.

+#define TCP_CTRL_RCVD_TS         0x0020 ///< Received a Timestamp option in syn.

+#define TCP_CTRL_SND_TS          0x0040 ///< Send Timestamp option to remote.

+#define TCP_CTRL_SND_URG         0x0080 ///< In urgent send mode.

+#define TCP_CTRL_RCVD_URG        0x0100 ///< In urgent receive mode.

+#define TCP_CTRL_SND_PSH         0x0200 ///< In PUSH send mode.

+#define TCP_CTRL_FIN_SENT        0x0400 ///< FIN is sent.

+#define TCP_CTRL_FIN_ACKED       0x0800 ///< FIN is ACKed.

+#define TCP_CTRL_TIMER_ON        0x1000 ///< At least one of the timer is on.

+#define TCP_CTRL_RTT_ON          0x2000 ///< The RTT measurement is on.

+#define TCP_CTRL_ACK_NOW         0x4000 ///< Send the ACK now, don't delay.

+

+//

+// Timer related values

+//

+#define TCP_TIMER_CONNECT        0                  ///< Connection establishment timer.

+#define TCP_TIMER_REXMIT         1                  ///< Retransmit timer.

+#define TCP_TIMER_PROBE          2                  ///< Window probe timer.

+#define TCP_TIMER_KEEPALIVE      3                  ///< Keepalive timer.

+#define TCP_TIMER_FINWAIT2       4                  ///< FIN_WAIT_2 timer.

+#define TCP_TIMER_2MSL           5                  ///< TIME_WAIT timer.

+#define TCP_TIMER_NUMBER         6                  ///< The total number of the TCP timer.

+#define TCP_TICK                 200                ///< Every TCP tick is 200ms.

+#define TCP_TICK_HZ              5                  ///< The frequence of TCP tick.

+#define TCP_RTT_SHIFT            3                  ///< SRTT & RTTVAR scaled by 8.

+#define TCP_RTO_MIN              TCP_TICK_HZ        ///< The minium value of RTO.

+#define TCP_RTO_MAX              (TCP_TICK_HZ * 60) ///< The maxium value of RTO.

+#define TCP_FOLD_RTT             4                  ///< Timeout threshod to fold RTT.

+

+//

+// Default values for some timers

+//

+#define TCP_MAX_LOSS             12                          ///< Default max times to retxmit.

+#define TCP_KEEPALIVE_IDLE_MIN   (TCP_TICK_HZ * 60 * 60 * 2) ///< First keepalive.

+#define TCP_KEEPALIVE_PERIOD     (TCP_TICK_HZ * 60)

+#define TCP_MAX_KEEPALIVE        8

+#define TCP_FIN_WAIT2_TIME       (2 * TCP_TICK_HZ)

+#define TCP_TIME_WAIT_TIME       (2 * TCP_TICK_HZ)

+#define TCP_PAWS_24DAY           (24 * 24 * 60 * 60 * TCP_TICK_HZ)

+#define TCP_CONNECT_TIME         (75 * TCP_TICK_HZ)

+

+//

+// The header space to be reserved before TCP data to accomodate :

+// 60byte IP head + 60byte TCP head + link layer head

+//

+#define TCP_MAX_HEAD             192

+

+//

+// Value ranges for some control option

+//

+#define TCP_RCV_BUF_SIZE         (2 * 1024 * 1024)

+#define TCP_RCV_BUF_SIZE_MIN     (8 * 1024)

+#define TCP_SND_BUF_SIZE         (2 * 1024 * 1024)

+#define TCP_SND_BUF_SIZE_MIN     (8 * 1024)

+#define TCP_BACKLOG              10

+#define TCP_BACKLOG_MIN          5

+#define TCP_MAX_LOSS_MIN         6

+#define TCP_CONNECT_TIME_MIN     (60 * TCP_TICK_HZ)

+#define TCP_MAX_KEEPALIVE_MIN    4

+#define TCP_KEEPALIVE_IDLE_MAX   (TCP_TICK_HZ * 60 * 60 * 4)

+#define TCP_KEEPALIVE_PERIOD_MIN (TCP_TICK_HZ * 30)

+#define TCP_FIN_WAIT2_TIME_MAX   (4 * TCP_TICK_HZ)

+#define TCP_TIME_WAIT_TIME_MAX   (60 * TCP_TICK_HZ)

+

+///

+/// TCP_CONNECTED: both ends have synchronized their ISN.

+///

+#define TCP_CONNECTED(state)     ((state) > TCP_SYN_RCVD)

+

+#define TCP_FIN_RCVD(State) \

+  ( \

+    ((State) == TCP_CLOSE_WAIT) || \

+    ((State) == TCP_LAST_ACK) || \

+    ((State) == TCP_CLOSING) || \

+    ((State) == TCP_TIME_WAIT) \

+  )

+

+#define TCP_LOCAL_CLOSED(State) \

+  ( \

+    ((State) == TCP_FIN_WAIT_1) || \

+    ((State) == TCP_FIN_WAIT_2) || \

+    ((State) == TCP_CLOSING) || \

+    ((State) == TCP_TIME_WAIT) || \

+    ((State) == TCP_LAST_ACK) \

+  )

+

+//

+// Get the TCP_SEG point from a net buffer's ProtoData.

+//

+#define TCPSEG_NETBUF(NBuf)     ((TCP_SEG *) ((NBuf)->ProtoData))

+

+//

+// Macros to compare sequence no

+//

+#define TCP_SEQ_LT(SeqA, SeqB)  ((INT32) ((SeqA) - (SeqB)) < 0)

+#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0)

+#define TCP_SEQ_GT(SeqA, SeqB)  ((INT32) ((SeqB) - (SeqA)) < 0)

+#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0)

+

+//

+// TCP_SEQ_BETWEEN return whether b <= m <= e

+//

+#define TCP_SEQ_BETWEEN(b, m, e)    ((e) - (b) >= (m) - (b))

+

+//

+// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2

+//

+#define TCP_SUB_SEQ(Seq1, Seq2)     ((UINT32) ((Seq1) - (Seq2)))

+

+//

+// Check whether Flag is on

+//

+#define TCP_FLG_ON(Value, Flag)     ((BOOLEAN) (((Value) & (Flag)) != 0))

+//

+// Set and Clear operation on a Flag

+//

+#define TCP_SET_FLG(Value, Flag)    ((Value) |= (Flag))

+#define TCP_CLEAR_FLG(Value, Flag)  ((Value) &= ~(Flag))

+

+//

+// Test whether two peers are equal

+//

+#define TCP_PEER_EQUAL(Pa, Pb, Ver) \

+  (((Pa)->Port == (Pb)->Port) && TcpIsIpEqual(&((Pa)->Ip), &((Pb)->Ip), Ver))

+

+//

+// Test whether Pa matches Pb, or Pa is more specific

+// than pb. Zero means wildcard.

+//

+#define TCP_PEER_MATCH(Pa, Pb, Ver) \

+  ( \

+    (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port)) && \

+    (TcpIsIpZero (&((Pb)->Ip), Ver) || TcpIsIpEqual (&((Pb)->Ip), &((Pa)->Ip), Ver)) \

+  )

+

+#define TCP_TIMER_ON(Flag, Timer)     ((Flag) & (1 << (Timer)))

+#define TCP_SET_TIMER(Flag, Timer)    ((Flag) = (UINT16) ((Flag) | (1 << (Timer))))

+#define TCP_CLEAR_TIMER(Flag, Timer)  ((Flag) = (UINT16) ((Flag) & (~(1 << (Timer)))))

+

+

+#define TCP_TIME_LT(Ta, Tb)           ((INT32) ((Ta) - (Tb)) < 0)

+#define TCP_TIME_LEQ(Ta, Tb)          ((INT32) ((Ta) - (Tb)) <= 0)

+#define TCP_SUB_TIME(Ta, Tb)          ((UINT32) ((Ta) - (Tb)))

+

+#define TCP_MAX_WIN                   0xFFFFU

+

+///

+/// TCP segmentation data.

+///

+typedef struct _TCP_SEG {

+  TCP_SEQNO Seq;  ///< Starting sequence number.

+  TCP_SEQNO End;  ///< The sequence of the last byte + 1, include SYN/FIN. End-Seq = SEG.LEN.

+  TCP_SEQNO Ack;  ///< ACK field in the segment.

+  UINT8     Flag; ///< TCP header flags.

+  UINT16    Urg;  ///< Valid if URG flag is set.

+  UINT32    Wnd;  ///< TCP window size field.

+} TCP_SEG;

+

+///

+/// Network endpoint, IP plus Port structure.

+///

+typedef struct _TCP_PEER {

+  EFI_IP_ADDRESS  Ip;     ///< IP address, in network byte order.

+  TCP_PORTNO      Port;   ///< Port number, in network byte order.

+} TCP_PEER;

+

+typedef struct _TCP_CONTROL_BLOCK  TCP_CB;

+

+///

+/// TCP control block: it includes various states.

+///

+struct _TCP_CONTROL_BLOCK {

+  LIST_ENTRY        List;     ///< Back and forward link entry

+  TCP_CB            *Parent;  ///< The parent TCP_CB structure

+

+  SOCKET            *Sk;      ///< The socket it controled.

+  TCP_PEER          LocalEnd; ///< Local endpoint.

+  TCP_PEER          RemoteEnd;///< Remote endpoint.

+

+  LIST_ENTRY        SndQue;   ///< Retxmission queue.

+  LIST_ENTRY        RcvQue;   ///< Reassemble queue.

+  UINT32            CtrlFlag; ///< Control flags, such as NO_NAGLE.

+  INT32             Error;    ///< Soft error status, such as TCP_CONNECT_RESET.

+

+  //

+  // RFC793 and RFC1122 defined variables

+  //

+  UINT8             State;      ///< TCP state, such as SYN_SENT, LISTEN.

+  UINT8             DelayedAck; ///< Number of delayed ACKs.

+  UINT16            HeadSum;    ///< Checksum of the fixed parts of pesudo

+                                ///< header: Src IP, Dst IP, 0, Protocol,

+                                ///< do not include the TCP length.

+

+  TCP_SEQNO         Iss;        ///< Initial Sending Sequence.

+  TCP_SEQNO         SndUna;     ///< First unacknowledged data.

+  TCP_SEQNO         SndNxt;     ///< Next data sequence to send.

+  TCP_SEQNO         SndPsh;     ///< Send PUSH point.

+  TCP_SEQNO         SndUp;      ///< Send urgent point.

+  UINT32            SndWnd;     ///< Window advertised by the remote peer.

+  UINT32            SndWndMax;  ///< Max send window advertised by the peer.

+  TCP_SEQNO         SndWl1;     ///< Seq number used for last window update.

+  TCP_SEQNO         SndWl2;     ///< Ack no of last window update.

+  UINT16            SndMss;     ///< Max send segment size.

+  TCP_SEQNO         RcvNxt;     ///< Next sequence no to receive.

+  UINT32            RcvWnd;     ///< Window advertised by the local peer.

+  TCP_SEQNO         RcvWl2;     ///< The RcvNxt (or ACK) of last window update.

+                                ///< It is necessary because of delayed ACK.

+

+  TCP_SEQNO         RcvUp;                   ///< Urgent point;

+  TCP_SEQNO         Irs;                     ///< Initial Receiving Sequence.

+  UINT16            RcvMss;                  ///< Max receive segment size.

+  UINT16            EnabledTimer;            ///< Which timer is currently enabled.

+  UINT32            Timer[TCP_TIMER_NUMBER]; ///< When the timer will expire.

+  INT32             NextExpire;  ///< Countdown offset for the nearest timer.

+  UINT32            Idle;        ///< How long the connection is in idle.

+  UINT32            ProbeTime;   ///< The time out value for current window prober.

+  BOOLEAN           ProbeTimerOn;///< If TRUE, the probe time is on.

+

+  //

+  // RFC1323 defined variables, about window scale,

+  // timestamp and PAWS

+  //

+  UINT8             SndWndScale;  ///< Wndscale received from the peer.

+  UINT8             RcvWndScale;  ///< Wndscale used to scale local buffer.

+  UINT32            TsRecent;     ///< TsRecent to echo to the remote peer.

+  UINT32            TsRecentAge;  ///< When this TsRecent is updated.

+

+  //

+  // RFC2988 defined variables. about RTT measurement

+  //

+  TCP_SEQNO         RttSeq;     ///< The seq of measured segment now.

+  UINT32            RttMeasure; ///< Currently measured RTT in heartbeats.

+  UINT32            SRtt;       ///< Smoothed RTT, scaled by 8.

+  UINT32            RttVar;     ///< RTT variance, scaled by 8.

+  UINT32            Rto;        ///< Current RTO, not scaled.

+

+  //

+  // RFC2581, and 3782 variables.

+  // Congestion control + NewReno fast recovery.

+  //

+  UINT32            CWnd;         ///< Sender's congestion window.

+  UINT32            Ssthresh;     ///< Slow start threshold.

+  TCP_SEQNO         Recover;      ///< Recover point for NewReno.

+  UINT16            DupAck;       ///< Number of duplicate ACKs.

+  UINT8             CongestState; ///< The current congestion state(RFC3782).

+  UINT8             LossTimes;    ///< Number of retxmit timeouts in a row.

+  TCP_SEQNO         LossRecover;  ///< Recover point for retxmit.

+

+  //

+  // configuration parameters, for EFI_TCP4_PROTOCOL specification

+  //

+  UINT32            KeepAliveIdle;   ///< Idle time before sending first probe.

+  UINT32            KeepAlivePeriod; ///< Interval for subsequent keep alive probe.

+  UINT8             MaxKeepAlive;    ///< Maxium keep alive probe times.

+  UINT8             KeepAliveProbes; ///< The number of keep alive probe.

+  UINT16            MaxRexmit;       ///< The maxium number of retxmit before abort.

+  UINT32            FinWait2Timeout; ///< The FIN_WAIT_2 timeout.

+  UINT32            TimeWaitTimeout; ///< The TIME_WAIT timeout.

+  UINT32            ConnectTimeout;  ///< The connect establishment timeout.

+

+  //

+  // configuration for tcp provided by user

+  //

+  BOOLEAN           UseDefaultAddr;

+  UINT8             Tos;

+  UINT8             Ttl;

+  EFI_IPv4_ADDRESS  SubnetMask;

+

+  IP_IO_IP_INFO     *IpInfo;        ///< Pointer reference to Ip used to send pkt

+  UINT32            Tick;           ///< 1 tick = 200ms

+};

+

+#endif

diff --git a/NetworkPkg/TcpDxe/TcpTimer.c b/NetworkPkg/TcpDxe/TcpTimer.c
new file mode 100644
index 0000000..cc52ad9
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpTimer.c
@@ -0,0 +1,593 @@
+/** @file

+  TCP timer related functions.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "TcpMain.h"

+

+UINT32    mTcpTick = 1000;

+

+/**

+  Connect timeout handler.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpConnectTimeout (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Timeout handler for TCP retransmission timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpRexmitTimeout (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Timeout handler for window probe timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpProbeTimeout (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Timeout handler for keepalive timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpKeepaliveTimeout (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Timeout handler for FIN_WAIT_2 timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpFinwait2Timeout (

+  IN OUT TCP_CB *Tcb

+  );

+

+/**

+  Timeout handler for 2MSL timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+Tcp2MSLTimeout (

+  IN OUT TCP_CB *Tcb

+  );

+

+TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {

+  TcpConnectTimeout,

+  TcpRexmitTimeout,

+  TcpProbeTimeout,

+  TcpKeepaliveTimeout,

+  TcpFinwait2Timeout,

+  Tcp2MSLTimeout,

+};

+

+/**

+  Close the TCP connection.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpClose (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  NetbufFreeList (&Tcb->SndQue);

+  NetbufFreeList (&Tcb->RcvQue);

+

+  TcpSetState (Tcb, TCP_CLOSED);

+}

+

+/**

+  Backoff the RTO.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpBackoffRto (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  //

+  // Fold the RTT estimate if too many times, the estimate

+  // may be wrong, fold it. So the next time a valid

+  // measurement is sampled, we can start fresh.

+  //

+  if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) {

+    Tcb->RttVar += Tcb->SRtt >> 2;

+    Tcb->SRtt = 0;

+  }

+

+  Tcb->Rto <<= 1;

+

+  if (Tcb->Rto < TCP_RTO_MIN) {

+

+    Tcb->Rto = TCP_RTO_MIN;

+  } else if (Tcb->Rto > TCP_RTO_MAX) {

+

+    Tcb->Rto = TCP_RTO_MAX;

+  }

+}

+

+/**

+  Connect timeout handler.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpConnectTimeout (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  if (!TCP_CONNECTED (Tcb->State)) {

+    DEBUG (

+      (EFI_D_ERROR,

+      "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n",

+      Tcb)

+      );

+

+    if (EFI_ABORTED == Tcb->Sk->SockError) {

+      SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);

+    }

+

+    if (TCP_SYN_RCVD == Tcb->State) {

+      DEBUG (

+        (EFI_D_WARN,

+        "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",

+        Tcb)

+        );

+

+      TcpResetConnection (Tcb);

+

+    }

+

+    TcpClose (Tcb);

+  }

+}

+

+

+/**

+  Timeout handler for TCP retransmission timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpRexmitTimeout (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  UINT32  FlightSize;

+

+  DEBUG (

+    (EFI_D_WARN,

+    "TcpRexmitTimeout: transmission timeout for TCB %p\n",

+    Tcb)

+    );

+

+  //

+  // Set the congestion window. FlightSize is the

+  // amount of data that has been sent but not

+  // yet ACKed.

+  //

+  FlightSize        = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);

+  Tcb->Ssthresh     = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2);

+

+  Tcb->CWnd         = Tcb->SndMss;

+  Tcb->LossRecover  = Tcb->SndNxt;

+

+  Tcb->LossTimes++;

+  if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {

+

+    DEBUG (

+      (EFI_D_ERROR,

+      "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",

+      Tcb)

+      );

+

+    if (EFI_ABORTED == Tcb->Sk->SockError) {

+      SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);

+    }

+

+    TcpClose (Tcb);

+    return ;

+  }

+

+  TcpBackoffRto (Tcb);

+  TcpRetransmit (Tcb, Tcb->SndUna);

+  TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);

+

+  Tcb->CongestState = TCP_CONGEST_LOSS;

+

+  TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);

+}

+

+/**

+  Timeout handler for window probe timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpProbeTimeout (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  //

+  // This is the timer for sender's SWSA. RFC1122 requires

+  // a timer set for sender's SWSA, and suggest combine it

+  // with window probe timer. If data is sent, don't set

+  // the probe timer, since retransmit timer is on.

+  //

+  if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {

+

+    ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);

+    Tcb->ProbeTimerOn = FALSE;

+    return ;

+  }

+

+  TcpSendZeroProbe (Tcb);

+  TcpSetProbeTimer (Tcb);

+}

+

+/**

+  Timeout handler for keepalive timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpKeepaliveTimeout (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  Tcb->KeepAliveProbes++;

+

+  //

+  // Too many Keep-alive probes, drop the connection

+  //

+  if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {

+

+    if (EFI_ABORTED == Tcb->Sk->SockError) {

+      SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);

+    }

+

+    TcpClose (Tcb);

+    return ;

+  }

+

+  TcpSendZeroProbe (Tcb);

+  TcpSetKeepaliveTimer (Tcb);

+}

+

+/**

+  Timeout handler for FIN_WAIT_2 timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpFinwait2Timeout (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  DEBUG (

+    (EFI_D_WARN,

+    "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",

+    Tcb)

+    );

+

+  TcpClose (Tcb);

+}

+

+/**

+  Timeout handler for 2MSL timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+Tcp2MSLTimeout (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  DEBUG (

+    (EFI_D_WARN,

+    "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",

+    Tcb)

+    );

+

+  TcpClose (Tcb);

+}

+

+/**

+  Update the timer status and the next expire time according to the timers

+  to expire in a specific future time slot.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpUpdateTimer (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  UINT16  Index;

+

+  //

+  // Don't use a too large value to init NextExpire

+  // since mTcpTick wraps around as sequence no does.

+  //

+  Tcb->NextExpire = TCP_EXPIRE_TIME;

+  TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);

+

+  for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {

+

+    if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&

+        TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)

+        ) {

+

+      Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);

+      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);

+    }

+  }

+}

+

+/**

+  Enable a TCP timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Timer    The index of the timer to be enabled.

+  @param[in]       TimeOut  The timeout value of this timer.

+

+**/

+VOID

+TcpSetTimer (

+  IN OUT TCP_CB *Tcb,

+  IN     UINT16 Timer,

+  IN     UINT32 TimeOut

+  )

+{

+  TCP_SET_TIMER (Tcb->EnabledTimer, Timer);

+  Tcb->Timer[Timer] = mTcpTick + TimeOut;

+

+  TcpUpdateTimer (Tcb);

+}

+

+/**

+  Clear one TCP timer.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+  @param[in]       Timer    The index of the timer to be cleared.

+

+**/

+VOID

+TcpClearTimer (

+  IN OUT TCP_CB *Tcb,

+  IN     UINT16 Timer

+  )

+{

+  TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);

+  TcpUpdateTimer (Tcb);

+}

+

+/**

+  Clear all TCP timers.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpClearAllTimer (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  Tcb->EnabledTimer = 0;

+  TcpUpdateTimer (Tcb);

+}

+

+/**

+  Enable the window prober timer and set the timeout value.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpSetProbeTimer (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  if (!Tcb->ProbeTimerOn) {

+    Tcb->ProbeTime    = Tcb->Rto;

+    Tcb->ProbeTimerOn = TRUE;

+

+  } else {

+    Tcb->ProbeTime <<= 1;

+  }

+

+  if (Tcb->ProbeTime < TCP_RTO_MIN) {

+

+    Tcb->ProbeTime = TCP_RTO_MIN;

+  } else if (Tcb->ProbeTime > TCP_RTO_MAX) {

+

+    Tcb->ProbeTime = TCP_RTO_MAX;

+  }

+

+  TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);

+}

+

+/**

+  Enable the keepalive timer and set the timeout value.

+

+  @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.

+

+**/

+VOID

+TcpSetKeepaliveTimer (

+  IN OUT TCP_CB *Tcb

+  )

+{

+  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {

+    return ;

+

+  }

+

+  //

+  // Set the timer to KeepAliveIdle if either

+  // 1. the keepalive timer is off

+  // 2. The keepalive timer is on, but the idle

+  // is less than KeepAliveIdle, that means the

+  // connection is alive since our last probe.

+  //

+  if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||

+      (Tcb->Idle < Tcb->KeepAliveIdle)

+      ) {

+

+    TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);

+    Tcb->KeepAliveProbes = 0;

+

+  } else {

+

+    TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);

+  }

+}

+

+/**

+  Heart beat timer handler.

+

+  @param[in]  Context        Context of the timer event, ignored.

+

+**/

+VOID

+EFIAPI

+TcpTickingDpc (

+  IN VOID       *Context

+  )

+{

+  LIST_ENTRY      *Entry;

+  LIST_ENTRY      *Next;

+  TCP_CB          *Tcb;

+  INT16           Index;

+

+  mTcpTick++;

+  mTcpGlobalIss += TCP_ISS_INCREMENT_2;

+

+  //

+  // Don't use LIST_FOR_EACH, which isn't delete safe.

+  //

+  for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {

+

+    Next  = Entry->ForwardLink;

+

+    Tcb   = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);

+

+    if (Tcb->State == TCP_CLOSED) {

+      continue;

+    }

+    //

+    // The connection is doing RTT measurement.

+    //

+    if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {

+      Tcb->RttMeasure++;

+    }

+

+    Tcb->Idle++;

+

+    if (Tcb->DelayedAck != 0) {

+      TcpSendAck (Tcb);

+    }

+

+    if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) {

+      Tcb->Tick--;

+    }

+

+    //

+    // No timer is active or no timer expired

+    //

+    if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {

+

+      continue;

+    }

+

+    //

+    // Call the timeout handler for each expired timer.

+    //

+    for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {

+

+      if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {

+        //

+        // disable the timer before calling the handler

+        // in case the handler enables it again.

+        //

+        TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);

+        mTcpTimerHandler[Index](Tcb);

+

+        //

+        // The Tcb may have been deleted by the timer, or

+        // no other timer is set.

+        //

+        if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {

+          break;

+        }

+      }

+    }

+

+    //

+    // If the Tcb still exist or some timer is set, update the timer

+    //

+    if (Index == TCP_TIMER_NUMBER) {

+      TcpUpdateTimer (Tcb);

+    }

+  }

+}

+

+/**

+  Heart beat timer handler, queues the DPC at TPL_CALLBACK.

+

+  @param[in]  Event    Timer event signaled, ignored.

+  @param[in]  Context  Context of the timer event, ignored.

+

+**/

+VOID

+EFIAPI

+TcpTicking (

+  IN EFI_EVENT Event,

+  IN VOID      *Context

+  )

+{

+  QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);

+}

+

diff --git a/NetworkPkg/Udp6Dxe/ComponentName.c b/NetworkPkg/Udp6Dxe/ComponentName.c
new file mode 100644
index 0000000..d7df096
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/ComponentName.c
@@ -0,0 +1,313 @@
+/** @file

+  UEFI Component Name(2) protocol implementation for UDP6 driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Udp6Impl.h"

+

+//

+// EFI Component Name Functions

+//

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6ComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6ComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  EFI_HANDLE                   ChildHandle OPTIONAL,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **ControllerName

+  );

+

+//

+// EFI Component Name Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gUdp6ComponentName = {

+  Udp6ComponentNameGetDriverName,

+  Udp6ComponentNameGetControllerName,

+  "eng"

+};

+

+//

+// EFI Component Name 2 Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Udp6ComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Udp6ComponentNameGetControllerName,

+  "en"

+};

+

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUdp6DriverNameTable[] = {

+  {

+    "eng;en",

+    L"UDP6 Network Service Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6ComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2 (

+           Language,

+           This->SupportedLanguages,

+           mUdp6DriverNameTable,

+           DriverName,

+           (BOOLEAN) (This == &gUdp6ComponentName)

+           );

+}

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6ComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  EFI_HANDLE                   ChildHandle OPTIONAL,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

+

diff --git a/NetworkPkg/Udp6Dxe/Udp6Driver.c b/NetworkPkg/Udp6Dxe/Udp6Driver.c
new file mode 100644
index 0000000..1cfd5f1
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Driver.c
@@ -0,0 +1,556 @@
+/** @file

+  Driver Binding functions and Service Binding functions for the Network driver module.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Udp6Impl.h"

+

+EFI_DRIVER_BINDING_PROTOCOL gUdp6DriverBinding = {

+  Udp6DriverBindingSupported,

+  Udp6DriverBindingStart,

+  Udp6DriverBindingStop,

+  0xa,

+  NULL,

+  NULL

+};

+

+EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding = {

+  Udp6ServiceBindingCreateChild,

+  Udp6ServiceBindingDestroyChild

+};

+

+/**

+  Tests to see if this driver supports a given controller. If a child device is provided,

+  it further tests to see if this driver supports creating a handle for the specified child device.

+

+  This function checks to see if the driver specified by This supports the device specified by

+  ControllerHandle. Drivers will typically use the device path attached to

+  ControllerHandle and/or the services from the bus I/O abstraction attached to

+  ControllerHandle to determine if the driver supports ControllerHandle. This function

+  may be called many times during platform initialization. In order to reduce boot times, the tests

+  performed by this function must be very small, and take as little time as possible to execute. This

+  function must not change the state of any hardware devices, and this function must be aware that the

+  device specified by ControllerHandle may already be managed by the same driver or a

+  different driver. This function must match its calls to AllocatePages() with FreePages(),

+  AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().

+  Because ControllerHandle may have been previously started by the same driver, if a protocol is

+  already in the opened state, then it must not be closed with CloseProtocol(). This is required

+  to guarantee the state of ControllerHandle is not modified by this function.

+

+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.

+  @param[in]  ControllerHandle     The handle of the controller to test. This handle

+                                   must support a protocol interface that supplies

+                                   an I/O abstraction to the driver.

+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This

+                                   parameter is ignored by device drivers, and is optional for bus

+                                   drivers. For bus drivers, if this parameter is not NULL, then

+                                   the bus driver must determine if the bus controller specified

+                                   by ControllerHandle and the child controller specified

+                                   by RemainingDevicePath are both supported by this

+                                   bus driver.

+

+  @retval EFI_SUCCESS              The device specified by ControllerHandle and

+                                   RemainingDevicePath is supported by the driver specified by This.

+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and

+                                   RemainingDevicePath is already being managed by the driver

+                                   specified by This.

+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and

+                                   RemainingDevicePath is already being managed by a different

+                                   driver or an application that requires exclusive access.

+                                   Currently not implemented.

+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and

+                                   RemainingDevicePath is not supported by the driver specified by This.

+**/

+EFI_STATUS

+EFIAPI

+Udp6DriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL

+  )

+{

+  EFI_STATUS  Status;

+  //

+  // Test for the Udp6ServiceBinding Protocol

+  //

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiUdp6ServiceBindingProtocolGuid,

+                  NULL,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+  if (!EFI_ERROR (Status)) {

+    return EFI_ALREADY_STARTED;

+  }

+  //

+  // Test for the Ip6ServiceBinding Protocol

+  //

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiIp6ServiceBindingProtocolGuid,

+                  NULL,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+

+  return Status;

+}

+

+/**

+  Start this driver on ControllerHandle.

+

+  This service is called by the EFI boot service ConnectController(). In order to make

+  drivers as small as possible, there are a few calling restrictions for

+  this service. ConnectController() must follow these

+  calling restrictions. If any other agent wishes to call Start() it

+  must also follow these calling restrictions.

+

+  @param[in]  This                   Protocol instance pointer.

+  @param[in]  ControllerHandle       Handle of device to bind the driver to.

+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child

+                                     device to start.

+

+  @retval EFI_SUCCES             This driver is added to ControllerHandle.

+  @retval EFI_OUT_OF_RESOURCES   The required system resource can't be allocated.

+  @retval other                  This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6DriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL

+  )

+{

+  EFI_STATUS         Status;

+  UDP6_SERVICE_DATA  *Udp6Service;

+

+  //

+  // Allocate Private Context Data Structure.

+  //

+  Udp6Service = AllocateZeroPool (sizeof (UDP6_SERVICE_DATA));

+  if (Udp6Service == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto EXIT;

+  }

+

+  Status = Udp6CreateService (Udp6Service, This->DriverBindingHandle, ControllerHandle);

+  if (EFI_ERROR (Status)) {

+    goto EXIT;

+  }

+

+  //

+  // Install the Udp6ServiceBindingProtocol on the ControllerHandle.

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &ControllerHandle,

+                  &gEfiUdp6ServiceBindingProtocolGuid,

+                  &Udp6Service->ServiceBinding,

+                  NULL

+                  );

+  if (EFI_ERROR (Status)) {

+    Udp6CleanService (Udp6Service);

+    goto EXIT;

+  } else {

+    Status = Udp6SetVariableData (Udp6Service);

+  }

+

+EXIT:

+  if (EFI_ERROR (Status)) {

+    if (Udp6Service != NULL) {

+      FreePool (Udp6Service);

+    }

+  }

+  return Status;

+}

+

+/**

+  Stop this driver on ControllerHandle.

+

+  This service is called by the  EFI boot service DisconnectController(). In order to

+  make drivers as small as possible, there are a few calling

+  restrictions for this service. DisconnectController()

+  must follow these calling restrictions. If any other agent wishes

+  to call Stop(), it must also follow these calling restrictions.

+

+  @param[in]  This                   Protocol instance pointer.

+  @param[in]  ControllerHandle       Handle of device to stop the driver on.

+  @param[in]  NumberOfChildren       Number of Handles in ChildHandleBuffer. If the number

+                                     of children is zero stop the entire bus driver.

+  @param[in]  ChildHandleBuffer      List of Child Handles to Stop. It is optional.

+

+  @retval EFI_SUCCES             This driver is removed ControllerHandle.

+  @retval EFI_DEVICE_ERROR       Can't find the NicHandle from the ControllerHandle and specified GUID.

+  @retval other                  This driver was not removed from this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6DriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL

+  )

+{

+  EFI_STATUS                    Status;

+  EFI_HANDLE                    NicHandle;

+  EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;

+  UDP6_SERVICE_DATA             *Udp6Service;

+  UDP6_INSTANCE_DATA            *Instance;

+

+  //

+  // Find the NicHandle where UDP6 ServiceBinding Protocol is installed.

+  //

+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid);

+  if (NicHandle == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // Retrieve the UDP6 ServiceBinding Protocol.

+  //

+  Status = gBS->OpenProtocol (

+                  NicHandle,

+                  &gEfiUdp6ServiceBindingProtocolGuid,

+                  (VOID **) &ServiceBinding,

+                  This->DriverBindingHandle,

+                  NicHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (ServiceBinding);

+

+  if (NumberOfChildren == 0) {

+

+    gBS->UninstallMultipleProtocolInterfaces (

+           NicHandle,

+           &gEfiUdp6ServiceBindingProtocolGuid,

+           &Udp6Service->ServiceBinding,

+           NULL

+           );

+

+    Udp6ClearVariableData (Udp6Service);

+

+    Udp6CleanService (Udp6Service);

+

+    FreePool (Udp6Service);

+  } else {

+

+    while (!IsListEmpty (&Udp6Service->ChildrenList)) {

+      Instance = NET_LIST_HEAD (&Udp6Service->ChildrenList, UDP6_INSTANCE_DATA, Link);

+

+      Status = ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle);

+    }

+  }

+

+  return Status;

+}

+

+/**

+  Creates a child handle and installs a protocol.

+

+  The CreateChild() function installs a protocol on ChildHandle.

+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.

+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.

+

+  @param[in]       This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in, out]  ChildHandle Pointer to the handle of the child to create. If it is NULL,

+                               then a new handle is created. If it is a pointer to an existing UEFI handle,

+                               then the protocol is added to the existing UEFI handle.

+

+  @retval EFI_SUCCES            The protocol was added to ChildHandle.

+  @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL.

+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create

+                                the child.

+  @retval other                 The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6ServiceBindingCreateChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN OUT EFI_HANDLE                *ChildHandle

+  )

+{

+  EFI_STATUS          Status;

+  UDP6_SERVICE_DATA   *Udp6Service;

+  UDP6_INSTANCE_DATA  *Instance;

+  EFI_TPL             OldTpl;

+  VOID                *Ip6;

+

+  if ((This == NULL) || (ChildHandle == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This);

+

+  //

+  // Allocate the instance private data structure.

+  //

+  Instance = AllocateZeroPool (sizeof (UDP6_INSTANCE_DATA));

+  if (Instance == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Udp6InitInstance (Udp6Service, Instance);

+

+  //

+  // Add an IpInfo for this instance.

+  //

+  Instance->IpInfo = IpIoAddIp (Udp6Service->IpIo);

+  if (Instance->IpInfo == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_ERROR;

+  }

+

+  //

+  // Install the Udp6Protocol for this instance.

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  ChildHandle,

+                  &gEfiUdp6ProtocolGuid,

+                  &Instance->Udp6Proto,

+                  NULL

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Instance->ChildHandle = *ChildHandle;

+

+  //

+  // Open the default Ip6 protocol in the IP_IO BY_CHILD.

+  //

+  Status = gBS->OpenProtocol (

+                  Udp6Service->IpIo->ChildHandle,

+                  &gEfiIp6ProtocolGuid,

+                  (VOID **) &Ip6,

+                  gUdp6DriverBinding.DriverBindingHandle,

+                  Instance->ChildHandle,

+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // Link this instance into the service context data and increase the ChildrenNumber.

+  //

+  InsertTailList (&Udp6Service->ChildrenList, &Instance->Link);

+  Udp6Service->ChildrenNumber++;

+

+  gBS->RestoreTPL (OldTpl);

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  if (Instance->ChildHandle != NULL) {

+    gBS->UninstallMultipleProtocolInterfaces (

+           Instance->ChildHandle,

+           &gEfiUdp6ProtocolGuid,

+           &Instance->Udp6Proto,

+           NULL

+           );

+  }

+

+  if (Instance->IpInfo != NULL) {

+    IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo);

+  }

+

+  Udp6CleanInstance (Instance);

+

+  FreePool (Instance);

+

+  return Status;

+}

+

+/**

+  Destroys a child handle with a set of I/O services.

+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol

+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the

+  last protocol on ChildHandle, then ChildHandle is destroyed.

+

+  @param[in]  This               Protocol instance pointer.

+  @param[in]  ChildHandle        Handle of the child to destroy.

+

+  @retval EFI_SUCCES             The I/O services were removed from the child

+                                 handle.

+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services

+                                 that are being removed.

+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.

+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because

+                                 its  I/O services are being used.

+  @retval other                  The child handle was not destroyed.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6ServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    ChildHandle

+  )

+{

+  EFI_STATUS          Status;

+  UDP6_SERVICE_DATA   *Udp6Service;

+  EFI_UDP6_PROTOCOL   *Udp6Proto;

+  UDP6_INSTANCE_DATA  *Instance;

+  EFI_TPL             OldTpl;

+

+  if ((This == NULL) || (ChildHandle == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This);

+

+  //

+  // Try to get the Udp6 protocol from the ChildHandle.

+  //

+  Status = gBS->OpenProtocol (

+                  ChildHandle,

+                  &gEfiUdp6ProtocolGuid,

+                  (VOID **) &Udp6Proto,

+                  gUdp6DriverBinding.DriverBindingHandle,

+                  ChildHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (Udp6Proto);

+

+  if (Instance->Destroyed) {

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Use the Destroyed flag to avoid the re-entering of the following code.

+  //

+  Instance->Destroyed = TRUE;

+

+  //

+  // Close the Ip6 protocol.

+  //

+  gBS->CloseProtocol (

+         Udp6Service->IpIo->ChildHandle,

+         &gEfiIp6ProtocolGuid,

+         gUdp6DriverBinding.DriverBindingHandle,

+         Instance->ChildHandle

+         );

+

+  //

+  // Uninstall the Udp6Protocol previously installed on the ChildHandle.

+  //

+  Status = gBS->UninstallMultipleProtocolInterfaces (

+                  ChildHandle,

+                  &gEfiUdp6ProtocolGuid,

+                  (VOID *) &Instance->Udp6Proto,

+                  NULL

+                  );

+  if (EFI_ERROR (Status)) {

+    Instance->Destroyed = FALSE;

+    return Status;

+  }

+

+  //

+  // Reset the configuration in case the instance's consumer forgets to do this.

+  //

+  Udp6Proto->Configure (Udp6Proto, NULL);

+

+  //

+  // Remove the IpInfo this instance consumes.

+  //

+  IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo);

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // Remove this instance from the service context data's ChildrenList.

+  //

+  RemoveEntryList (&Instance->Link);

+  Udp6Service->ChildrenNumber--;

+

+  //

+  // Clean the instance.

+  //

+  Udp6CleanInstance (Instance);

+

+  gBS->RestoreTPL (OldTpl);

+

+  FreePool (Instance);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  This is the declaration of an EFI image entry point. This entry point is

+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including

+  both device drivers and bus drivers.

+

+  The entry point for Udp6 driver that installs the driver binding

+  and component name protocol on its ImageHandle.

+

+  @param[in] ImageHandle        The firmware allocated handle for the UEFI image.

+  @param[in] SystemTable        A pointer to the EFI System Table.

+

+  @retval EFI_SUCCESS           The operation completed successfully.

+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6DriverEntryPoint (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  )

+{

+  EFI_STATUS  Status;

+

+  //

+  // Install the Udp6DriverBinding and Udp6ComponentName protocols.

+  //

+

+  Status = EfiLibInstallDriverBindingComponentName2 (

+             ImageHandle,

+             SystemTable,

+             &gUdp6DriverBinding,

+             ImageHandle,

+             &gUdp6ComponentName,

+             &gUdp6ComponentName2

+             );

+  if (!EFI_ERROR (Status)) {

+    //

+    // Initialize the UDP random port.

+    //

+    mUdp6RandomPort = (UINT16)(

+                        ((UINT16) NetRandomInitSeed ()) %

+                         UDP6_PORT_KNOWN +

+                         UDP6_PORT_KNOWN

+                         );

+  }

+

+  return Status;

+}

+

+

diff --git a/NetworkPkg/Udp6Dxe/Udp6Driver.h b/NetworkPkg/Udp6Dxe/Udp6Driver.h
new file mode 100644
index 0000000..e5a923b
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Driver.h
@@ -0,0 +1,182 @@
+/** @file

+  Driver Binding functions and Service Binding functions for the Network driver module.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _UDP6_DRIVER_H_

+#define _UDP6_DRIVER_H_

+

+#include <Protocol/DriverBinding.h>

+#include <Protocol/ServiceBinding.h>

+#include <Protocol/DevicePath.h>

+

+/**

+  Tests to see if this driver supports a given controller. If a child device is provided,

+  it further tests to see if this driver supports creating a handle for the specified child device.

+

+  This function checks to see if the driver specified by This supports the device specified by

+  ControllerHandle. Drivers typically use the device path attached to

+  ControllerHandle and/or the services from the bus I/O abstraction attached to

+  ControllerHandle to determine if the driver supports ControllerHandle. This function

+  may be called many times during platform initialization. In order to reduce boot times, the tests

+  performed by this function must be very small, and take as little time as possible to execute. This

+  function must not change the state of any hardware devices, and this function must be aware that the

+  device specified by ControllerHandle may already be managed by the same driver or a

+  different driver. This function must match its calls to AllocatePages() with FreePages(),

+  AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().

+  Since ControllerHandle may have been previously started by the same driver, if a protocol is

+  already in the opened state, then it must not be closed with CloseProtocol(). This is required

+  to guarantee the state of ControllerHandle is not modified by this function.

+

+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.

+  @param[in]  ControllerHandle     The handle of the controller to test. This handle

+                                   must support a protocol interface that supplies

+                                   an I/O abstraction to the driver.

+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This

+                                   parameter is ignored by device drivers, and is optional for bus

+                                   drivers. For bus drivers, if this parameter is not NULL, then

+                                   the bus driver must determine if the bus controller specified

+                                   by ControllerHandle and the child controller specified

+                                   by RemainingDevicePath are both supported by this

+                                   bus driver.

+

+  @retval EFI_SUCCESS              The device specified by ControllerHandle and

+                                   RemainingDevicePath is supported by the driver specified by This.

+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and

+                                   RemainingDevicePath is already being managed by the driver

+                                   specified by This.

+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and

+                                   RemainingDevicePath is already being managed by a different

+                                   driver or an application that requires exclusive access.

+                                   Currently not implemented.

+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and

+                                   RemainingDevicePath is not supported by the driver specified by This.

+**/

+EFI_STATUS

+EFIAPI

+Udp6DriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL

+  );

+

+/**

+  Start this driver on ControllerHandle.

+

+  This service is called by the  EFI boot service ConnectController(). In order to make

+  drivers as small as possible, there are a few calling restrictions for

+  this service. ConnectController() must follow these

+  calling restrictions. If any other agent wishes to call Start() it

+  must also follow these calling restrictions.

+

+  @param[in]  This                   Protocol instance pointer.

+  @param[in]  ControllerHandle       Handle of device to bind a driver to.

+  @param[in]  RemainingDevicePath    Optional parameter use to pick a specific child

+                                     device to start.

+

+  @retval EFI_SUCCES             This driver is added to ControllerHandle.

+  @retval EFI_OUT_OF_RESOURCES   The required system resource can't be allocated.

+  @retval other                  This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6DriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL

+  );

+

+/**

+  Stop this driver on ControllerHandle.

+

+  This service is called by the  EFI boot service DisconnectController(). In order to

+  make drivers as small as possible, there are a few calling

+  restrictions for this service. DisconnectController()

+  must follow these calling restrictions. If any other agent wishes

+  to call Stop(), it must also follow these calling restrictions.

+

+  @param[in]  This                   Protocol instance pointer.

+  @param[in]  ControllerHandle       Handle of device to stop the driver on.

+  @param[in]  NumberOfChildren       Number of Handles in ChildHandleBuffer. If number

+                                     of children is zero, stop the entire bus driver.

+  @param[in]  ChildHandleBuffer      List of Child Handles to Stop. It is optional.

+

+  @retval EFI_SUCCESS            This driver removed ControllerHandle.

+  @retval EFI_DEVICE_ERROR       Can't find the NicHandle from the ControllerHandle and specified GUID.

+  @retval other                  This driver was not removed from this device.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6DriverBindingStop (

+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  UINTN                        NumberOfChildren,

+  IN  EFI_HANDLE                   *ChildHandleBuffer OPTIONAL

+  );

+

+/**

+  Creates a child handle and installs a protocol.

+

+  The CreateChild() function installs a protocol on ChildHandle.

+  If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.

+  If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.

+

+  @param[in]       This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.

+  @param[in, out]  ChildHandle Pointer to the handle of the child to create. If it is NULL,

+                               then a new handle is created. If it is a pointer to an existing UEFI handle,

+                               then the protocol is added to the existing UEFI handle.

+

+  @retval EFI_SUCCES            The protocol was added to ChildHandle.

+  @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL.

+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources availabe to create

+                                the child.

+  @retval other                 The child handle was not created.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6ServiceBindingCreateChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN OUT EFI_HANDLE                *ChildHandle

+  );

+

+/**

+  Destroys a child handle with a set of I/O services.

+  The DestroyChild() function does the opposite of CreateChild(). It removes a protocol

+  that was installed by CreateChild() from ChildHandle. If the removed protocol is the

+  last protocol on ChildHandle, then ChildHandle is destroyed.

+

+  @param[in]  This               Protocol instance pointer.

+  @param[in]  ChildHandle        Handle of the child to destroy.

+

+  @retval EFI_SUCCES             The I/O services were removed from the child

+                                 handle.

+  @retval EFI_UNSUPPORTED        The child handle does not support the I/O services

+                                 that are being removed.

+  @retval EFI_INVALID_PARAMETER  Child handle is not a valid EFI Handle.

+  @retval EFI_ACCESS_DENIED      The child handle could not be destroyed because

+                                 its I/O services are being used.

+  @retval other                  The child handle was not destroyed.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6ServiceBindingDestroyChild (

+  IN EFI_SERVICE_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                    ChildHandle

+  );

+

+#endif

+

diff --git a/NetworkPkg/Udp6Dxe/Udp6Dxe.inf b/NetworkPkg/Udp6Dxe/Udp6Dxe.inf
new file mode 100644
index 0000000..30b2bc1
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Dxe.inf
@@ -0,0 +1,63 @@
+## @file Udp6Dxe.inf

+#  Component description file for Udp6 module.

+#

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

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = Udp6Dxe

+  FILE_GUID                      = D912C7BC-F098-4367-92BA-E911083C7B0E

+  MODULE_TYPE                    = UEFI_DRIVER

+  VERSION_STRING                 = 1.0

+

+  ENTRY_POINT                    = Udp6DriverEntryPoint

+  UNLOAD_IMAGE                   = NetLibDefaultUnload

+

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC

+#

+

+[Sources]

+  Udp6Driver.h

+  Udp6Driver.c

+  Udp6Impl.c

+  Udp6Impl.h

+  ComponentName.c

+  Udp6Main.c

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+

+[LibraryClasses]

+  BaseLib

+  BaseMemoryLib

+  MemoryAllocationLib

+  UefiBootServicesTableLib

+  UefiDriverEntryPoint

+  UefiRuntimeServicesTableLib

+  UefiLib

+  DebugLib

+  IpIoLib

+  NetLib

+  DpcLib

+

+

+[Protocols]

+  gEfiIp6ProtocolGuid                           # PROTOCOL ALWAYS_CONSUMED

+  gEfiIp6ServiceBindingProtocolGuid             # PROTOCOL ALWAYS_CONSUMED

+  gEfiUdp6ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED

+  gEfiUdp6ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED

+

diff --git a/NetworkPkg/Udp6Dxe/Udp6Impl.c b/NetworkPkg/Udp6Dxe/Udp6Impl.c
new file mode 100644
index 0000000..8e25931
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Impl.c
@@ -0,0 +1,2131 @@
+/** @file

+  Udp6 driver's whole implementation.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Udp6Impl.h"

+

+UINT16  mUdp6RandomPort;

+

+/**

+  This function checks and timeouts the I/O datagrams holding by the corresponding

+  service context.

+

+  @param[in]  Event              The event this function is registered to.

+  @param[in]  Context            The context data registered during the creation of

+                                 the Event.

+

+**/

+VOID

+EFIAPI

+Udp6CheckTimeout (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  );

+

+/**

+  This function finds the udp instance by the specified <Address, Port> pair.

+

+  @param[in]  InstanceList       Pointer to the head of the list linking the udp

+                                 instances.

+  @param[in]  Address            Pointer to the specified IPv6 address.

+  @param[in]  Port               The udp port number.

+

+  @retval TRUE     The specified <Address, Port> pair is found.

+  @retval FALSE    Otherwise.

+

+**/

+BOOLEAN

+Udp6FindInstanceByPort (

+  IN LIST_ENTRY        *InstanceList,

+  IN EFI_IPv6_ADDRESS  *Address,

+  IN UINT16            Port

+  );

+

+/**

+  This function is the packet transmitting notify function registered to the IpIo

+  interface. It's called to signal the udp TxToken when the IpIo layer completes

+  transmitting of the udp datagram.

+

+  @param[in]  Status            The completion status of the output udp datagram.

+  @param[in]  Context           Pointer to the context data.

+  @param[in]  Sender            Specify a EFI_IP6_PROTOCOL for sending.

+  @param[in]  NotifyData        Pointer to the notify data.

+

+**/

+VOID

+EFIAPI

+Udp6DgramSent (

+  IN EFI_STATUS        Status,

+  IN VOID              *Context,

+  IN IP_IO_IP_PROTOCOL Sender,

+  IN VOID              *NotifyData

+  );

+

+/**

+  This function processes the received datagram passed up by the IpIo layer.

+

+  @param[in]  Status            The status of this udp datagram.

+  @param[in]  IcmpError         The IcmpError code, only available when Status is

+                                EFI_ICMP_ERROR.

+  @param[in]  NetSession        Pointer to the EFI_NET_SESSION_DATA.

+  @param[in]  Packet            Pointer to the NET_BUF containing the received udp

+                                datagram.

+  @param[in]  Context           Pointer to the context data.

+

+**/

+VOID

+EFIAPI

+Udp6DgramRcvd (

+  IN EFI_STATUS            Status,

+  IN UINT8                 IcmpError,

+  IN EFI_NET_SESSION_DATA  *NetSession,

+  IN NET_BUF               *Packet,

+  IN VOID                  *Context

+  );

+

+/**

+  This function cancle the token specified by Arg in the Map.

+

+  @param[in]  Map             Pointer to the NET_MAP.

+  @param[in]  Item            Pointer to the NET_MAP_ITEM.

+  @param[in]  Arg             Pointer to the token to be cancelled, if NULL, all

+                              the tokens in this Map will be cancelled.

+                              This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS         The token is cancelled if Arg is NULL or the token

+                              is not the same as that in the Item if Arg is not

+                              NULL.

+  @retval EFI_ABORTED         Arg is not NULL, and the token specified by Arg is

+                              cancelled.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6CancelTokens (

+  IN NET_MAP       *Map,

+  IN NET_MAP_ITEM  *Item,

+  IN VOID          *Arg OPTIONAL

+  );

+

+/**

+  This function check if the received udp datagram matches with the Instance.

+

+  @param[in]  Instance           Pointer to the udp instance context data.

+  @param[in]  Udp6Session        Pointer to the EFI_UDP6_SESSION_DATA abstracted

+                                 from the received udp datagram.

+

+  @retval TRUE     The udp datagram matches the receiving requirements of the Instance.

+  @retval FALSE    The udp datagram doe not match the receiving requirements of the Instance.

+

+**/

+BOOLEAN

+Udp6MatchDgram (

+  IN UDP6_INSTANCE_DATA     *Instance,

+  IN EFI_UDP6_SESSION_DATA  *Udp6Session

+  );

+

+/**

+  This function removes the Wrap specified by Context and releases relevant resources.

+

+  @param[in]  Event                  The Event this notify function is registered to.

+  @param[in]  Context                Pointer to the context data.

+

+**/

+VOID

+EFIAPI

+Udp6RecycleRxDataWrap (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  );

+

+/**

+  This function wraps the Packet into RxData.

+

+  @param[in]  Instance           Pointer to the instance context data.

+  @param[in]  Packet             Pointer to the buffer containing the received

+                                 datagram.

+  @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this

+                                 datagram.

+

+  @return Pointer to the structure wrapping the RxData and the Packet.

+

+**/

+UDP6_RXDATA_WRAP *

+Udp6WrapRxData (

+  IN UDP6_INSTANCE_DATA     *Instance,

+  IN NET_BUF                *Packet,

+  IN EFI_UDP6_RECEIVE_DATA  *RxData

+  );

+

+/**

+  This function enqueues the received datagram into the instances' receiving queues.

+

+  @param[in]  Udp6Service        Pointer to the udp service context data.

+  @param[in]  Packet             Pointer to the buffer containing the received

+                                 datagram.

+  @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this

+                                 datagram.

+

+  @return The times this datagram is enqueued.

+

+**/

+UINTN

+Udp6EnqueueDgram (

+  IN UDP6_SERVICE_DATA      *Udp6Service,

+  IN NET_BUF                *Packet,

+  IN EFI_UDP6_RECEIVE_DATA  *RxData

+  );

+

+/**

+  This function delivers the datagrams enqueued in the instances.

+

+  @param[in]  Udp6Service            Pointer to the udp service context data.

+

+**/

+VOID

+Udp6DeliverDgram (

+  IN UDP6_SERVICE_DATA  *Udp6Service

+  );

+

+/**

+  This function demultiplexes the received udp datagram to the apropriate instances.

+

+  @param[in]  Udp6Service        Pointer to the udp service context data.

+  @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA abstrated from

+                                 the received datagram.

+  @param[in]  Packet             Pointer to the buffer containing the received udp

+                                 datagram.

+

+**/

+VOID

+Udp6Demultiplex (

+  IN UDP6_SERVICE_DATA     *Udp6Service,

+  IN EFI_NET_SESSION_DATA  *NetSession,

+  IN NET_BUF               *Packet

+  );

+

+/**

+  This function handles the received Icmp Error message and demultiplexes it to the

+  instance.

+

+  @param[in]       Udp6Service        Pointer to the udp service context data.

+  @param[in]       IcmpError          The icmp error code.

+  @param[in]       NetSession         Pointer to the EFI_NET_SESSION_DATA abstracted

+                                      from the received Icmp Error packet.

+  @param[in, out]  Packet             Pointer to the Icmp Error packet.

+

+**/

+VOID

+Udp6IcmpHandler (

+  IN UDP6_SERVICE_DATA     *Udp6Service,

+  IN UINT8                 IcmpError,

+  IN EFI_NET_SESSION_DATA  *NetSession,

+  IN OUT NET_BUF           *Packet

+  );

+

+/**

+  This function builds and sends out a icmp port unreachable message.

+

+  @param[in]  IpIo               Pointer to the IP_IO instance.

+  @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA of the packet

+                                 causes this icmp error message.

+  @param[in]  Udp6Header         Pointer to the udp header of the datagram causes

+                                 this icmp error message.

+

+**/

+VOID

+Udp6SendPortUnreach (

+  IN IP_IO                 *IpIo,

+  IN EFI_NET_SESSION_DATA  *NetSession,

+  IN VOID                  *Udp6Header

+  );

+

+/**

+  Find the key in the netmap

+

+  @param[in]  Map                    The netmap to search within.

+  @param[in]  Key                    The key to search.

+

+  @return The point to the item contains the Key, or NULL if Key isn't in the map.

+

+**/

+NET_MAP_ITEM *

+Udp6MapMultiCastAddr (

+  IN  NET_MAP               *Map,

+  IN  VOID                  *Key

+  );

+

+/**

+  Create the Udp service context data.

+

+  @param[in]  Udp6Service        Pointer to the UDP6_SERVICE_DATA.

+  @param[in]  ImageHandle        The image handle of this udp6 driver.

+  @param[in]  ControllerHandle   The controller handle this udp6 driver binds on.

+

+  @retval EFI_SUCCESS            The udp6 service context data was created and

+                                 initialized.

+  @retval EFI_OUT_OF_RESOURCES   Cannot allocate memory.

+  @retval Others                 An error condition occurred.

+

+**/

+EFI_STATUS

+Udp6CreateService (

+  IN UDP6_SERVICE_DATA  *Udp6Service,

+  IN EFI_HANDLE         ImageHandle,

+  IN EFI_HANDLE         ControllerHandle

+  )

+{

+  EFI_STATUS       Status;

+  IP_IO_OPEN_DATA  OpenData;

+

+  ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA));

+

+  Udp6Service->Signature        = UDP6_SERVICE_DATA_SIGNATURE;

+  Udp6Service->ServiceBinding   = mUdp6ServiceBinding;

+  Udp6Service->ImageHandle      = ImageHandle;

+  Udp6Service->ControllerHandle = ControllerHandle;

+  Udp6Service->ChildrenNumber   = 0;

+

+  InitializeListHead (&Udp6Service->ChildrenList);

+

+  //

+  // Create the IpIo for this service context.

+  //

+  Udp6Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_6);

+  if (Udp6Service->IpIo == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Set the OpenData used to open the IpIo.

+  //

+  CopyMem (

+    &OpenData.IpConfigData.Ip6CfgData,

+    &mIp6IoDefaultIpConfigData,

+    sizeof (EFI_IP6_CONFIG_DATA)

+    );

+  OpenData.RcvdContext           = (VOID *) Udp6Service;

+  OpenData.SndContext            = NULL;

+  OpenData.PktRcvdNotify         = Udp6DgramRcvd;

+  OpenData.PktSentNotify         = Udp6DgramSent;

+

+  //

+  // Configure and start the IpIo.

+  //

+  Status = IpIoOpen (Udp6Service->IpIo, &OpenData);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create the event for Udp timeout checking.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,

+                  TPL_CALLBACK,

+                  Udp6CheckTimeout,

+                  Udp6Service,

+                  &Udp6Service->TimeoutEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Start the timeout timer event.

+  //

+  Status = gBS->SetTimer (

+                  Udp6Service->TimeoutEvent,

+                  TimerPeriodic,

+                  UDP6_TIMEOUT_INTERVAL

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  if (Udp6Service->TimeoutEvent != NULL) {

+    gBS->CloseEvent (Udp6Service->TimeoutEvent);

+  }

+

+  IpIoDestroy (Udp6Service->IpIo);

+

+  return Status;

+}

+

+

+/**

+  Clean the Udp service context data.

+

+  @param[in, out]  Udp6Service      Pointer to the UDP6_SERVICE_DATA.

+

+**/

+VOID

+Udp6CleanService (

+  IN OUT UDP6_SERVICE_DATA  *Udp6Service

+  )

+{

+  //

+  // Close the TimeoutEvent timer.

+  //

+  gBS->CloseEvent (Udp6Service->TimeoutEvent);

+

+  //

+  // Destroy the IpIo.

+  //

+  IpIoDestroy (Udp6Service->IpIo);

+}

+

+

+/**

+  This function checks and times out the I/O datagrams listed in the

+  UDP6_SERVICE_DATA which is specified by the input parameter Context.

+

+

+  @param[in]  Event              The event this function registered to.

+  @param[in]  Context            The context data registered during the creation of

+                                 the Event.

+

+**/

+VOID

+EFIAPI

+Udp6CheckTimeout (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  )

+{

+  UDP6_SERVICE_DATA   *Udp6Service;

+  LIST_ENTRY          *Entry;

+  UDP6_INSTANCE_DATA  *Instance;

+  LIST_ENTRY          *WrapEntry;

+  LIST_ENTRY          *NextEntry;

+  UDP6_RXDATA_WRAP    *Wrap;

+

+  Udp6Service = (UDP6_SERVICE_DATA *) Context;

+  NET_CHECK_SIGNATURE (Udp6Service, UDP6_SERVICE_DATA_SIGNATURE);

+

+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {

+    //

+    // Iterate all the instances belonging to this service context.

+    //

+    Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);

+    NET_CHECK_SIGNATURE (Instance, UDP6_INSTANCE_DATA_SIGNATURE);

+

+    if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {

+      //

+      // Skip this instance if it's not configured or no receive timeout.

+      //

+      continue;

+    }

+

+    NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {

+      //

+      // Iterate all the rxdatas belonging to this udp instance.

+      //

+      Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP6_RXDATA_WRAP, Link);

+

+      if (Wrap->TimeoutTick < UDP6_TIMEOUT_INTERVAL / 10) {

+        //

+        // Remove this RxData if it timeouts.

+        //

+        Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);

+      } else {

+        Wrap->TimeoutTick -= UDP6_TIMEOUT_INTERVAL / 10;

+      }

+    }

+  }

+}

+

+

+/**

+  This function intializes the new created udp instance.

+

+  @param[in]       Udp6Service      Pointer to the UDP6_SERVICE_DATA.

+  @param[in, out]  Instance         Pointer to the un-initialized UDP6_INSTANCE_DATA.

+

+**/

+VOID

+Udp6InitInstance (

+  IN UDP6_SERVICE_DATA       *Udp6Service,

+  IN OUT UDP6_INSTANCE_DATA  *Instance

+  )

+{

+  //

+  // Set the signature.

+  //

+  Instance->Signature = UDP6_INSTANCE_DATA_SIGNATURE;

+

+  //

+  // Init the lists.

+  //

+  InitializeListHead (&Instance->Link);

+  InitializeListHead (&Instance->RcvdDgramQue);

+  InitializeListHead (&Instance->DeliveredDgramQue);

+

+  //

+  // Init the NET_MAPs.

+  //

+  NetMapInit (&Instance->TxTokens);

+  NetMapInit (&Instance->RxTokens);

+  NetMapInit (&Instance->McastIps);

+

+  //

+  // Save the pointer to the UDP6_SERVICE_DATA, and initialize other members.

+  //

+  Instance->Udp6Service = Udp6Service;

+  CopyMem (&Instance->Udp6Proto, &mUdp6Protocol, sizeof (EFI_UDP6_PROTOCOL));

+  Instance->IcmpError   = EFI_SUCCESS;

+  Instance->Configured  = FALSE;

+  Instance->IsNoMapping = FALSE;

+  Instance->Destroyed   = FALSE;

+}

+

+

+/**

+  This function cleans the udp instance.

+

+  @param[in, out]  Instance       Pointer to the UDP6_INSTANCE_DATA to clean.

+

+**/

+VOID

+Udp6CleanInstance (

+  IN OUT UDP6_INSTANCE_DATA  *Instance

+  )

+{

+  NetMapClean (&Instance->McastIps);

+  NetMapClean (&Instance->RxTokens);

+  NetMapClean (&Instance->TxTokens);

+}

+

+

+/**

+  This function finds the udp instance by the specified <Address, Port> pair.

+

+  @param[in]  InstanceList       Pointer to the head of the list linking the udp

+                                 instances.

+  @param[in]  Address            Pointer to the specified IPv6 address.

+  @param[in]  Port               The udp port number.

+

+  @retval TRUE     The specified <Address, Port> pair is found.

+  @retval FALSE    Otherwise.

+

+**/

+BOOLEAN

+Udp6FindInstanceByPort (

+  IN LIST_ENTRY        *InstanceList,

+  IN EFI_IPv6_ADDRESS  *Address,

+  IN UINT16            Port

+  )

+{

+  LIST_ENTRY            *Entry;

+  UDP6_INSTANCE_DATA    *Instance;

+  EFI_UDP6_CONFIG_DATA  *ConfigData;

+

+  NET_LIST_FOR_EACH (Entry, InstanceList) {

+    //

+    // Iterate all the udp instances.

+    //

+    Instance   = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);

+    ConfigData = &Instance->ConfigData;

+

+    if (!Instance->Configured || ConfigData->AcceptAnyPort) {

+      //

+      // If the instance is not configured, or the configdata of the instance indicates

+      // this instance accepts any port, skip it.

+      //

+      continue;

+    }

+

+    if (EFI_IP6_EQUAL (&ConfigData->StationAddress, Address) &&

+        (ConfigData->StationPort == Port)

+        ) {

+      //

+      // If both the address and the port are the same, return TRUE.

+      //

+      return TRUE;

+    }

+  }

+

+  //

+  // Return FALSE when matching fails.

+  //

+  return FALSE;

+}

+

+

+/**

+  This function tries to bind the udp instance according to the configured port

+  allocation stragety.

+

+  @param[in]  InstanceList       Pointer to the head of the list linking the udp

+                                 instances.

+  @param[in]  ConfigData         Pointer to the ConfigData of the instance to be

+                                 bound.

+

+  @retval EFI_SUCCESS            The bound operation completed successfully.

+  @retval EFI_ACCESS_DENIED      The <Address, Port> specified by the ConfigData is

+                                 already used by other instance.

+  @retval EFI_OUT_OF_RESOURCES   No available port resources.

+

+**/

+EFI_STATUS

+Udp6Bind (

+  IN LIST_ENTRY            *InstanceList,

+  IN EFI_UDP6_CONFIG_DATA  *ConfigData

+  )

+{

+  EFI_IPv6_ADDRESS  *StationAddress;

+  UINT16            StartPort;

+

+  if (ConfigData->AcceptAnyPort) {

+    return EFI_SUCCESS;

+  }

+

+  StationAddress = &ConfigData->StationAddress;

+

+  if (ConfigData->StationPort != 0) {

+

+    if (!ConfigData->AllowDuplicatePort &&

+        Udp6FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)

+        ) {

+      //

+      // Do not allow duplicate ports and the port is already used by other instance.

+      //

+      return EFI_ACCESS_DENIED;

+    }

+  } else {

+    //

+    // Select a random port for this instance.

+    //

+    if (ConfigData->AllowDuplicatePort) {

+      //

+      // Just pick up the random port if the instance allows duplicate port.

+      //

+      ConfigData->StationPort = mUdp6RandomPort;

+    } else {

+

+      StartPort = mUdp6RandomPort;

+

+      while (Udp6FindInstanceByPort (InstanceList, StationAddress, mUdp6RandomPort)) {

+

+        mUdp6RandomPort++;

+        if (mUdp6RandomPort == 0) {

+          mUdp6RandomPort = UDP6_PORT_KNOWN;

+        }

+

+        if (mUdp6RandomPort == StartPort) {

+          //

+          // No available port.

+          //

+          return EFI_OUT_OF_RESOURCES;

+        }

+      }

+

+      ConfigData->StationPort = mUdp6RandomPort;

+    }

+

+    mUdp6RandomPort++;

+    if (mUdp6RandomPort == 0) {

+      mUdp6RandomPort = UDP6_PORT_KNOWN;

+    }

+  }

+  return EFI_SUCCESS;

+}

+

+

+/**

+  This function is used to check whether the NewConfigData has any un-reconfigurable

+  parameters changed compared to the OldConfigData.

+

+  @param[in]  OldConfigData    Pointer to the current ConfigData the udp instance

+                               uses.

+  @param[in]  NewConfigData    Pointer to the new ConfigData.

+

+  @retval TRUE     The instance is reconfigurable according to the NewConfigData.

+  @retval FALSE    Otherwise.

+

+**/

+BOOLEAN

+Udp6IsReconfigurable (

+  IN EFI_UDP6_CONFIG_DATA  *OldConfigData,

+  IN EFI_UDP6_CONFIG_DATA  *NewConfigData

+  )

+{

+  if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) ||

+      (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) ||

+      (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort)

+      ) {

+    //

+    // The receiving filter parameters cannot be changed.

+    //

+    return FALSE;

+  }

+

+  if ((!NewConfigData->AcceptAnyPort) &&

+      (NewConfigData->StationPort != OldConfigData->StationPort)

+      ) {

+    //

+    // The port is not changeable.

+    //

+    return FALSE;

+  }

+

+  if (!EFI_IP6_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress)) {

+    //

+    //  The StationAddress is not the same.

+    //

+    return FALSE;

+  }

+

+

+  if (!EFI_IP6_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) {

+    //

+    // The remoteaddress is not the same.

+    //

+    return FALSE;

+  }

+

+  if (!NetIp6IsUnspecifiedAddr (&NewConfigData->RemoteAddress) &&

+      (NewConfigData->RemotePort != OldConfigData->RemotePort)

+      ) {

+    //

+    // The RemotePort differs if it's designated in the configdata.

+    //

+    return FALSE;

+  }

+

+  //

+  // All checks pass, return TRUE.

+  //

+  return TRUE;

+}

+

+

+/**

+  This function builds the Ip6 configdata from the Udp6ConfigData.

+

+  @param[in]       Udp6ConfigData         Pointer to the EFI_UDP6_CONFIG_DATA.

+  @param[in, out]  Ip6ConfigData          Pointer to the EFI_IP6_CONFIG_DATA.

+

+**/

+VOID

+Udp6BuildIp6ConfigData (

+  IN EFI_UDP6_CONFIG_DATA      *Udp6ConfigData,

+  IN OUT EFI_IP6_CONFIG_DATA   *Ip6ConfigData

+  )

+{

+  CopyMem (

+    Ip6ConfigData,

+    &mIp6IoDefaultIpConfigData,

+    sizeof (EFI_IP6_CONFIG_DATA)

+    );

+  Ip6ConfigData->DefaultProtocol      = EFI_IP_PROTO_UDP;

+  Ip6ConfigData->AcceptPromiscuous    = Udp6ConfigData->AcceptPromiscuous;

+  IP6_COPY_ADDRESS (&Ip6ConfigData->StationAddress, &Udp6ConfigData->StationAddress);

+  IP6_COPY_ADDRESS (&Ip6ConfigData->DestinationAddress, &Udp6ConfigData->RemoteAddress);

+  //

+  // Use the -1 magic number to disable the receiving process of the ip instance.

+  //

+  Ip6ConfigData->ReceiveTimeout    = (UINT32) (-1);

+}

+

+

+/**

+  This function validates the TxToken. It returns the error code according to the spec.

+

+  @param[in]  Instance           Pointer to the udp instance context data.

+  @param[in]  TxToken            Pointer to the token to be checked.

+

+  @retval EFI_SUCCESS            The TxToken is valid.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 Token.Event is NULL;

+                                 Token.Packet.TxData is NULL;

+                                 Token.Packet.TxData.FragmentCount is zero;

+                                 Token.Packet.TxData.DataLength is not equal to the

+                                 sum of fragment lengths;

+                                 One or more of the

+                                 Token.Packet.TxData.FragmentTable[].FragmentLength

+                                 fields is zero;

+                                 One or more of the

+                                 Token.Packet.TxData.FragmentTable[].FragmentBuffer

+                                 fields is NULL;

+                                 UdpSessionData.DestinationAddress are not valid

+                                 unicast IPv6 addresses if the UdpSessionData is

+                                 not NULL;

+                                 UdpSessionData.DestinationPort and

+                                 ConfigData.RemotePort are all zero if the

+                                 UdpSessionData is not NULL.

+  @retval EFI_BAD_BUFFER_SIZE    The data length is greater than the maximum UDP

+                                 packet size.

+

+**/

+EFI_STATUS

+Udp6ValidateTxToken (

+  IN UDP6_INSTANCE_DATA         *Instance,

+  IN EFI_UDP6_COMPLETION_TOKEN  *TxToken

+  )

+{

+  EFI_UDP6_TRANSMIT_DATA  *TxData;

+  UINT32                  Index;

+  UINT32                  TotalLen;

+  EFI_UDP6_CONFIG_DATA    *ConfigData;

+  EFI_UDP6_SESSION_DATA   *UdpSessionData;

+

+

+  if (TxToken->Event == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  TxData = TxToken->Packet.TxData;

+

+  if ((TxData == NULL) || (TxData->FragmentCount == 0)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  TotalLen = 0;

+  for (Index = 0; Index < TxData->FragmentCount; Index++) {

+

+    if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||

+        (TxData->FragmentTable[Index].FragmentLength == 0)

+        ) {

+      //

+      // If the FragmentBuffer is NULL, or the FragmentLeng is zero.

+      //

+      return EFI_INVALID_PARAMETER;

+    }

+

+    TotalLen += TxData->FragmentTable[Index].FragmentLength;

+  }

+

+  if (TotalLen != TxData->DataLength) {

+    //

+    // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the

+    // DataLength.

+    //

+    return EFI_INVALID_PARAMETER;

+  }

+

+  ConfigData     = &Instance->ConfigData;

+  UdpSessionData = TxData->UdpSessionData;

+

+  if (UdpSessionData != NULL) {

+

+    if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) {

+      //

+      // Ambiguous; no avalaible DestinationPort for this token.

+      //

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&

+        NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)

+        ) {

+      //

+      // The DestinationAddress is not specificed.

+      //

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&

+        !NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)

+        ) {

+      //

+      // The ConfigData.RemoteAddress is not zero and the UdpSessionData.DestinationAddress

+      // is not zero too.

+      //

+      return EFI_INVALID_PARAMETER;

+    }

+  } else if (NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)) {

+    //

+    // The configured RemoteAddress is all zero, and the user doesn't override the

+    // destination address.

+    //

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (TxData->DataLength > UDP6_MAX_DATA_SIZE) {

+    return EFI_BAD_BUFFER_SIZE;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  This function checks whether the specified Token duplicates the one in the Map.

+

+  @param[in]  Map                Pointer to the NET_MAP.

+  @param[in]  Item               Pointer to the NET_MAP_ITEM contain the pointer to

+                                 the Token.

+  @param[in]  Context            Pointer to the Token to be checked.

+

+  @retval EFI_SUCCESS            The Token specified by Context differs from the

+                                 one in the Item.

+  @retval EFI_ACCESS_DENIED      The Token duplicates with the one in the Item.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6TokenExist (

+  IN NET_MAP       *Map,

+  IN NET_MAP_ITEM  *Item,

+  IN VOID          *Context

+  )

+{

+  EFI_UDP6_COMPLETION_TOKEN  *Token;

+  EFI_UDP6_COMPLETION_TOKEN  *TokenInItem;

+

+  Token       = (EFI_UDP6_COMPLETION_TOKEN *) Context;

+  TokenInItem = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;

+

+  if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {

+    //

+    // The Token duplicates with the TokenInItem in case either the two pointers are the

+    // same, or the Events of these two tokens are the same.

+    //

+    return EFI_ACCESS_DENIED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  This function calculates the checksum for the Packet, utilizing the pre-calculated

+  pseudo HeadSum to reduce some overhead.

+

+  @param[in]  Packet           Pointer to the NET_BUF contains the udp datagram.

+  @param[in]  HeadSum          Checksum of the pseudo header, execpt the length

+                               field.

+

+  @return The 16-bit checksum of this udp datagram.

+

+**/

+UINT16

+Udp6Checksum (

+  IN NET_BUF *Packet,

+  IN UINT16  HeadSum

+  )

+{

+  UINT16  Checksum;

+

+  Checksum  = NetbufChecksum (Packet);

+  Checksum  = NetAddChecksum (Checksum, HeadSum);

+

+  Checksum  = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize));

+  Checksum  = (UINT16) (~Checksum);

+  return Checksum;

+}

+

+

+/**

+  This function removes the specified Token from the TokenMap.

+

+  @param[in]  TokenMap           Pointer to the NET_MAP containing the tokens.

+  @param[in]  Token              Pointer to the Token to be removed.

+

+  @retval EFI_SUCCESS            The specified Token is removed from the TokenMap.

+  @retval EFI_NOT_FOUND          The specified Token is not found in the TokenMap.

+

+**/

+EFI_STATUS

+Udp6RemoveToken (

+  IN NET_MAP                    *TokenMap,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token

+  )

+{

+  NET_MAP_ITEM  *Item;

+

+  //

+  // Find the Token first.

+  //

+  Item = NetMapFindKey (TokenMap, (VOID *) Token);

+

+  if (Item != NULL) {

+    //

+    // Remove the token if it's found in the map.

+    //

+    NetMapRemoveItem (TokenMap, Item, NULL);

+

+    return EFI_SUCCESS;

+  }

+  return EFI_NOT_FOUND;

+}

+

+

+/**

+  This function is the packet transmitting notify function registered to the IpIo

+  interface. It's called to signal the udp TxToken when IpIo layer completes the

+  transmitting of the udp datagram.

+

+  @param[in]  Status            The completion status of the output udp datagram.

+  @param[in]  Context           Pointer to the context data.

+  @param[in]  Sender            Specify a EFI_IP6_PROTOCOL for sending.

+  @param[in]  NotifyData        Pointer to the notify data.

+

+**/

+VOID

+EFIAPI

+Udp6DgramSent (

+  IN EFI_STATUS        Status,

+  IN VOID              *Context,

+  IN IP_IO_IP_PROTOCOL Sender,

+  IN VOID              *NotifyData

+  )

+{

+  UDP6_INSTANCE_DATA         *Instance;

+  EFI_UDP6_COMPLETION_TOKEN  *Token;

+

+  Instance = (UDP6_INSTANCE_DATA *) Context;

+  Token    = (EFI_UDP6_COMPLETION_TOKEN *) NotifyData;

+

+  if (Udp6RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) {

+    //

+    // The token may be cancelled. Only signal it if the remove operation succeeds.

+    //

+    Token->Status = Status;

+    gBS->SignalEvent (Token->Event);

+    DispatchDpc ();

+  }

+}

+

+

+/**

+  This function processes the received datagram passed up by the IpIo layer.

+

+  @param[in]  Status            The status of this udp datagram.

+  @param[in]  IcmpError         The IcmpError code, only available when Status is

+                                EFI_ICMP_ERROR.

+  @param[in]  NetSession        Pointer to the EFI_NET_SESSION_DATA.

+  @param[in]  Packet            Pointer to the NET_BUF containing the received udp

+                                datagram.

+  @param[in]  Context           Pointer to the context data.

+

+**/

+VOID

+EFIAPI

+Udp6DgramRcvd (

+  IN EFI_STATUS            Status,

+  IN UINT8                 IcmpError,

+  IN EFI_NET_SESSION_DATA  *NetSession,

+  IN NET_BUF               *Packet,

+  IN VOID                  *Context

+  )

+{

+  NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);

+

+  //

+  // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR.

+  //

+  if (Status == EFI_SUCCESS) {

+

+    //

+    // Demultiplex the received datagram.

+    //

+    Udp6Demultiplex ((UDP6_SERVICE_DATA *) Context, NetSession, Packet);

+  } else {

+    //

+    // Handle the ICMP6 Error packet.

+    //

+    Udp6IcmpHandler ((UDP6_SERVICE_DATA *) Context, IcmpError, NetSession, Packet);

+  }

+

+  //

+  // Dispatch the DPC queued by the NotifyFunction of the rx token's events

+  // that are signaled with received data.

+  //

+  DispatchDpc ();

+}

+

+

+/**

+  This function removes the multicast group specified by Arg from the Map.

+

+  @param[in]  Map                Pointer to the NET_MAP.

+  @param[in]  Item               Pointer to the NET_MAP_ITEM.

+  @param[in]  Arg                Pointer to the Arg, it's the pointer to a

+                                 multicast IPv6 Address. This parameter is

+                                 optional and may be NULL.

+

+  @retval EFI_SUCCESS            The multicast address is removed.

+  @retval EFI_ABORTED            The specified multicast address is removed, and the

+                                 Arg is not NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6LeaveGroup (

+  IN NET_MAP       *Map,

+  IN NET_MAP_ITEM  *Item,

+  IN VOID          *Arg OPTIONAL

+  )

+{

+  EFI_IPv6_ADDRESS  *McastIp;

+

+  McastIp = Arg;

+

+  if ((McastIp != NULL) &&

+      !EFI_IP6_EQUAL (McastIp, ((EFI_IPv6_ADDRESS *)Item->Key))

+      ) {

+    //

+    // McastIp is not NULL and the multicast address contained in the Item

+    // is not the same as McastIp.

+    //

+    return EFI_SUCCESS;

+  }

+

+  FreePool (Item->Key);

+

+  //

+  // Remove this Item.

+  //

+  NetMapRemoveItem (Map, Item, NULL);

+

+  if (McastIp != NULL) {

+    //

+    // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration.

+    //

+    return EFI_ABORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  This function cancle the token specified by Arg in the Map.

+

+  @param[in]  Map             Pointer to the NET_MAP.

+  @param[in]  Item            Pointer to the NET_MAP_ITEM.

+  @param[in]  Arg             Pointer to the token to be cancelled. If NULL, all

+                              the tokens in this Map will be cancelled.

+                              This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS         The token is cancelled if Arg is NULL, or the token

+                              is not the same as that in the Item, if Arg is not

+                              NULL.

+  @retval EFI_ABORTED         Arg is not NULL, and the token specified by Arg is

+                              cancelled.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6CancelTokens (

+  IN NET_MAP       *Map,

+  IN NET_MAP_ITEM  *Item,

+  IN VOID          *Arg OPTIONAL

+  )

+{

+  EFI_UDP6_COMPLETION_TOKEN  *TokenToCancel;

+  NET_BUF                    *Packet;

+  IP_IO                      *IpIo;

+

+  if ((Arg != NULL) && (Item->Key != Arg)) {

+    return EFI_SUCCESS;

+  }

+

+  if (Item->Value != NULL) {

+    //

+    // If the token is a transmit token, the corresponding Packet is recorded in

+    // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken

+    // will invoke Udp6DgramSent, the token will be signaled and this Item will

+    // be removed from the Map there.

+    //

+    Packet  = (NET_BUF *) (Item->Value);

+    IpIo    = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0]));

+

+    IpIoCancelTxToken (IpIo, Packet);

+  } else {

+    //

+    // The token is a receive token. Abort it and remove it from the Map.

+    //

+    TokenToCancel = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;

+    NetMapRemoveItem (Map, Item, NULL);

+

+    TokenToCancel->Status = EFI_ABORTED;

+    gBS->SignalEvent (TokenToCancel->Event);

+  }

+

+  if (Arg != NULL) {

+    return EFI_ABORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  This function removes all the Wrap datas in the RcvdDgramQue.

+

+  @param[in]  Instance    Pointer to the Udp6 Instance.

+

+**/

+VOID

+Udp6FlushRcvdDgram (

+  IN UDP6_INSTANCE_DATA  *Instance

+  )

+{

+  UDP6_RXDATA_WRAP  *Wrap;

+

+  while (!IsListEmpty (&Instance->RcvdDgramQue)) {

+    //

+    // Iterate all the Wraps in the RcvdDgramQue.

+    //

+    Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);

+

+    //

+    // The Wrap will be removed from the RcvdDgramQue by this function call.

+    //

+    Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);

+  }

+}

+

+

+

+/**

+  Cancel Udp6 tokens from the Udp6 instance.

+

+  @param[in]  Instance           Pointer to the udp instance context data.

+  @param[in]  Token              Pointer to the token to be canceled. If NULL, all

+                                 tokens in this instance will be cancelled.

+                                 This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS            The Token is cancelled.

+  @retval EFI_NOT_FOUND          The Token is not found.

+

+**/

+EFI_STATUS

+Udp6InstanceCancelToken (

+  IN UDP6_INSTANCE_DATA         *Instance,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL

+  )

+{

+  EFI_STATUS  Status;

+

+  //

+  // Cancel this token from the TxTokens map.

+  //

+  Status = NetMapIterate (&Instance->TxTokens, Udp6CancelTokens, Token);

+

+  if ((Token != NULL) && (Status == EFI_ABORTED)) {

+    //

+    // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from

+    // the TxTokens and returns success.

+    //

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Try to cancel this token from the RxTokens map in condition either the Token

+  // is NULL or the specified Token is not in TxTokens.

+  //

+  Status = NetMapIterate (&Instance->RxTokens, Udp6CancelTokens, Token);

+

+  if ((Token != NULL) && (Status == EFI_SUCCESS)) {

+    //

+    // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the

+    // TxTokens nor the RxTokens, or say, it's not found.

+    //

+    return EFI_NOT_FOUND;

+  }

+

+  ASSERT ((Token != NULL) ||

+          ((0 == NetMapGetCount (&Instance->TxTokens)) &&

+          (0 == NetMapGetCount (&Instance->RxTokens)))

+          );

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  This function checks if the received udp datagram matches with the Instance.

+

+  @param[in]  Instance           Pointer to the udp instance context data.

+  @param[in]  Udp6Session        Pointer to the EFI_UDP6_SESSION_DATA abstracted

+                                 from the received udp datagram.

+

+  @retval TRUE     The udp datagram matches the receiving requirements of the Instance.

+  @retval FALSE    The udp datagram does not matche the receiving requirements of the Instance.

+

+**/

+BOOLEAN

+Udp6MatchDgram (

+  IN UDP6_INSTANCE_DATA     *Instance,

+  IN EFI_UDP6_SESSION_DATA  *Udp6Session

+  )

+{

+  EFI_UDP6_CONFIG_DATA  *ConfigData;

+  EFI_IPv6_ADDRESS      Destination;

+

+  ConfigData = &Instance->ConfigData;

+

+  if (ConfigData->AcceptPromiscuous) {

+    //

+    // Always matches if this instance is in the promiscuous state.

+    //

+    return TRUE;

+  }

+

+  if ((!ConfigData->AcceptAnyPort && (Udp6Session->DestinationPort != ConfigData->StationPort)) ||

+      ((ConfigData->RemotePort != 0) && (Udp6Session->SourcePort != ConfigData->RemotePort))

+      ) {

+    //

+    // The local port or the remote port doesn't match.

+    //

+    return FALSE;

+  }

+

+  if (!NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) &&

+      !EFI_IP6_EQUAL (&ConfigData->RemoteAddress, &Udp6Session->SourceAddress)

+      ) {

+    //

+    // This datagram doesn't come from the instance's specified sender.

+    //

+    return FALSE;

+  }

+

+  if (NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress) ||

+      EFI_IP6_EQUAL (&Udp6Session->DestinationAddress, &ConfigData->StationAddress)

+      ) {

+    //

+    // The instance is configured to receive datagrams destinated to any station IP or

+    // the destination address of this datagram matches the configured station IP.

+    //

+    return TRUE;

+  }

+

+  IP6_COPY_ADDRESS (&Destination, &Udp6Session->DestinationAddress);

+

+  if (IP6_IS_MULTICAST (&Destination) &&

+      (NULL != Udp6MapMultiCastAddr (&Instance->McastIps, &Destination))

+      ) {

+    //

+    // It's a multicast packet and the multicast address is accepted by this instance.

+    //

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+

+/**

+  This function removes the Wrap specified by Context and release relevant resources.

+

+  @param[in]  Event                  The Event this notify function registered to.

+  @param[in]  Context                Pointer to the context data.

+

+**/

+VOID

+EFIAPI

+Udp6RecycleRxDataWrap (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  )

+{

+  UDP6_RXDATA_WRAP  *Wrap;

+

+  Wrap = (UDP6_RXDATA_WRAP *) Context;

+

+  //

+  // Remove the Wrap from the list it belongs to.

+  //

+  RemoveEntryList (&Wrap->Link);

+

+  //

+  // Free the Packet associated with this Wrap.

+  //

+  NetbufFree (Wrap->Packet);

+

+  //

+  // Close the event.

+  //

+  gBS->CloseEvent (Wrap->RxData.RecycleSignal);

+

+  FreePool (Wrap);

+}

+

+

+/**

+  This function wraps the Packet into RxData.

+

+  @param[in]  Instance           Pointer to the instance context data.

+  @param[in]  Packet             Pointer to the buffer containing the received

+                                 datagram.

+  @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this

+                                 datagram.

+

+  @return Pointer to the structure wrapping the RxData and the Packet.

+

+**/

+UDP6_RXDATA_WRAP *

+Udp6WrapRxData (

+  IN UDP6_INSTANCE_DATA     *Instance,

+  IN NET_BUF                *Packet,

+  IN EFI_UDP6_RECEIVE_DATA  *RxData

+  )

+{

+  EFI_STATUS            Status;

+  UDP6_RXDATA_WRAP      *Wrap;

+

+  //

+  // Allocate buffer for the Wrap.

+  //

+  Wrap = AllocateZeroPool (sizeof (UDP6_RXDATA_WRAP) +

+         (Packet->BlockOpNum - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA));

+  if (Wrap == NULL) {

+    return NULL;

+  }

+

+  InitializeListHead (&Wrap->Link);

+

+  CopyMem (&Wrap->RxData, RxData, sizeof(EFI_UDP6_RECEIVE_DATA));

+  //

+  // Create the Recycle event.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  Udp6RecycleRxDataWrap,

+                  Wrap,

+                  &Wrap->RxData.RecycleSignal

+                  );

+  if (EFI_ERROR (Status)) {

+    FreePool (Wrap);

+    return NULL;

+  }

+

+  Wrap->Packet      = Packet;

+  Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;

+

+  return Wrap;

+}

+

+

+/**

+  This function enqueues the received datagram into the instances' receiving queues.

+

+  @param[in]  Udp6Service        Pointer to the udp service context data.

+  @param[in]  Packet             Pointer to the buffer containing the received

+                                 datagram.

+  @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this

+                                 datagram.

+

+  @return The times this datagram is enqueued.

+

+**/

+UINTN

+Udp6EnqueueDgram (

+  IN UDP6_SERVICE_DATA      *Udp6Service,

+  IN NET_BUF                *Packet,

+  IN EFI_UDP6_RECEIVE_DATA  *RxData

+  )

+{

+  LIST_ENTRY          *Entry;

+  UDP6_INSTANCE_DATA  *Instance;

+  UDP6_RXDATA_WRAP    *Wrap;

+  UINTN               Enqueued;

+

+  Enqueued = 0;

+

+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {

+    //

+    // Iterate the instances.

+    //

+    Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);

+

+    if (!Instance->Configured) {

+      continue;

+    }

+

+    if (Udp6MatchDgram (Instance, &RxData->UdpSession)) {

+      //

+      // Wrap the RxData and put this Wrap into the instances RcvdDgramQue.

+      //

+      Wrap = Udp6WrapRxData (Instance, Packet, RxData);

+      if (Wrap == NULL) {

+        continue;

+      }

+

+      NET_GET_REF (Packet);

+

+      InsertTailList (&Instance->RcvdDgramQue, &Wrap->Link);

+

+      Enqueued++;

+    }

+  }

+

+  return Enqueued;

+}

+

+

+/**

+  This function delivers the received datagrams to the specified instance.

+

+  @param[in]  Instance               Pointer to the instance context data.

+

+**/

+VOID

+Udp6InstanceDeliverDgram (

+  IN UDP6_INSTANCE_DATA  *Instance

+  )

+{

+  UDP6_RXDATA_WRAP           *Wrap;

+  EFI_UDP6_COMPLETION_TOKEN  *Token;

+  NET_BUF                    *Dup;

+  EFI_UDP6_RECEIVE_DATA      *RxData;

+  EFI_TPL                    OldTpl;

+

+  if (!IsListEmpty (&Instance->RcvdDgramQue) &&

+      !NetMapIsEmpty (&Instance->RxTokens)

+      ) {

+

+    Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);

+

+    if (NET_BUF_SHARED (Wrap->Packet)) {

+      //

+      // Duplicate the Packet if it is shared between instances.

+      //

+      Dup = NetbufDuplicate (Wrap->Packet, NULL, 0);

+      if (Dup == NULL) {

+        return;

+      }

+

+      NetbufFree (Wrap->Packet);

+

+      Wrap->Packet = Dup;

+    }

+

+    NetListRemoveHead (&Instance->RcvdDgramQue);

+

+    Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);

+

+    //

+    // Build the FragmentTable and set the FragmentCount in RxData.

+    //

+    RxData                = &Wrap->RxData;

+    RxData->FragmentCount = Wrap->Packet->BlockOpNum;

+

+    NetbufBuildExt (

+      Wrap->Packet,

+      (NET_FRAGMENT *) RxData->FragmentTable,

+      &RxData->FragmentCount

+      );

+

+    Token->Status        = EFI_SUCCESS;

+    Token->Packet.RxData = &Wrap->RxData;

+

+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);

+    InsertTailList (&Instance->DeliveredDgramQue, &Wrap->Link);

+    gBS->RestoreTPL (OldTpl);

+

+    gBS->SignalEvent (Token->Event);

+  }

+}

+

+

+/**

+  This function delivers the datagrams enqueued in the instances.

+

+  @param[in]  Udp6Service            Pointer to the udp service context data.

+

+**/

+VOID

+Udp6DeliverDgram (

+  IN UDP6_SERVICE_DATA  *Udp6Service

+  )

+{

+  LIST_ENTRY          *Entry;

+  UDP6_INSTANCE_DATA  *Instance;

+

+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {

+    //

+    // Iterate the instances.

+    //

+    Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);

+

+    if (!Instance->Configured) {

+      continue;

+    }

+

+    //

+    // Deliver the datagrams of this instance.

+    //

+    Udp6InstanceDeliverDgram (Instance);

+  }

+}

+

+

+/**

+  This function demultiplexes the received udp datagram to the appropriate instances.

+

+  @param[in]  Udp6Service        Pointer to the udp service context data.

+  @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA abstrated from

+                                 the received datagram.

+  @param[in]  Packet             Pointer to the buffer containing the received udp

+                                 datagram.

+

+**/

+VOID

+Udp6Demultiplex (

+  IN UDP6_SERVICE_DATA     *Udp6Service,

+  IN EFI_NET_SESSION_DATA  *NetSession,

+  IN NET_BUF               *Packet

+  )

+{

+  EFI_UDP_HEADER        *Udp6Header;

+  UINT16                 HeadSum;

+  EFI_UDP6_RECEIVE_DATA  RxData;

+  EFI_UDP6_SESSION_DATA  *Udp6Session;

+  UINTN                  Enqueued;

+

+  //

+  // Get the datagram header from the packet buffer.

+  //

+  Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);

+  ASSERT (Udp6Header != NULL);

+

+  if (Udp6Header->Checksum != 0) {

+    //

+    // check the checksum.

+    //

+    HeadSum = NetIp6PseudoHeadChecksum (

+                &NetSession->Source.v6,

+                &NetSession->Dest.v6,

+                EFI_IP_PROTO_UDP,

+                0

+                );

+

+    if (Udp6Checksum (Packet, HeadSum) != 0) {

+      //

+      // Wrong checksum.

+      //

+      return;

+    }

+  }

+

+  gRT->GetTime (&RxData.TimeStamp, NULL);

+

+  Udp6Session                  = &RxData.UdpSession;

+  Udp6Session->SourcePort      = NTOHS (Udp6Header->SrcPort);

+  Udp6Session->DestinationPort = NTOHS (Udp6Header->DstPort);

+

+  IP6_COPY_ADDRESS (&Udp6Session->SourceAddress, &NetSession->Source);

+  IP6_COPY_ADDRESS (&Udp6Session->DestinationAddress, &NetSession->Dest);

+

+  //

+  // Trim the UDP header.

+  //

+  NetbufTrim (Packet, UDP6_HEADER_SIZE, TRUE);

+

+  RxData.DataLength = (UINT32) Packet->TotalSize;

+

+  //

+  // Try to enqueue this datagram into the instances.

+  //

+  Enqueued = Udp6EnqueueDgram (Udp6Service, Packet, &RxData);

+

+  if (Enqueued == 0) {

+    //

+    // Send the port unreachable ICMP packet before we free this NET_BUF

+    //

+    Udp6SendPortUnreach (Udp6Service->IpIo, NetSession, Udp6Header);

+  }

+

+  //

+  // Try to free the packet before deliver it.

+  //

+  NetbufFree (Packet);

+

+  if (Enqueued > 0) {

+    //

+    // Deliver the datagram.

+    //

+    Udp6DeliverDgram (Udp6Service);

+  }

+}

+

+

+/**

+  This function builds and sends out a icmp port unreachable message.

+

+  @param[in]  IpIo               Pointer to the IP_IO instance.

+  @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA of the packet

+                                 causes this icmp error message.

+  @param[in]  Udp6Header         Pointer to the udp header of the datagram causes

+                                 this icmp error message.

+

+**/

+VOID

+Udp6SendPortUnreach (

+  IN IP_IO                 *IpIo,

+  IN EFI_NET_SESSION_DATA  *NetSession,

+  IN VOID                  *Udp6Header

+  )

+{

+  NET_BUF              *Packet;

+  UINT32               Len;

+  IP6_ICMP_ERROR_HEAD  *IcmpErrHdr;

+  UINT8                *Ptr;

+  IP_IO_OVERRIDE       Override;

+  IP_IO_IP_INFO        *IpSender;

+  EFI_IP6_MODE_DATA    *Ip6ModeData;

+  EFI_STATUS           Status;

+  EFI_IP6_PROTOCOL     *Ip6Protocol;

+

+  Ip6ModeData = NULL;

+

+  //

+  // An ICMPv6 error message MUST NOT be originated as A packet destined to

+  // 1) an IPv6 multicast address 2) The IPv6 Unspecified Address

+  //

+  if (NetSession->IpVersion == IP_VERSION_6) {

+    if (NetIp6IsUnspecifiedAddr (&NetSession->Dest.v6) ||

+      IP6_IS_MULTICAST (&NetSession->Dest.v6)

+      ) {

+      goto EXIT;

+    }

+  }

+

+

+  IpSender    = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest);

+

+  //

+  // Get the Ipv6 Mode Data.

+  //

+  Ip6ModeData = AllocateZeroPool (sizeof (EFI_IP6_MODE_DATA));

+  ASSERT (Ip6ModeData != NULL);

+

+  //

+  // If not finding the related IpSender use the default IpIo to send out

+  // the port unreachable ICMP message.

+  //

+  if (IpSender == NULL) {

+    Ip6Protocol = IpIo->Ip.Ip6;

+  } else {

+    Ip6Protocol = IpSender->Ip.Ip6;

+  }

+

+  Status = Ip6Protocol->GetModeData (

+                          Ip6Protocol,

+                          Ip6ModeData,

+                          NULL,

+                          NULL

+                          );

+

+  if (EFI_ERROR (Status)) {

+    goto EXIT;

+  }

+  //

+  // The ICMP6 packet length, includes whole invoking packet and ICMP6 error header.

+  //

+  Len = NetSession->IpHdrLen +

+        NTOHS(((EFI_UDP_HEADER *) Udp6Header)->Length) +

+        sizeof (IP6_ICMP_ERROR_HEAD);

+

+  //

+  // If the ICMP6 packet length larger than IP MTU, adjust its length to MTU.

+  //

+  if (Ip6ModeData->MaxPacketSize < Len) {

+    Len = Ip6ModeData->MaxPacketSize;

+  }

+

+  //

+  // Allocate buffer for the icmp error message.

+  //

+  Packet = NetbufAlloc (Len);

+  if (Packet == NULL) {

+    goto EXIT;

+  }

+

+  //

+  // Allocate space for the IP6_ICMP_ERROR_HEAD.

+  //

+  IcmpErrHdr = (IP6_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE);

+  ASSERT (IcmpErrHdr != NULL);

+

+  //

+  // Set the required fields for the icmp port unreachable message.

+  //

+  IcmpErrHdr->Head.Type     = ICMP_V6_DEST_UNREACHABLE;

+  IcmpErrHdr->Head.Code     = ICMP_V6_PORT_UNREACHABLE;

+  IcmpErrHdr->Head.Checksum = 0;

+  IcmpErrHdr->Fourth        = 0;

+

+  //

+  // Copy as much of invoking Packet as possible without the ICMPv6 packet

+  // exceeding the minimum Ipv6 MTU. The length of IP6_ICMP_ERROR_HEAD contains

+  // the length of EFI_IP6_HEADER, so when using the length of IP6_ICMP_ERROR_HEAD

+  // for pointer movement that fact should be considered.

+  //

+  Ptr = (VOID *) &IcmpErrHdr->Head;

+  Ptr = (UINT8 *) (UINTN) ((UINTN) Ptr + sizeof (IP6_ICMP_ERROR_HEAD) - sizeof (EFI_IP6_HEADER));

+  CopyMem (Ptr, NetSession->IpHdr.Ip6Hdr, NetSession->IpHdrLen);

+  CopyMem (

+    Ptr + NetSession->IpHdrLen,

+    Udp6Header,

+    Len - NetSession->IpHdrLen - sizeof (IP6_ICMP_ERROR_HEAD) + sizeof (EFI_IP6_HEADER)

+    );

+

+  //

+  // Set the checksum as zero, and IP6 driver will calcuate it with pseudo header.

+  //

+  IcmpErrHdr->Head.Checksum = 0;

+

+  //

+  // Fill the override data.

+  //

+  Override.Ip6OverrideData.FlowLabel     = 0;

+  Override.Ip6OverrideData.HopLimit      = 255;

+  Override.Ip6OverrideData.Protocol      = IP6_ICMP;

+

+  //

+  // Send out this icmp packet.

+  //

+  IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override);

+

+  NetbufFree (Packet);

+

+EXIT:

+  if (Ip6ModeData != NULL) {

+    FreePool (Ip6ModeData);

+  }

+}

+

+

+/**

+  This function handles the received Icmp Error message and de-multiplexes it to the

+  instance.

+

+  @param[in]       Udp6Service        Pointer to the udp service context data.

+  @param[in]       IcmpError          The icmp error code.

+  @param[in]       NetSession         Pointer to the EFI_NET_SESSION_DATA abstracted

+                                      from the received Icmp Error packet.

+  @param[in, out]  Packet             Pointer to the Icmp Error packet.

+

+**/

+VOID

+Udp6IcmpHandler (

+  IN UDP6_SERVICE_DATA     *Udp6Service,

+  IN UINT8                 IcmpError,

+  IN EFI_NET_SESSION_DATA  *NetSession,

+  IN OUT NET_BUF           *Packet

+  )

+{

+  EFI_UDP_HEADER         *Udp6Header;

+  EFI_UDP6_SESSION_DATA  Udp6Session;

+  LIST_ENTRY             *Entry;

+  UDP6_INSTANCE_DATA     *Instance;

+

+  Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);

+  ASSERT (Udp6Header != NULL);

+

+  IP6_COPY_ADDRESS (&Udp6Session.SourceAddress, &NetSession->Source);

+  IP6_COPY_ADDRESS (&Udp6Session.DestinationAddress, &NetSession->Dest);

+

+  Udp6Session.SourcePort      = NTOHS (Udp6Header->DstPort);

+  Udp6Session.DestinationPort = NTOHS (Udp6Header->SrcPort);

+

+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {

+    //

+    // Iterate all the instances.

+    //

+    Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);

+

+    if (!Instance->Configured) {

+      continue;

+    }

+

+    if (Udp6MatchDgram (Instance, &Udp6Session)) {

+      //

+      // Translate the Icmp Error code according to the udp spec.

+      //

+      Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_6, NULL, NULL);

+

+      if (IcmpError > ICMP_ERR_UNREACH_PORT) {

+        Instance->IcmpError = EFI_ICMP_ERROR;

+      }

+

+      //

+      // Notify the instance with the received Icmp Error.

+      //

+      Udp6ReportIcmpError (Instance);

+

+      break;

+    }

+  }

+

+  NetbufFree (Packet);

+}

+

+

+/**

+  This function reports the received ICMP error.

+

+  @param[in]  Instance          Pointer to the udp instance context data.

+

+**/

+VOID

+Udp6ReportIcmpError (

+  IN UDP6_INSTANCE_DATA  *Instance

+  )

+{

+  EFI_UDP6_COMPLETION_TOKEN  *Token;

+

+  if (NetMapIsEmpty (&Instance->RxTokens)) {

+    //

+    // There are no receive tokens to deliver the ICMP error.

+    //

+    return;

+  }

+

+  if (EFI_ERROR (Instance->IcmpError)) {

+    //

+    // Try to get a RxToken from the RxTokens map.

+    //

+    Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);

+

+    if (Token != NULL) {

+      //

+      // Report the error through the Token.

+      //

+      Token->Status = Instance->IcmpError;

+      gBS->SignalEvent (Token->Event);

+

+      //

+      // Clear the IcmpError.

+      //

+      Instance->IcmpError = EFI_SUCCESS;

+    }

+  }

+}

+

+

+/**

+  This function is a dummy ext-free function for the NET_BUF created for the output

+  udp datagram.

+

+  @param[in]  Context                Pointer to the context data.

+

+**/

+VOID

+EFIAPI

+Udp6NetVectorExtFree (

+  IN VOID  *Context

+  )

+{

+}

+

+

+/**

+  Set the Udp6 variable data.

+

+  @param[in]  Udp6Service            Udp6 service data.

+

+  @retval     EFI_OUT_OF_RESOURCES   There are not enough resources to set the

+                                     variable.

+  @retval     other                  Set variable failed.

+

+**/

+EFI_STATUS

+Udp6SetVariableData (

+  IN UDP6_SERVICE_DATA  *Udp6Service

+  )

+{

+  UINT32                  NumConfiguredInstance;

+  LIST_ENTRY              *Entry;

+  UINTN                   VariableDataSize;

+  EFI_UDP6_VARIABLE_DATA  *Udp6VariableData;

+  EFI_UDP6_SERVICE_POINT  *Udp6ServicePoint;

+  UDP6_INSTANCE_DATA      *Udp6Instance;

+  CHAR16                  *NewMacString;

+  EFI_STATUS              Status;

+

+  NumConfiguredInstance = 0;

+

+  //

+  // Go through the children list to count the configured children.

+  //

+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {

+    Udp6Instance = NET_LIST_USER_STRUCT_S (

+                     Entry,

+                     UDP6_INSTANCE_DATA,

+                     Link,

+                     UDP6_INSTANCE_DATA_SIGNATURE

+                     );

+

+    if (Udp6Instance->Configured) {

+      NumConfiguredInstance++;

+    }

+  }

+

+  //

+  // Calculate the size of the Udp6VariableData. As there may be no Udp6 child,

+  // we should add extra buffer for the service points only if the number of configured

+  // children is more than 1.

+  //

+  VariableDataSize = sizeof (EFI_UDP6_VARIABLE_DATA);

+

+  if (NumConfiguredInstance > 1) {

+    VariableDataSize += sizeof (EFI_UDP6_SERVICE_POINT) * (NumConfiguredInstance - 1);

+  }

+

+  Udp6VariableData = AllocateZeroPool (VariableDataSize);

+  if (Udp6VariableData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Udp6VariableData->DriverHandle = Udp6Service->ImageHandle;

+  Udp6VariableData->ServiceCount = NumConfiguredInstance;

+

+  Udp6ServicePoint = &Udp6VariableData->Services[0];

+

+  //

+  // Go through the children list to fill the configured children's address pairs.

+  //

+  NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {

+    Udp6Instance = NET_LIST_USER_STRUCT_S (

+                     Entry,

+                     UDP6_INSTANCE_DATA,

+                     Link,

+                     UDP6_INSTANCE_DATA_SIGNATURE

+                     );

+

+    if (Udp6Instance->Configured) {

+      Udp6ServicePoint->InstanceHandle = Udp6Instance->ChildHandle;

+      Udp6ServicePoint->LocalPort      = Udp6Instance->ConfigData.StationPort;

+      Udp6ServicePoint->RemotePort     = Udp6Instance->ConfigData.RemotePort;

+

+      IP6_COPY_ADDRESS (

+        &Udp6ServicePoint->LocalAddress,

+        &Udp6Instance->ConfigData.StationAddress

+        );

+      IP6_COPY_ADDRESS (

+        &Udp6ServicePoint->RemoteAddress,

+        &Udp6Instance->ConfigData.RemoteAddress

+        );

+      Udp6ServicePoint++;

+    }

+  }

+

+  //

+  // Get the MAC string.

+  //

+  Status = NetLibGetMacString (

+             Udp6Service->ControllerHandle,

+             Udp6Service->ImageHandle,

+             &NewMacString

+             );

+  if (EFI_ERROR (Status)) {

+    goto EXIT;

+  }

+

+  if (Udp6Service->MacString != NULL) {

+    //

+    // The variable is set already, we're going to update it.

+    //

+    if (StrCmp (Udp6Service->MacString, NewMacString) != 0) {

+      //

+      // The MAC address is changed, delete the previous variable first.

+      //

+      gRT->SetVariable (

+             Udp6Service->MacString,

+             &gEfiUdp6ServiceBindingProtocolGuid,

+             EFI_VARIABLE_BOOTSERVICE_ACCESS,

+             0,

+             NULL

+             );

+    }

+

+    FreePool (Udp6Service->MacString);

+  }

+

+  Udp6Service->MacString = NewMacString;

+

+  Status = gRT->SetVariable (

+                  Udp6Service->MacString,

+                  &gEfiUdp6ServiceBindingProtocolGuid,

+                  EFI_VARIABLE_BOOTSERVICE_ACCESS,

+                  VariableDataSize,

+                  (VOID *) Udp6VariableData

+                  );

+

+EXIT:

+

+  FreePool (Udp6VariableData);

+

+  return Status;

+}

+

+

+/**

+  Clear the variable and free the resource.

+

+  @param[in, out]  Udp6Service            Udp6 service data.

+

+**/

+VOID

+Udp6ClearVariableData (

+  IN OUT UDP6_SERVICE_DATA  *Udp6Service

+  )

+{

+  ASSERT (Udp6Service->MacString != NULL);

+

+  gRT->SetVariable (

+         Udp6Service->MacString,

+         &gEfiUdp6ServiceBindingProtocolGuid,

+         EFI_VARIABLE_BOOTSERVICE_ACCESS,

+         0,

+         NULL

+         );

+

+  FreePool (Udp6Service->MacString);

+  Udp6Service->MacString = NULL;

+}

+

+

+/**

+  Find the key in the netmap.

+

+  @param[in]  Map                    The netmap to search within.

+  @param[in]  Key                    The key to search.

+

+  @return The point to the item contains the Key, or NULL, if Key isn't in the map.

+

+**/

+NET_MAP_ITEM *

+Udp6MapMultiCastAddr (

+  IN  NET_MAP               *Map,

+  IN  VOID                  *Key

+  )

+{

+  LIST_ENTRY        *Entry;

+  NET_MAP_ITEM      *Item;

+  EFI_IPv6_ADDRESS  *Addr;

+

+  ASSERT (Map != NULL);

+  NET_LIST_FOR_EACH (Entry, &Map->Used) {

+    Item  = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);

+    Addr  = (EFI_IPv6_ADDRESS *) Item->Key;

+    if (EFI_IP6_EQUAL (Addr, Key)) {

+      return Item;

+    }

+  }

+  return NULL;

+}

+

diff --git a/NetworkPkg/Udp6Dxe/Udp6Impl.h b/NetworkPkg/Udp6Dxe/Udp6Impl.h
new file mode 100644
index 0000000..108e30b
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Impl.h
@@ -0,0 +1,673 @@
+/** @file

+  Udp6 driver's whole implementation and internal data structures.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef _UDP6_IMPL_H_

+#define _UDP6_IMPL_H_

+

+#include <Uefi.h>

+

+#include <Protocol/Ip6.h>

+#include <Protocol/Udp6.h>

+

+#include <Library/IpIoLib.h>

+#include <Library/DebugLib.h>

+#include <Library/UefiRuntimeServicesTableLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/BaseLib.h>

+#include <Library/UefiLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/DpcLib.h>

+

+#include "Udp6Driver.h"

+

+extern EFI_COMPONENT_NAME2_PROTOCOL   gUdp6ComponentName2;

+extern EFI_COMPONENT_NAME_PROTOCOL    gUdp6ComponentName;

+extern EFI_SERVICE_BINDING_PROTOCOL   mUdp6ServiceBinding;

+extern EFI_UDP6_PROTOCOL              mUdp6Protocol;

+extern UINT16                         mUdp6RandomPort;

+

+//

+// Define time out 50 milliseconds

+//

+#define UDP6_TIMEOUT_INTERVAL (50 * TICKS_PER_MS)

+#define UDP6_HEADER_SIZE      sizeof (EFI_UDP_HEADER)

+#define UDP6_MAX_DATA_SIZE    65507

+#define UDP6_PORT_KNOWN       1024

+

+#define UDP6_SERVICE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', '6')

+#define UDP6_INSTANCE_DATA_SIGNATURE  SIGNATURE_32 ('U', 'd', 'p', 'S')

+

+#define UDP6_SERVICE_DATA_FROM_THIS(a) \

+  CR ( \

+  (a), \

+  UDP6_SERVICE_DATA, \

+  ServiceBinding, \

+  UDP6_SERVICE_DATA_SIGNATURE \

+  )

+

+#define UDP6_INSTANCE_DATA_FROM_THIS(a) \

+  CR ( \

+  (a), \

+  UDP6_INSTANCE_DATA, \

+  Udp6Proto, \

+  UDP6_INSTANCE_DATA_SIGNATURE \

+  )

+//

+// Udp6 service contest data

+//

+typedef struct _UDP6_SERVICE_DATA {

+  UINT32                        Signature;

+  EFI_SERVICE_BINDING_PROTOCOL  ServiceBinding;

+  EFI_HANDLE                    ImageHandle;

+  EFI_HANDLE                    ControllerHandle;

+  LIST_ENTRY                    ChildrenList;

+  UINTN                         ChildrenNumber;

+  IP_IO                         *IpIo;

+  EFI_EVENT                     TimeoutEvent;

+  CHAR16                        *MacString;

+} UDP6_SERVICE_DATA;

+

+typedef struct _UDP6_INSTANCE_DATA {

+  UINT32                Signature;

+  LIST_ENTRY            Link;

+  UDP6_SERVICE_DATA     *Udp6Service;

+  EFI_UDP6_PROTOCOL     Udp6Proto;

+  EFI_UDP6_CONFIG_DATA  ConfigData;

+  EFI_HANDLE            ChildHandle;

+  BOOLEAN               Configured;

+  BOOLEAN               IsNoMapping;

+  NET_MAP               TxTokens;

+  NET_MAP               RxTokens;

+  NET_MAP               McastIps;

+  LIST_ENTRY            RcvdDgramQue;

+  LIST_ENTRY            DeliveredDgramQue;

+  UINT16                HeadSum;

+  EFI_STATUS            IcmpError;

+  IP_IO_IP_INFO         *IpInfo;

+  BOOLEAN               Destroyed;

+} UDP6_INSTANCE_DATA;

+

+typedef struct _UDP6_RXDATA_WRAP {

+  LIST_ENTRY             Link;

+  NET_BUF                *Packet;

+  UINT32                 TimeoutTick;

+  EFI_UDP6_RECEIVE_DATA  RxData;

+} UDP6_RXDATA_WRAP;

+

+/**

+  Clean the Udp service context data.

+

+  @param[in, out]  Udp6Service      Pointer to the UDP6_SERVICE_DATA.

+

+**/

+VOID

+Udp6CleanService (

+  IN OUT UDP6_SERVICE_DATA  *Udp6Service

+  );

+

+/**

+  Create the Udp service context data.

+

+  @param[in]  Udp6Service            Pointer to the UDP6_SERVICE_DATA.

+  @param[in]  ImageHandle            The image handle of this udp6 driver.

+  @param[in]  ControllerHandle       The controller handle this udp6 driver binds on.

+

+  @retval EFI_SUCCESS            The udp6 service context data was created and

+                                 initialized.

+  @retval EFI_OUT_OF_RESOURCES   Cannot allocate memory.

+  @retval Others                 An error condition occurred.

+

+**/

+EFI_STATUS

+Udp6CreateService (

+  IN UDP6_SERVICE_DATA  *Udp6Service,

+  IN EFI_HANDLE         ImageHandle,

+  IN EFI_HANDLE         ControllerHandle

+  );

+

+/**

+  Set the Udp6 variable data.

+

+  @param[in]  Udp6Service            Udp6 service data.

+

+  @retval     EFI_OUT_OF_RESOURCES   There are not enough resources to set the

+                                     variable.

+  @retval     other                  Set variable failed.

+

+**/

+EFI_STATUS

+Udp6SetVariableData (

+  IN UDP6_SERVICE_DATA  *Udp6Service

+  );

+

+/**

+  This function cleans the udp instance.

+

+  @param[in, out]  Instance       Pointer to the UDP6_INSTANCE_DATA to clean.

+

+**/

+VOID

+Udp6CleanInstance (

+  IN OUT UDP6_INSTANCE_DATA  *Instance

+  );

+

+/**

+  Clear the variable and free the resource.

+

+  @param[in, out]  Udp6Service            Udp6 service data.

+

+**/

+VOID

+Udp6ClearVariableData (

+  IN OUT UDP6_SERVICE_DATA  *Udp6Service

+  );

+

+/**

+  This function intializes the new created udp instance.

+

+  @param[in]      Udp6Service      Pointer to the UDP6_SERVICE_DATA.

+  @param[in, out]  Instance         Pointer to the un-initialized UDP6_INSTANCE_DATA.

+

+**/

+VOID

+Udp6InitInstance (

+  IN UDP6_SERVICE_DATA       *Udp6Service,

+  IN OUT UDP6_INSTANCE_DATA  *Instance

+  );

+

+/**

+  This function reports the received ICMP error.

+

+  @param[in]  Instance          Pointer to the udp instance context data.

+

+**/

+VOID

+Udp6ReportIcmpError (

+  IN UDP6_INSTANCE_DATA  *Instance

+  );

+

+/**

+  This function copies the current operational settings of this EFI UDPv6 Protocol

+  instance into user-supplied buffers. This function is used optionally to retrieve

+  the operational mode data of underlying networks or drivers.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[out] Udp6ConfigData     The buffer in which the current UDP configuration

+                                 data is returned. This parameter is optional and

+                                 may be NULL.

+  @param[out] Ip6ModeData        The buffer in which the current EFI IPv6 Protocol

+                                 mode data is returned. This parameter is optional

+                                 and may be NULL.

+  @param[out] MnpConfigData      The buffer in which the current managed network

+                                 configuration data is returned. This parameter

+                                 is optional and may be NULL.

+  @param[out] SnpModeData        The buffer in which the simple network mode data

+                                 is returned. This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS            The mode data was read.

+  @retval EFI_NOT_STARTED        When Udp6ConfigData is queried, no configuration

+                                 data is  available because this instance has not

+                                 been started.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6GetModeData (

+  IN  EFI_UDP6_PROTOCOL                *This,

+  OUT EFI_UDP6_CONFIG_DATA             *Udp6ConfigData OPTIONAL,

+  OUT EFI_IP6_MODE_DATA                *Ip6ModeData    OPTIONAL,

+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA  *MnpConfigData  OPTIONAL,

+  OUT EFI_SIMPLE_NETWORK_MODE          *SnpModeData    OPTIONAL

+  );

+

+/**

+  This function is used to do the following:

+  Initialize and start this instance of the EFI UDPv6 Protocol.

+  Change the filtering rules and operational parameters.

+  Reset this instance of the EFI UDPv6 Protocol.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  UdpConfigData      Pointer to the buffer to set the configuration

+                                 data. This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS            The configuration settings were set, changed, or

+                                 reset successfully.

+  @retval EFI_NO_MAPPING         When the UdpConifgData.UseAnyStationAddress is set

+                                 to true  and there is no address available for IP6

+                                 driver to binding  source address to this

+                                 instance.

+  @retval EFI_INVALID_PARAMETER  One or more following conditions are TRUE:

+                                 This is NULL.

+                                 UdpConfigData.StationAddress is not a valid

+                                 unicast IPv6 address.

+                                 UdpConfigData.RemoteAddress is not a valid unicast

+                                 IPv6  address, if it is not zero.

+  @retval EFI_ALREADY_STARTED    The EFI UDPv6 Protocol instance is already

+                                 started/configured and must be stopped/reset

+                                 before it can be reconfigured. Only TrafficClass,

+                                 HopLimit, ReceiveTimeout, and TransmitTimeout can

+                                 be reconfigured without stopping the current

+                                 instance of the EFI UDPv6 Protocol.

+  @retval EFI_ACCESS_DENIED      UdpConfigData.AllowDuplicatePort is FALSE, and

+                                 UdpConfigData.StationPort is already used by another

+                                 instance.

+  @retval EFI_OUT_OF_RESOURCES   The EFI UDPv6 Protocol driver cannot allocate

+                                 memory for this EFI UDPv6 Protocol instance.

+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred, and

+                                 this instance was not opened.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Configure (

+  IN EFI_UDP6_PROTOCOL     *This,

+  IN EFI_UDP6_CONFIG_DATA  *UdpConfigData OPTIONAL

+  );

+

+/**

+  This function places a sending request to this instance of the EFI UDPv6 Protocol,

+  alongside the transmit data that was filled by the user.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to the completion token that will be

+                                 placed into the transmit queue.

+

+  @retval EFI_SUCCESS            The data has been queued for transmission.

+  @retval EFI_NOT_STARTED        This EFI UDPv6 Protocol instance has not been

+                                 started.

+  @retval EFI_NO_MAPPING         The under-layer IPv6 driver was responsible for

+                                 choosing a source address for this instance, but

+                                 no  source address was available for use.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 This is NULL. Token is NULL. Token.Event is NULL.

+                                 Token.Packet.TxData is NULL.

+                                 Token.Packet.TxData.FragmentCount is zero.

+                                 Token.Packet.TxData.DataLength is not equal to the

+                                 sum of fragment lengths.

+                                 One or more of the

+                                 Token.Packet.TxData.FragmentTable[]

+                                 .FragmentLength fields is zero.

+                                 One or more of the

+                                 Token.Packet.TxData.FragmentTable[]

+                                 .FragmentBuffer fields is NULL.

+                                 One or more of the

+                                 Token.Packet.TxData.UdpSessionData.

+                                 DestinationAddres are not valid unicast IPv6

+                                 addresses, if the  UdpSessionData is not NULL.

+                                 Token.Packet.TxData.UdpSessionData.

+                                 DestinationAddres is NULL

+                                 Token.Packet.TxData.UdpSessionData.

+                                 DestinatioPort is zero.

+                                 Token.Packet.TxData.UdpSessionData is

+                                 NULL and this  instance's

+                                 UdpConfigData.RemoteAddress is unspecified.

+  @retval EFI_ACCESS_DENIED      The transmit completion token with the same

+                                 Token.Event is already in the transmit queue.

+  @retval EFI_NOT_READY          The completion token could not be queued because

+                                 the transmit queue is full.

+  @retval EFI_OUT_OF_RESOURCES   Could not queue the transmit data.

+  @retval EFI_NOT_FOUND          There is no route to the destination network or

+                                 address.

+  @retval EFI_BAD_BUFFER_SIZE    The data length is greater than the maximum UDP

+                                 packet size. Or the length of the IP header + UDP

+                                 header + data length is greater than MTU if

+                                 DoNotFragment is TRUE.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Transmit (

+  IN EFI_UDP6_PROTOCOL          *This,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token

+  );

+

+/**

+  This function places a completion token into the receive packet queue. This function

+  is always asynchronous.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to a token that is associated with the

+                                 receive data descriptor.

+

+  @retval EFI_SUCCESS            The receive completion token is cached.

+  @retval EFI_NOT_STARTED        This EFI UDPv6 Protocol instance has not been

+                                 started.

+  @retval EFI_NO_MAPPING         When using a default address, configuration (DHCP,

+                                 BOOTP, RARP, etc.) is not finished yet.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 This is NULL.

+                                 Token is NULL.

+                                 Token.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued

+                                 due to a lack of system resources (usually

+                                 memory).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+                                 The EFI UDPv6 Protocol instance has been reset to

+                                 startup defaults.

+  @retval EFI_ACCESS_DENIED      A receive completion token with the same

+                                 Token.Event is already in the receive queue.

+  @retval EFI_NOT_READY          The receive request could not be queued because

+                                 the receive  queue is full.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Receive (

+  IN EFI_UDP6_PROTOCOL          *This,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token

+  );

+

+/**

+  This function is used to abort a pending transmit or receive request.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to a token that has been issued by

+                                 EFI_UDP6_PROTOCOL.Transmit() or

+                                 EFI_UDP6_PROTOCOL.Receive(). This parameter is

+                                 optional and may be NULL.

+

+  @retval EFI_SUCCESS            The asynchronous I/O request is aborted and

+                                 Token.Event is  signaled. When Token is NULL, all

+                                 pending requests are aborted and their events are

+                                 signaled.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_NOT_STARTED        This instance has not been started.

+  @retval EFI_NO_MAPPING         When using the default address, configuration

+                                 (DHCP, BOOTP, RARP, etc.) is not finished yet.

+  @retval EFI_NOT_FOUND          When Token is not NULL, the asynchronous I/O

+                                 request is not found in the transmit or receive

+                                 queue. It either completed or was not issued by

+                                 Transmit() or Receive().

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Cancel (

+  IN EFI_UDP6_PROTOCOL          *This,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL

+  );

+

+/**

+  This function can be used by network drivers and applications to increase the rate that

+  data packets are moved between the communications device and the transmit/receive queues.

+

+  @param[in] This                Pointer to the EFI_UDP6_PROTOCOL instance.

+

+  @retval EFI_SUCCESS            Incoming or outgoing data was processed.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+  @retval EFI_TIMEOUT            Data was dropped out of the transmit and/or

+                                 receive queue.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Poll (

+  IN EFI_UDP6_PROTOCOL  *This

+  );

+

+/**

+  This function is used to enable and disable the multicast group filtering.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  JoinFlag           Set to TRUE to join a multicast group. Set to

+                                 FALSE to leave one or all multicast groups.

+  @param[in]  MulticastAddress   Pointer to multicast group address to join or

+                                 leave. This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_NOT_STARTED        The EFI UDPv6 Protocol instance has not been

+                                 started.

+  @retval EFI_OUT_OF_RESOURCES   Could not allocate resources to join the group.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 This is NULL. JoinFlag is TRUE and

+                                 MulticastAddress is NULL. JoinFlag is TRUE and

+                                 *MulticastAddress is not a valid  multicast

+                                 address.

+  @retval EFI_ALREADY_STARTED    The group address is already in the group table

+                                 (when JoinFlag is TRUE).

+  @retval EFI_NOT_FOUND          The group address is not in the group table (when

+                                 JoinFlag is FALSE).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Groups (

+  IN EFI_UDP6_PROTOCOL  *This,

+  IN BOOLEAN            JoinFlag,

+  IN EFI_IPv6_ADDRESS   *MulticastAddress OPTIONAL

+  );

+

+/**

+  This function tries to bind the udp instance according to the configured port

+  allocation stragety.

+

+  @param[in]  InstanceList       Pointer to the head of the list linking the udp

+                                 instances.

+  @param[in]  ConfigData         Pointer to the ConfigData of the instance to be

+                                 bound.

+

+  @retval EFI_SUCCESS            The bound operation completed successfully.

+  @retval EFI_ACCESS_DENIED      The <Address, Port> specified by the ConfigData is

+                                 already used by another instance.

+  @retval EFI_OUT_OF_RESOURCES   No available port resources.

+

+**/

+EFI_STATUS

+Udp6Bind (

+  IN LIST_ENTRY            *InstanceList,

+  IN EFI_UDP6_CONFIG_DATA  *ConfigData

+  );

+

+/**

+  This function builds the Ip6 configdata from the Udp6ConfigData.

+

+  @param[in]       Udp6ConfigData         Pointer to the EFI_UDP6_CONFIG_DATA.

+  @param[in, out]  Ip6ConfigData          Pointer to the EFI_IP6_CONFIG_DATA.

+

+**/

+VOID

+Udp6BuildIp6ConfigData (

+  IN EFI_UDP6_CONFIG_DATA      *Udp6ConfigData,

+  IN OUT EFI_IP6_CONFIG_DATA   *Ip6ConfigData

+  );

+

+/**

+  This function checks whether the specified Token duplicates with the one in the Map.

+

+  @param[in]  Map                Pointer to the NET_MAP.

+  @param[in]  Item               Pointer to the NET_MAP_ITEM contain the pointer to

+                                 the Token.

+  @param[in]  Context            Pointer to the Token to be checked.

+

+  @retval EFI_SUCCESS            The Token specified by Context differs from the

+                                 one in the Item.

+  @retval EFI_ACCESS_DENIED      The Token duplicates with the one in the Item.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6TokenExist (

+  IN NET_MAP       *Map,

+  IN NET_MAP_ITEM  *Item,

+  IN VOID          *Context

+  );

+

+/**

+  This function removes the specified Token from the TokenMap.

+

+  @param[in]  TokenMap           Pointer to the NET_MAP containing the tokens.

+  @param[in]  Token              Pointer to the Token to be removed.

+

+  @retval EFI_SUCCESS            The specified Token is removed from the TokenMap.

+  @retval EFI_NOT_FOUND          The specified Token is not found in the TokenMap.

+

+**/

+EFI_STATUS

+Udp6RemoveToken (

+  IN NET_MAP                    *TokenMap,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token

+  );

+

+/**

+  This function is used to check whether the NewConfigData has any un-reconfigurable

+  parameters changed compared to the OldConfigData.

+

+  @param[in]  OldConfigData    Pointer to the current ConfigData the udp instance

+                               uses.

+  @param[in]  NewConfigData    Pointer to the new ConfigData.

+

+  @retval TRUE     The instance is reconfigurable according to NewConfigData.

+  @retval FALSE   The instance is not reconfigurable according to NewConfigData.

+

+**/

+BOOLEAN

+Udp6IsReconfigurable (

+  IN EFI_UDP6_CONFIG_DATA  *OldConfigData,

+  IN EFI_UDP6_CONFIG_DATA  *NewConfigData

+  );

+

+/**

+  This function removes the multicast group specified by Arg from the Map.

+

+  @param[in]  Map                Pointer to the NET_MAP.

+  @param[in]  Item               Pointer to the NET_MAP_ITEM.

+  @param[in]  Arg                Pointer to the Arg. It is the pointer to a

+                                 multicast IPv6 Address. This parameter is

+                                 optional and may be NULL.

+

+  @retval EFI_SUCCESS            The multicast address is removed.

+  @retval EFI_ABORTED            The specified multicast address is removed, and the

+                                 Arg is not NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6LeaveGroup (

+  IN NET_MAP       *Map,

+  IN NET_MAP_ITEM  *Item,

+  IN VOID          *Arg OPTIONAL

+  );

+

+/**

+  This function validates the TxToken, it returns the error code according to the spec.

+

+  @param[in]  Instance           Pointer to the udp instance context data.

+  @param[in]  TxToken            Pointer to the token to be checked.

+

+  @retval EFI_SUCCESS            The TxToken is valid.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 Token.Event is NULL.

+                                 Token.Packet.TxData is NULL.

+                                 Token.Packet.TxData.FragmentCount is zero.

+                                 Token.Packet.TxData.DataLength is not equal to the

+                                 sum of fragment lengths.

+                                 One or more of the

+                                 Token.Packet.TxData.FragmentTable[].FragmentLength

+                                 fields is zero.

+                                 One or more of the

+                                 Token.Packet.TxData.FragmentTable[].FragmentBuffer

+                                 fields is NULL.

+                                 UdpSessionData.DestinationAddress are not valid

+                                 unicast IPv6 addresses if the UdpSessionData is

+                                 not NULL.

+                                 UdpSessionData.DestinationPort and

+                                 ConfigData.RemotePort are all zero if the

+                                 UdpSessionData is not NULL.

+  @retval EFI_BAD_BUFFER_SIZE    The data length is greater than the maximum UDP

+                                 packet size.

+

+**/

+EFI_STATUS

+Udp6ValidateTxToken (

+  IN UDP6_INSTANCE_DATA         *Instance,

+  IN EFI_UDP6_COMPLETION_TOKEN  *TxToken

+  );

+

+/**

+  This function is a dummy ext-free function for the NET_BUF created for the output

+  udp datagram.

+

+  @param[in]  Context               Pointer to the context data.

+

+**/

+VOID

+EFIAPI

+Udp6NetVectorExtFree (

+  IN VOID  *Context

+  );

+

+/**

+  This function calculates the checksum for the Packet, utilizing the pre-calculated

+  pseudo header to reduce overhead.

+

+  @param[in]  Packet           Pointer to the NET_BUF contains the udp datagram.

+  @param[in]  HeadSum          Checksum of the pseudo header execpt the length

+                               field.

+

+  @return The 16-bit checksum of this udp datagram.

+

+**/

+UINT16

+Udp6Checksum (

+  IN NET_BUF *Packet,

+  IN UINT16  HeadSum

+  );

+

+/**

+  This function delivers the received datagrams to the specified instance.

+

+  @param[in]  Instance               Pointer to the instance context data.

+

+**/

+VOID

+Udp6InstanceDeliverDgram (

+  IN UDP6_INSTANCE_DATA  *Instance

+  );

+

+/**

+  Cancel Udp6 tokens from the Udp6 instance.

+

+  @param[in]  Instance           Pointer to the udp instance context data.

+  @param[in]  Token              Pointer to the token to be canceled. If NULL, all

+                                 tokens in this instance will be cancelled.

+                                 This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS            The Token is cancelled.

+  @retval EFI_NOT_FOUND          The Token is not found.

+

+**/

+EFI_STATUS

+Udp6InstanceCancelToken (

+  IN UDP6_INSTANCE_DATA         *Instance,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL

+  );

+

+/**

+  This function removes all the Wrap datas in the RcvdDgramQue.

+

+  @param[in]  Instance    Pointer to the Udp6 Instance.

+

+**/

+VOID

+Udp6FlushRcvdDgram (

+  IN UDP6_INSTANCE_DATA  *Instance

+  );

+

+#endif

+

diff --git a/NetworkPkg/Udp6Dxe/Udp6Main.c b/NetworkPkg/Udp6Dxe/Udp6Main.c
new file mode 100644
index 0000000..0cad596
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Main.c
@@ -0,0 +1,855 @@
+/** @file

+  Contains all EFI_UDP6_PROTOCOL interfaces.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "Udp6Impl.h"

+

+EFI_UDP6_PROTOCOL  mUdp6Protocol = {

+  Udp6GetModeData,

+  Udp6Configure,

+  Udp6Groups,

+  Udp6Transmit,

+  Udp6Receive,

+  Udp6Cancel,

+  Udp6Poll

+};

+

+

+/**

+  This function copies the current operational settings of this EFI UDPv6 Protocol

+  instance into user-supplied buffers. This function is used optionally to retrieve

+  the operational mode data of underlying networks or drivers.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[out] Udp6ConfigData     The buffer in which the current UDP configuration

+                                 data is returned. This parameter is optional and

+                                 may be NULL.

+  @param[out] Ip6ModeData        The buffer in which the current EFI IPv6 Protocol

+                                 mode data is returned. This parameter is optional

+                                 and may be NULL.

+  @param[out] MnpConfigData      The buffer in which the current managed network

+                                 configuration data is returned. This parameter is

+                                 optional and may be NULL.

+  @param[out] SnpModeData        The buffer in which the simple network mode data

+                                 is returned. This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS            The mode data was read.

+  @retval EFI_NOT_STARTED        When Udp6ConfigData is queried, no configuration

+                                 data is  available because this instance has not

+                                 been started.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6GetModeData (

+  IN  EFI_UDP6_PROTOCOL                *This,

+  OUT EFI_UDP6_CONFIG_DATA             *Udp6ConfigData OPTIONAL,

+  OUT EFI_IP6_MODE_DATA                *Ip6ModeData    OPTIONAL,

+  OUT EFI_MANAGED_NETWORK_CONFIG_DATA  *MnpConfigData  OPTIONAL,

+  OUT EFI_SIMPLE_NETWORK_MODE          *SnpModeData    OPTIONAL

+  )

+{

+  UDP6_INSTANCE_DATA  *Instance;

+  EFI_IP6_PROTOCOL    *Ip;

+  EFI_TPL             OldTpl;

+  EFI_STATUS          Status;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);

+

+  if (!Instance->Configured && (Udp6ConfigData != NULL)) {

+    return EFI_NOT_STARTED;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (Udp6ConfigData != NULL) {

+    //

+    // Set the Udp6ConfigData.

+    //

+    CopyMem (Udp6ConfigData, &Instance->ConfigData, sizeof (EFI_UDP6_CONFIG_DATA));

+  }

+

+  Ip = Instance->IpInfo->Ip.Ip6;

+

+  //

+  // Get the underlying Ip6ModeData, MnpConfigData and SnpModeData.

+  //

+  Status = Ip->GetModeData (Ip, Ip6ModeData, MnpConfigData, SnpModeData);

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  This function is used to do the following:

+  Initialize and start this instance of the EFI UDPv6 Protocol.

+  Change the filtering rules and operational parameters.

+  Reset this instance of the EFI UDPv6 Protocol.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  UdpConfigData      Pointer to the buffer to set the configuration

+                                 data. This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS            The configuration settings were set, changed, or

+                                 reset successfully.

+  @retval EFI_NO_MAPPING         When the UdpConifgData.UseAnyStationAddress is set

+                                 to true and there is no address available for the IP6

+                                 driver to bind a source address to this instance.

+  @retval EFI_INVALID_PARAMETER  One or more following conditions are TRUE:

+                                 This is NULL.

+                                 UdpConfigData.StationAddress is not a valid

+                                 unicast IPv6 address.

+                                 UdpConfigData.RemoteAddress is not a valid unicast

+                                 IPv6  address if it is not zero.

+  @retval EFI_ALREADY_STARTED    The EFI UDPv6 Protocol instance is already

+                                 started/configured and must be stopped/reset

+                                 before it can be reconfigured. Only TrafficClass,

+                                 HopLimit, ReceiveTimeout, and  TransmitTimeout can

+                                 be reconfigured without stopping the  current

+                                 instance of the EFI UDPv6 Protocol.

+  @retval EFI_ACCESS_DENIED      UdpConfigData.AllowDuplicatePort is FALSE and

+                                 UdpConfigData.StationPort is already used by another

+                                 instance.

+  @retval EFI_OUT_OF_RESOURCES   The EFI UDPv6 Protocol driver cannot allocate

+                                 memory for this EFI UDPv6 Protocol instance.

+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred, and

+                                 this instance was not opened.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Configure (

+  IN EFI_UDP6_PROTOCOL     *This,

+  IN EFI_UDP6_CONFIG_DATA  *UdpConfigData OPTIONAL

+  )

+{

+  EFI_STATUS           Status;

+  UDP6_INSTANCE_DATA   *Instance;

+  UDP6_SERVICE_DATA    *Udp6Service;

+  EFI_TPL              OldTpl;

+  EFI_IPv6_ADDRESS     StationAddress;

+  EFI_IPv6_ADDRESS     RemoteAddress;

+  EFI_IP6_CONFIG_DATA  Ip6ConfigData;

+  EFI_IPv6_ADDRESS     LocalAddr;

+  EFI_IPv6_ADDRESS     RemoteAddr;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);

+

+  if (!Instance->Configured && (UdpConfigData == NULL)) {

+    return EFI_SUCCESS;

+  }

+

+  Udp6Service = Instance->Udp6Service;

+  Status      = EFI_SUCCESS;

+  ASSERT (Udp6Service != NULL);

+

+  OldTpl      = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (UdpConfigData != NULL) {

+

+    IP6_COPY_ADDRESS (&StationAddress, &UdpConfigData->StationAddress);

+    IP6_COPY_ADDRESS (&RemoteAddress, &UdpConfigData->RemoteAddress);

+

+    if ((!NetIp6IsUnspecifiedAddr (&StationAddress) && !NetIp6IsValidUnicast (&StationAddress)) ||

+        (!NetIp6IsUnspecifiedAddr (&RemoteAddress) && !NetIp6IsValidUnicast (&RemoteAddress))

+        ){

+      //

+      // If not use default address, and StationAddress is not a valid unicast

+      // if it is not IPv6 address or RemoteAddress is not a valid unicast IPv6

+      // address if it is not 0.

+      //

+      Status = EFI_INVALID_PARAMETER;

+      goto ON_EXIT;

+    }

+

+    if (Instance->Configured) {

+      //

+      // The instance is already configured, try to do the re-configuration.

+      //

+      if (!Udp6IsReconfigurable (&Instance->ConfigData, UdpConfigData)) {

+        //

+        // If the new configuration data wants to change some unreconfigurable

+        // settings, return EFI_ALREADY_STARTED.

+        //

+        Status = EFI_ALREADY_STARTED;

+        goto ON_EXIT;

+      }

+

+      //

+      // Save the reconfigurable parameters.

+      //

+      Instance->ConfigData.TrafficClass    = UdpConfigData->TrafficClass;

+      Instance->ConfigData.HopLimit        = UdpConfigData->HopLimit;

+      Instance->ConfigData.ReceiveTimeout  = UdpConfigData->ReceiveTimeout;

+      Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;

+    } else {

+      //

+      // Construct the Ip configuration data from the UdpConfigData.

+      //

+      Udp6BuildIp6ConfigData (UdpConfigData, &Ip6ConfigData);

+

+      //

+      // Configure the Ip instance wrapped in the IpInfo.

+      //

+      Status = IpIoConfigIp (Instance->IpInfo, &Ip6ConfigData);

+      if (EFI_ERROR (Status)) {

+        if (Status == EFI_NO_MAPPING) {

+          Instance->IsNoMapping = TRUE;

+        }

+

+        goto ON_EXIT;

+      }

+

+      Instance->IsNoMapping = FALSE;

+

+      //

+      // Save the configuration data.

+      //

+      CopyMem (

+        &Instance->ConfigData,

+        UdpConfigData,

+        sizeof (EFI_UDP6_CONFIG_DATA)

+        );

+      IP6_COPY_ADDRESS (&Instance->ConfigData.StationAddress, &Ip6ConfigData.StationAddress);

+      //

+      // Try to allocate the required port resource.

+      //

+      Status = Udp6Bind (&Udp6Service->ChildrenList, &Instance->ConfigData);

+      if (EFI_ERROR (Status)) {

+        //

+        // Reset the ip instance if bind fails.

+        //

+        IpIoConfigIp (Instance->IpInfo, NULL);

+        goto ON_EXIT;

+      }

+

+      //

+      // Pre calculate the checksum for the pseudo head, ignore the UDP length first.

+      //

+      IP6_COPY_ADDRESS (&LocalAddr, &Instance->ConfigData.StationAddress);

+      IP6_COPY_ADDRESS (&RemoteAddr, &Instance->ConfigData.RemoteAddress);

+

+      Instance->HeadSum = NetIp6PseudoHeadChecksum (

+                            &LocalAddr,

+                            &RemoteAddr,

+                            EFI_IP_PROTO_UDP,

+                            0

+                            );

+

+      Instance->Configured = TRUE;

+    }

+  } else {

+    //

+    // UdpConfigData is NULL, reset the instance.

+    //

+    Instance->Configured  = FALSE;

+    Instance->IsNoMapping = FALSE;

+

+    //

+    // Reset the Ip instance wrapped in the IpInfo.

+    //

+    IpIoConfigIp (Instance->IpInfo, NULL);

+

+    //

+    // Cancel all the user tokens.

+    //

+    Status = Instance->Udp6Proto.Cancel (&Instance->Udp6Proto, NULL);

+

+    //

+    // Remove the buffered RxData for this instance.

+    //

+    Udp6FlushRcvdDgram (Instance);

+

+    ASSERT (IsListEmpty (&Instance->DeliveredDgramQue));

+  }

+

+  Status = Udp6SetVariableData (Instance->Udp6Service);

+

+ON_EXIT:

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  This function is used to enable and disable the multicast group filtering.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  JoinFlag           Set to TRUE to join a multicast group. Set to

+                                 FALSE to leave one or all multicast groups.

+  @param[in]  MulticastAddress   Pointer to multicast group address to join or

+                                 leave. This parameter is optional and may be NULL.

+

+  @retval EFI_SUCCESS            The operation completed successfully.

+  @retval EFI_NOT_STARTED        The EFI UDPv6 Protocol instance has not been

+                                 started.

+  @retval EFI_OUT_OF_RESOURCES   Could not allocate resources to join the group.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 This is NULL.

+                                 JoinFlag is TRUE and MulticastAddress is NULL.

+                                 JoinFlag is TRUE and *MulticastAddress is not a

+                                 valid  multicast address.

+  @retval EFI_ALREADY_STARTED    The group address is already in the group table

+                                 (when JoinFlag is TRUE).

+  @retval EFI_NOT_FOUND          The group address is not in the group table (when

+                                 JoinFlag is FALSE).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Groups (

+  IN EFI_UDP6_PROTOCOL  *This,

+  IN BOOLEAN            JoinFlag,

+  IN EFI_IPv6_ADDRESS   *MulticastAddress OPTIONAL

+  )

+{

+  EFI_STATUS          Status;

+  UDP6_INSTANCE_DATA  *Instance;

+  EFI_IP6_PROTOCOL    *Ip;

+  EFI_TPL             OldTpl;

+  EFI_IPv6_ADDRESS    *McastIp;

+

+  if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  McastIp = NULL;

+

+  if (JoinFlag) {

+    if (!IP6_IS_MULTICAST (MulticastAddress)) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    McastIp = AllocateCopyPool (sizeof (EFI_IPv6_ADDRESS), MulticastAddress);

+    if (McastIp == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  }

+

+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);

+  if (!Instance->Configured) {

+    return EFI_NOT_STARTED;

+  }

+

+  Ip      = Instance->IpInfo->Ip.Ip6;

+

+  OldTpl  = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // Invoke the Ip instance the Udp6 instance consumes to do the group operation.

+  //

+  Status = Ip->Groups (Ip, JoinFlag, MulticastAddress);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Keep a local copy of the configured multicast IPs because IpIo receives

+  // datagrams from the 0 station address IP instance and then UDP delivers to

+  // the matched instance. This copy of multicast IPs is used to avoid receive

+  // the mutlicast datagrams destinated to multicast IPs the other instances configured.

+  //

+  if (JoinFlag) {

+

+    Status = NetMapInsertTail (&Instance->McastIps, (VOID *) McastIp, NULL);

+  } else {

+

+    NetMapIterate (&Instance->McastIps, Udp6LeaveGroup, MulticastAddress);

+  }

+

+ON_EXIT:

+

+  gBS->RestoreTPL (OldTpl);

+

+  if (EFI_ERROR (Status)) {

+    if (McastIp != NULL) {

+      FreePool (McastIp);

+    }

+  }

+

+  return Status;

+}

+

+

+

+/**

+  This function places a sending request to this instance of the EFI UDPv6 Protocol,

+  alongside the transmit data that was filled by the user.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to the completion token that will be

+                                 placed into the transmit queue.

+

+  @retval EFI_SUCCESS            The data was queued for transmission.

+  @retval EFI_NOT_STARTED        This EFI UDPv6 Protocol instance has not been

+                                 started.

+  @retval EFI_NO_MAPPING         The under-layer IPv6 driver was responsible for

+                                 choosing a source address for this instance, but

+                                 no  source address was available for use.

+  @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:

+                                 This is NULL.

+                                 Token is NULL. Token.Event is NULL.

+                                 Token.Packet.TxData is NULL.

+                                 Token.Packet.TxData.FragmentCount is zero.

+                                 Token.Packet.TxData.DataLength is not equal to the

+                                 sum of fragment lengths.

+                                 One or more of the

+                                 Token.Packet.TxData.FragmentTable[].FragmentLength

+                                 fields is zero.

+                                 One or more of the

+                                 Token.Packet.TxData.FragmentTable[].FragmentBuffer

+                                 fields is NULL. One or more of the

+                                 Token.Packet.TxData.UdpSessionData.DestinationAddres

+                                 are not valid unicast IPv6

+                                 addresses if the  UdpSessionData is not NULL.

+                                 Token.Packet.TxData.UdpSessionData.

+                                 DestinationAddress is NULL

+                                 Token.Packet.TxData.UdpSessionData.

+                                 DestinatioPort

+                                 is zero.

+                                 Token.Packet.TxData.UdpSessionData is NULL and this

+                                 instance's UdpConfigData.RemoteAddress  is unspecified.

+  @retval EFI_ACCESS_DENIED      The transmit completion token with the same

+                                 Token.Event is already in the transmit queue.

+  @retval EFI_NOT_READY          The completion token could not be queued because

+                                 the transmit queue is full.

+  @retval EFI_OUT_OF_RESOURCES   Could not queue the transmit data.

+  @retval EFI_NOT_FOUND          There is no route to the destination network or

+                                 address.

+  @retval EFI_BAD_BUFFER_SIZE    The data length is greater than the maximum UDP

+                                 packet size. Or, the length of the IP header + UDP

+                                 header + data length is greater than MTU if

+                                 DoNotFragment is TRUE.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Transmit (

+  IN EFI_UDP6_PROTOCOL          *This,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token

+  )

+{

+  EFI_STATUS              Status;

+  UDP6_INSTANCE_DATA      *Instance;

+  EFI_TPL                 OldTpl;

+  NET_BUF                 *Packet;

+  EFI_UDP_HEADER          *Udp6Header;

+  EFI_UDP6_CONFIG_DATA    *ConfigData;

+  EFI_IPv6_ADDRESS        Source;

+  EFI_IPv6_ADDRESS        Destination;

+  EFI_UDP6_TRANSMIT_DATA  *TxData;

+  EFI_UDP6_SESSION_DATA   *UdpSessionData;

+  UDP6_SERVICE_DATA       *Udp6Service;

+  IP_IO_OVERRIDE          Override;

+  UINT16                  HeadSum;

+  EFI_IP_ADDRESS          IpDestAddr;

+

+  if ((This == NULL) || (Token == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);

+

+  if (!Instance->Configured) {

+    return EFI_NOT_STARTED;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // Validate the Token, if the token is invalid return the error code.

+  //

+  Status = Udp6ValidateTxToken (Instance, Token);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token)) ||

+      EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token))

+      ){

+    //

+    // Try to find a duplicate token in the two token maps, if found, return

+    // EFI_ACCESS_DENIED.

+    //

+    Status = EFI_ACCESS_DENIED;

+    goto ON_EXIT;

+  }

+

+  TxData = Token->Packet.TxData;

+

+  //

+  // Create a net buffer to hold the user buffer and the udp header.

+  //

+  Packet = NetbufFromExt (

+             (NET_FRAGMENT *) TxData->FragmentTable,

+             TxData->FragmentCount,

+             UDP6_HEADER_SIZE,

+             0,

+             Udp6NetVectorExtFree,

+             NULL

+             );

+  if (Packet == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  //

+  // Store the IpIo in ProtoData.

+  //

+  Udp6Service                        = Instance->Udp6Service;

+  *((UINTN *) &Packet->ProtoData[0]) = (UINTN) (Udp6Service->IpIo);

+

+  Udp6Header = (EFI_UDP_HEADER *) NetbufAllocSpace (Packet, UDP6_HEADER_SIZE, TRUE);

+  ASSERT (Udp6Header != NULL);

+  ConfigData = &Instance->ConfigData;

+

+  //

+  // Fill the udp header.

+  //

+  Udp6Header->SrcPort      = HTONS (ConfigData->StationPort);

+  Udp6Header->DstPort      = HTONS (ConfigData->RemotePort);

+  Udp6Header->Length       = HTONS ((UINT16) Packet->TotalSize);

+  Udp6Header->Checksum     = 0;

+  //

+  // Set the UDP Header in NET_BUF, this UDP header is for IP6 can fast get the

+  // Udp header for pseudoHeadCheckSum.

+  //

+  Packet->Udp    = Udp6Header;

+  UdpSessionData = TxData->UdpSessionData;

+

+  if (UdpSessionData != NULL) {

+    //

+    // Set the Destination according to the specified

+    // UdpSessionData.

+    //

+

+    if (UdpSessionData->DestinationPort != 0) {

+      Udp6Header->DstPort = HTONS (UdpSessionData->DestinationPort);

+    }

+

+    IP6_COPY_ADDRESS (&Source, &ConfigData->StationAddress);

+    if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress)) {

+      IP6_COPY_ADDRESS (&Destination, &UdpSessionData->DestinationAddress);

+    } else {

+      IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress);

+    }

+

+    //

+    //Calculate the pseudo head checksum using the overridden parameters.

+    //

+    if (!NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress)) {

+      HeadSum = NetIp6PseudoHeadChecksum (

+                  &Source,

+                  &Destination,

+                  EFI_IP_PROTO_UDP,

+                  0

+                  );

+

+      //

+      // calculate the checksum.

+      //

+      Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum);

+      if (Udp6Header->Checksum == 0) {

+        //

+        // If the calculated checksum is 0, fill the Checksum field with all ones.

+        //

+        Udp6Header->Checksum = 0XFFFF;

+      }

+    } else {

+      //

+      // Set the checksum is zero if the ConfigData->StationAddress is unspcified

+      // and the Ipv6 will fill the correct value of this checksum.

+      //

+      Udp6Header->Checksum = 0;

+

+    }

+  } else {

+    //

+    // UdpSessionData is NULL, use the address and port information previously configured.

+    //

+    IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress);

+

+    HeadSum = Instance->HeadSum;

+    //

+    // calculate the checksum.

+    //

+    Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum);

+    if (Udp6Header->Checksum == 0) {

+      //

+      // If the calculated checksum is 0, fill the Checksum field with all ones.

+      //

+      Udp6Header->Checksum = 0xffff;

+    }

+  }

+

+

+

+  //

+  // Fill the IpIo Override data.

+  //

+  Override.Ip6OverrideData.Protocol  = EFI_IP_PROTO_UDP;

+  Override.Ip6OverrideData.HopLimit  = ConfigData->HopLimit;

+  Override.Ip6OverrideData.FlowLabel = 0;

+

+  //

+  // Save the token into the TxToken map.

+  //

+  Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet);

+  if (EFI_ERROR (Status)) {

+    goto FREE_PACKET;

+  }

+

+  //

+  // Send out this datagram through IpIo.

+  //

+  if (UdpSessionData != NULL){

+    IP6_COPY_ADDRESS (&(IpDestAddr.v6), &Destination);

+  } else {

+    ZeroMem (&IpDestAddr.v6, sizeof (EFI_IPv6_ADDRESS));

+  }

+

+  Status = IpIoSend (

+             Udp6Service->IpIo,

+             Packet,

+             Instance->IpInfo,

+             Instance,

+             Token,

+             &IpDestAddr,

+             &Override

+             );

+  if (EFI_ERROR (Status)) {

+    //

+    // Remove this token from the TxTokens.

+    //

+    Udp6RemoveToken (&Instance->TxTokens, Token);

+  }

+

+FREE_PACKET:

+

+  NetbufFree (Packet);

+

+ON_EXIT:

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  This function places a completion token into the receive packet queue. This function

+  is always asynchronous.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to a token that is associated with the

+                                 receive data descriptor.

+

+  @retval EFI_SUCCESS            The receive completion token was cached.

+  @retval EFI_NOT_STARTED        This EFI UDPv6 Protocol instance has not been

+                                 started.

+  @retval EFI_NO_MAPPING         When using a default address, configuration (DHCP,

+                                 BOOTP, RARP, etc.) is not finished yet.

+  @retval EFI_INVALID_PARAMETER  One or more of the following conditions is TRUE:

+                                 This is NULL. Token is NULL. Token.Event is NULL.

+  @retval EFI_OUT_OF_RESOURCES   The receive completion token could not be queued

+                                 due to a lack of system resources (usually

+                                 memory).

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+                                 The EFI UDPv6 Protocol instance has been reset to

+                                 startup defaults.

+  @retval EFI_ACCESS_DENIED      A receive completion token with the same

+                                 Token.Event is already in the receive queue.

+  @retval EFI_NOT_READY          The receive request could not be queued because

+                                 the receive  queue is full.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Receive (

+  IN EFI_UDP6_PROTOCOL          *This,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token

+  )

+{

+  EFI_STATUS          Status;

+  UDP6_INSTANCE_DATA  *Instance;

+  EFI_TPL             OldTpl;

+

+  if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);

+

+  if (!Instance->Configured) {

+    return EFI_NOT_STARTED;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token)) ||

+      EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token))

+      ){

+    //

+    // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or

+    // RxTokens map.

+    //

+    Status = EFI_ACCESS_DENIED;

+    goto ON_EXIT;

+  }

+

+  Token->Packet.RxData = NULL;

+

+  //

+  // Save the token into the RxTokens map.

+  //

+  Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL);

+  if (EFI_ERROR (Status)) {

+    Status = EFI_NOT_READY;

+    goto ON_EXIT;

+  }

+

+  //

+  // If there is an icmp error, report it.

+  //

+  Udp6ReportIcmpError (Instance);

+

+  //

+  // Try to delivered the received datagrams.

+  //

+  Udp6InstanceDeliverDgram (Instance);

+

+  //

+  // Dispatch the DPC queued by the NotifyFunction of Token->Event.

+  //

+  DispatchDpc ();

+

+ON_EXIT:

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  This function is used to abort a pending transmit or receive request.

+

+  @param[in]  This               Pointer to the EFI_UDP6_PROTOCOL instance.

+  @param[in]  Token              Pointer to a token that has been issued by

+                                 EFI_UDP6_PROTOCOL.Transmit() or

+                                 EFI_UDP6_PROTOCOL.Receive(). This parameter is

+                                 optional and may be NULL.

+

+  @retval EFI_SUCCESS            The asynchronous I/O request was aborted, and

+                                 Token.Event was  signaled. When Token is NULL, all

+                                 pending requests are aborted and their events are

+                                 signaled.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_NOT_STARTED        This instance has not been started.

+  @retval EFI_NO_MAPPING         When using the default address, configuration

+                                 (DHCP, BOOTP, RARP, etc.) is not finished yet.

+  @retval EFI_NOT_FOUND          When Token is not NULL, the asynchronous I/O

+                                 request is not found in the transmit or receive

+                                 queue. It is either completed or not issued by

+                                 Transmit() or Receive().

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Cancel (

+  IN EFI_UDP6_PROTOCOL          *This,

+  IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL

+  )

+{

+  EFI_STATUS          Status;

+  UDP6_INSTANCE_DATA  *Instance;

+  EFI_TPL             OldTpl;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);

+

+  if (!Instance->Configured) {

+    return EFI_NOT_STARTED;

+  }

+

+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

+

+  //

+  // Cancle the tokens specified by Token for this instance.

+  //

+  Status = Udp6InstanceCancelToken (Instance, Token);

+

+  //

+  // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.

+  //

+  DispatchDpc ();

+

+  gBS->RestoreTPL (OldTpl);

+

+  return Status;

+}

+

+

+/**

+  This function can be used by network drivers and applications to increase the rate that

+  data packets are moved between the communications device and the transmit/receive queues.

+

+  @param[in] This                Pointer to the EFI_UDP6_PROTOCOL instance.

+

+  @retval EFI_SUCCESS            Incoming or outgoing data was processed.

+  @retval EFI_INVALID_PARAMETER  This is NULL.

+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.

+  @retval EFI_TIMEOUT            Data was dropped out of the transmit and/or

+                                 receive queue.

+

+**/

+EFI_STATUS

+EFIAPI

+Udp6Poll (

+  IN EFI_UDP6_PROTOCOL  *This

+  )

+{

+  UDP6_INSTANCE_DATA  *Instance;

+  EFI_IP6_PROTOCOL    *Ip;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);

+  Ip       = Instance->IpInfo->Ip.Ip6;

+

+  //

+  // Invode the Ip instance consumed by the udp instance to do the poll operation.

+  //

+  return Ip->Poll (Ip);

+}

diff --git a/NetworkPkg/UefiPxeBcDxe/ComponentName.c b/NetworkPkg/UefiPxeBcDxe/ComponentName.c
new file mode 100644
index 0000000..4228519
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/ComponentName.c
@@ -0,0 +1,312 @@
+/** @file

+  UEFI Component Name(2) protocol implementation for UefiPxeBc driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "PxeBcImpl.h"

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  );

+

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **ControllerName

+  );

+

+

+//

+// EFI Component Name Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL    gPxeBcComponentName  = {

+  PxeBcComponentNameGetDriverName,

+  PxeBcComponentNameGetControllerName,

+  "eng"

+};

+

+//

+// EFI Component Name 2 Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL   gPxeBcComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PxeBcComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PxeBcComponentNameGetControllerName,

+  "en"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE       mPxeBcDriverNameTable[] = {

+  {

+    "eng;en",

+    L"UEFI PXE Base Code Driver"

+  },

+  {

+    NULL,

+    NULL

+  }

+};

+

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the driver.

+

+  This function retrieves the user-readable name of a driver in the form of a

+  Unicode string. If the driver specified by This has a user-readable name in

+  the language specified by Language, then a pointer to the driver name is

+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified

+  by This does not support the language specified by Language,

+  then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language. This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified

+                                in RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  DriverName       A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                driver specified by This in the language

+                                specified by Language.

+

+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by

+                                This and the language specified by Language was

+                                returned in DriverName.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER DriverName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **DriverName

+  )

+{

+  return LookupUnicodeString2(

+           Language,

+           This->SupportedLanguages,

+           mPxeBcDriverNameTable,

+           DriverName,

+           (BOOLEAN)(This == &gPxeBcComponentName)

+           );

+}

+

+

+/**

+  Retrieves a Unicode string that is the user-readable name of the controller

+  that is being managed by a driver.

+

+  This function retrieves the user-readable name of the controller specified by

+  ControllerHandle and ChildHandle in the form of a Unicode string. If the

+  driver specified by This has a user-readable name in the language specified by

+  Language, then a pointer to the controller name is returned in ControllerName,

+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently

+  managing the controller specified by ControllerHandle and ChildHandle,

+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not

+  support the language specified by Language, then EFI_UNSUPPORTED is returned.

+

+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or

+                                EFI_COMPONENT_NAME_PROTOCOL instance.

+

+  @param[in]  ControllerHandle  The handle of a controller that the driver

+                                specified by This is managing.  This handle

+                                specifies the controller whose name is to be

+                                returned.

+

+  @param[in]  ChildHandle       The handle of the child controller to retrieve

+                                the name of.  This is an optional parameter that

+                                may be NULL.  It will be NULL for device

+                                drivers.  It will also be NULL for a bus drivers

+                                that wish to retrieve the name of the bus

+                                controller.  It will not be NULL for a bus

+                                driver that wishes to retrieve the name of a

+                                child controller.

+

+  @param[in]  Language          A pointer to a Null-terminated ASCII string

+                                array indicating the language.  This is the

+                                language of the driver name that the caller is

+                                requesting, and it must match one of the

+                                languages specified in SupportedLanguages. The

+                                number of languages supported by a driver is up

+                                to the driver writer. Language is specified in

+                                RFC 4646 or ISO 639-2 language code format.

+

+  @param[out]  ControllerName   A pointer to the Unicode string to return.

+                                This Unicode string is the name of the

+                                controller specified by ControllerHandle and

+                                ChildHandle in the language specified by

+                                Language from the point of view of the driver

+                                specified by This.

+

+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in

+                                the language specified by Language for the

+                                driver specified by This was returned in

+                                DriverName.

+

+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid

+                                EFI_HANDLE.

+

+  @retval EFI_INVALID_PARAMETER Language is NULL.

+

+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently

+                                managing the controller specified by

+                                ControllerHandle and ChildHandle.

+

+  @retval EFI_UNSUPPORTED       The driver specified by This does not support

+                                the language specified by Language.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,

+  IN  EFI_HANDLE                   ControllerHandle,

+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,

+  IN  CHAR8                        *Language,

+  OUT CHAR16                       **ControllerName

+  )

+{

+  return EFI_UNSUPPORTED;

+}

+

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
new file mode 100644
index 0000000..dd1d76d
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
@@ -0,0 +1,1170 @@
+/** @file

+  Boot functions implementation for UefiPxeBc Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "PxeBcImpl.h"

+

+

+/**

+  Display the string of the boot item.

+

+  If the length of the boot item string beyond 70 Char, just display 70 Char.

+

+  @param[in]  Str           The pointer to the string.

+  @param[in]  Len           The length of the string.

+

+**/

+VOID

+PxeBcDisplayBootItem (

+  IN UINT8                 *Str,

+  IN UINT8                 Len

+  )

+{

+  UINT8                    Tmp;

+

+  //

+  // Cut off the chars behind 70th.

+  //

+  Len       = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);

+  Tmp       = Str[Len];

+  Str[Len]  = 0;

+  AsciiPrint ("%a \n", Str);

+

+  //

+  // Restore the original 70th char.

+  //

+  Str[Len]  = Tmp;

+}

+

+

+/**

+  Select and maintain the boot prompt if needed.

+

+  @param[in]  Private          Pointer to PxeBc private data.

+

+  @retval EFI_SUCCESS          Selected boot prompt done.

+  @retval EFI_TIMEOUT          Selected boot prompt timed out.

+  @retval EFI_NOT_FOUND        The proxy offer is not Pxe10.

+  @retval EFI_ABORTED          User cancelled the operation.

+  @retval EFI_NOT_READY        Reading the input key from the keyboard has not finish.

+

+**/

+EFI_STATUS

+PxeBcSelectBootPrompt (

+  IN PXEBC_PRIVATE_DATA      *Private

+  )

+{

+  PXEBC_DHCP_PACKET_CACHE    *Cache;

+  PXEBC_VENDOR_OPTION        *VendorOpt;

+  EFI_PXE_BASE_CODE_MODE     *Mode;

+  EFI_EVENT                  TimeoutEvent;

+  EFI_EVENT                  DescendEvent;

+  EFI_INPUT_KEY              InputKey;

+  EFI_STATUS                 Status;

+  UINT32                     OfferType;

+  UINT8                      Timeout;

+  UINT8                      *Prompt;

+  UINT8                      PromptLen;

+  INT32                      SecCol;

+  INT32                      SecRow;

+

+  TimeoutEvent = NULL;

+  DescendEvent = NULL;

+  Mode         = Private->PxeBc.Mode;

+  Cache        = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;

+  OfferType    = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;

+

+  //

+  // Only ProxyPxe10 offer needs boot prompt.

+  //

+  if (OfferType != PxeOfferTypeProxyPxe10) {

+    return EFI_NOT_FOUND;

+  }

+

+  //

+  // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.

+  //

+  ASSERT (!Mode->UsingIpv6);

+

+  VendorOpt = &Cache->Dhcp4.VendorOpt;

+  if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {

+    return EFI_SUCCESS;

+  }

+

+  Timeout   = VendorOpt->MenuPrompt->Timeout;

+  Prompt    = VendorOpt->MenuPrompt->Prompt;

+  PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);

+

+  //

+  // The valid scope of Timeout refers to PXE2.1 spec.

+  //

+  if (Timeout == 0) {

+    return EFI_SUCCESS;

+  }

+  if (Timeout == 255) {

+    return EFI_TIMEOUT;

+  }

+

+  //

+  // Create and start a timer as timeout event.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_TIMER,

+                  TPL_CALLBACK,

+                  NULL,

+                  NULL,

+                  &TimeoutEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->SetTimer (

+                  TimeoutEvent,

+                  TimerRelative,

+                  Timeout * TICKS_PER_SECOND

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Create and start a periodic timer as descend event by second.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_TIMER,

+                  TPL_CALLBACK,

+                  NULL,

+                  NULL,

+                  &DescendEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  Status = gBS->SetTimer (

+                  DescendEvent,

+                  TimerPeriodic,

+                  TICKS_PER_SECOND

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Display the boot item and cursor on the screen.

+  //

+  SecCol = gST->ConOut->Mode->CursorColumn;

+  SecRow = gST->ConOut->Mode->CursorRow;

+

+  PxeBcDisplayBootItem (Prompt, PromptLen);

+

+  gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);

+  AsciiPrint ("(%d) ", Timeout--);

+

+  while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {

+    if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {

+      gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);

+      AsciiPrint ("(%d) ", Timeout--);

+    }

+    if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {

+      gBS->Stall (10 * TICKS_PER_MS);

+      continue;

+    }

+    //

+    // Parse the input key by user.

+    //

+    if (InputKey.ScanCode == 0) {

+

+      switch (InputKey.UnicodeChar) {

+

+      case CTRL ('c'):

+        Status = EFI_ABORTED;

+        break;

+

+      case CTRL ('m'):

+      case 'm':

+      case 'M':

+        Status = EFI_TIMEOUT;

+        break;

+

+      default:

+        continue;

+      }

+

+    } else {

+

+      switch (InputKey.ScanCode) {

+

+      case SCAN_F8:

+        Status = EFI_TIMEOUT;

+        break;

+

+      case SCAN_ESC:

+        Status = EFI_ABORTED;

+        break;

+

+      default:

+        continue;

+      }

+    }

+

+    break;

+  }

+

+  //

+  // Reset the cursor on the screen.

+  //

+  gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);

+

+ON_EXIT:

+  if (DescendEvent != NULL) {

+    gBS->CloseEvent (DescendEvent);

+  }

+  if (TimeoutEvent != NULL) {

+    gBS->CloseEvent (TimeoutEvent);

+  }

+

+  return Status;

+}

+

+

+/**

+  Select the boot menu by user's input.

+

+  @param[in]  Private         Pointer to PxeBc private data.

+  @param[out] Type            The type of the menu.

+  @param[in]  UseDefaultItem  Use default item or not.

+

+  @retval EFI_ABORTED     User cancel operation.

+  @retval EFI_SUCCESS     Select the boot menu success.

+  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.

+

+**/

+EFI_STATUS

+PxeBcSelectBootMenu (

+  IN  PXEBC_PRIVATE_DATA              *Private,

+  OUT UINT16                          *Type,

+  IN  BOOLEAN                         UseDefaultItem

+  )

+{

+  EFI_PXE_BASE_CODE_MODE     *Mode;

+  PXEBC_DHCP_PACKET_CACHE    *Cache;

+  PXEBC_VENDOR_OPTION        *VendorOpt;

+  EFI_INPUT_KEY              InputKey;

+  UINT32                     OfferType;

+  UINT8                      MenuSize;

+  UINT8                      MenuNum;

+  INT32                      TopRow;

+  UINT16                     Select;

+  UINT16                     LastSelect;

+  UINT8                      Index;

+  BOOLEAN                    Finish;

+  CHAR8                      Blank[PXEBC_DISPLAY_MAX_LINE];

+  PXEBC_BOOT_MENU_ENTRY      *MenuItem;

+  PXEBC_BOOT_MENU_ENTRY      *MenuArray[PXEBC_MENU_MAX_NUM];

+

+  Finish    = FALSE;

+  Select    = 1;

+  Index     = 0;

+  *Type     = 0;

+  Mode      = Private->PxeBc.Mode;

+  Cache     = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;

+  OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;

+

+  //

+  // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.

+  //

+  ASSERT (!Mode->UsingIpv6);

+  ASSERT (OfferType == PxeOfferTypeProxyPxe10);

+

+  VendorOpt = &Cache->Dhcp4.VendorOpt;

+  if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Display the boot menu on the screen.

+  //

+  SetMem (Blank, sizeof(Blank), ' ');

+

+  MenuSize  = VendorOpt->BootMenuLen;

+  MenuItem  = VendorOpt->BootMenu;

+

+  if (MenuSize == 0) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {

+    ASSERT (MenuItem != NULL);

+    MenuArray[Index]  = MenuItem;

+    MenuSize          = (UINT8) (MenuSize - (MenuItem->DescLen + 3));

+    MenuItem          = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);

+    Index++;

+  }

+

+  if (UseDefaultItem) {

+    ASSERT (MenuArray[0] != NULL);

+    CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));

+    *Type = NTOHS (*Type);

+    return EFI_SUCCESS;

+  }

+

+  MenuNum = Index;

+

+  for (Index = 0; Index < MenuNum; Index++) {

+    ASSERT (MenuArray[Index] != NULL);

+    PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);

+  }

+

+  TopRow = gST->ConOut->Mode->CursorRow - MenuNum;

+

+  //

+  // Select the boot item by user in the boot menu.

+  //

+  do {

+    //

+    // Highlight selected row.

+    //

+    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));

+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);

+    ASSERT (Select < PXEBC_MENU_MAX_NUM);

+    ASSERT (MenuArray[Select] != NULL);

+    Blank[MenuArray[Select]->DescLen] = 0;

+    AsciiPrint ("%a\r", Blank);

+    PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);

+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);

+    LastSelect = Select;

+

+    while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {

+      gBS->Stall (10 * TICKS_PER_MS);

+    }

+

+    if (InputKey.ScanCode != 0) {

+      switch (InputKey.UnicodeChar) {

+      case CTRL ('c'):

+        InputKey.ScanCode = SCAN_ESC;

+        break;

+

+      case CTRL ('j'):  /* linefeed */

+      case CTRL ('m'):  /* return */

+        Finish = TRUE;

+        break;

+

+      case CTRL ('i'):  /* tab */

+      case ' ':

+      case 'd':

+      case 'D':

+        InputKey.ScanCode = SCAN_DOWN;

+        break;

+

+      case CTRL ('h'):  /* backspace */

+      case 'u':

+      case 'U':

+        InputKey.ScanCode = SCAN_UP;

+        break;

+

+      default:

+        InputKey.ScanCode = 0;

+      }

+    }

+

+    switch (InputKey.ScanCode) {

+    case SCAN_LEFT:

+    case SCAN_UP:

+      if (Select != 0) {

+        Select--;

+      }

+      break;

+

+    case SCAN_DOWN:

+    case SCAN_RIGHT:

+      if (++Select == MenuNum) {

+        Select--;

+      }

+      break;

+

+    case SCAN_PAGE_UP:

+    case SCAN_HOME:

+      Select = 0;

+      break;

+

+    case SCAN_PAGE_DOWN:

+    case SCAN_END:

+      Select = (UINT16) (MenuNum - 1);

+      break;

+

+    case SCAN_ESC:

+      return EFI_ABORTED;

+    }

+

+    //

+    // Unhighlight the last selected row.

+    //

+    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));

+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);

+    ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);

+    ASSERT (MenuArray[LastSelect] != NULL);

+    Blank[MenuArray[LastSelect]->DescLen] = 0;

+    AsciiPrint ("%a\r", Blank);

+    PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);

+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);

+  } while (!Finish);

+

+  //

+  // Swap the byte order.

+  //

+  ASSERT (Select < PXEBC_MENU_MAX_NUM);

+  ASSERT (MenuArray[Select] != NULL);

+  CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));

+  *Type = NTOHS (*Type);

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Parse out the boot information from the last Dhcp4 reply packet.

+

+  @param[in, out] Private      Pointer to PxeBc private data.

+  @param[out]     BufferSize   Size of the boot file to be downloaded.

+

+  @retval EFI_SUCCESS          Successfully parsed out all the boot information.

+  @retval Others               Failed to parse out the boot information.

+

+**/

+EFI_STATUS

+PxeBcDhcp4BootInfo (

+  IN OUT PXEBC_PRIVATE_DATA   *Private,

+     OUT UINT64               *BufferSize

+  )

+{

+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;

+  EFI_PXE_BASE_CODE_MODE      *Mode;

+  EFI_STATUS                  Status;

+  PXEBC_DHCP4_PACKET_CACHE    *Cache4;

+  UINT16                      Value;

+

+  PxeBc       = &Private->PxeBc;

+  Mode        = PxeBc->Mode;

+  Status      = EFI_SUCCESS;

+  *BufferSize = 0;

+

+  //

+  // Get the last received Dhcp4 reply packet.

+  //

+  if (Mode->PxeReplyReceived) {

+    Cache4 = &Private->PxeReply.Dhcp4;

+  } else if (Mode->ProxyOfferReceived) {

+    Cache4 = &Private->ProxyOffer.Dhcp4;

+  } else {

+    Cache4 = &Private->DhcpAck.Dhcp4;

+  }

+

+  //

+  // Parse the boot server Ipv4 address by next server address.

+  // If this field isn't available, use option 54 instead.

+  //

+  CopyMem (

+    &Private->ServerIp,

+    &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,

+    sizeof (EFI_IPv4_ADDRESS)

+    );

+

+  if (Private->ServerIp.Addr[0] == 0) {

+    CopyMem (

+      &Private->ServerIp,

+      Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,

+      sizeof (EFI_IPv4_ADDRESS)

+      );

+  }

+

+  //

+  // Parse the boot file name by option.

+  //

+  ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);

+  Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;

+

+  if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {

+    //

+    // Parse the boot file size by option.

+    //

+    CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));

+    Value       = NTOHS (Value);

+    //

+    // The field of boot file size is 512 bytes in unit.

+    //

+    *BufferSize = 512 * Value;

+  } else {

+    //

+    // Get the bootfile size by tftp command if no option available.

+    //

+    Status = PxeBc->Mtftp (

+                      PxeBc,

+                      EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,

+                      NULL,

+                      FALSE,

+                      BufferSize,

+                      &Private->BlockSize,

+                      &Private->ServerIp,

+                      Private->BootFileName,

+                      NULL,

+                      FALSE

+                      );

+  }

+

+  //

+  // Save the value of boot file size.

+  //

+  Private->BootFileSize = (UINTN) *BufferSize;

+

+  //

+  // Display all the information: boot server address, boot file name and boot file size.

+  //

+  AsciiPrint ("\n  Server IP address is ");

+  PxeBcShowIp4Addr (&Private->ServerIp.v4);

+  AsciiPrint ("\n  NBP filename is %a", Private->BootFileName);

+  AsciiPrint ("\n  NBP filesize is %d Bytes", Private->BootFileSize);

+

+  return Status;

+}

+

+

+/**

+  Parse out the boot information from the last Dhcp6 reply packet.

+

+  @param[in, out] Private      Pointer to PxeBc private data.

+  @param[out]     BufferSize   Size of the boot file to be downloaded.

+

+  @retval EFI_SUCCESS          Successfully parsed out all the boot information.

+  @retval EFI_BUFFER_TOO_SMALL

+  @retval Others               Failed to parse out the boot information.

+

+**/

+EFI_STATUS

+PxeBcDhcp6BootInfo (

+  IN OUT PXEBC_PRIVATE_DATA   *Private,

+     OUT UINT64               *BufferSize

+  )

+{

+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;

+  EFI_PXE_BASE_CODE_MODE      *Mode;

+  EFI_STATUS                  Status;

+  PXEBC_DHCP6_PACKET_CACHE    *Cache6;

+  UINT16                      Value;

+

+  PxeBc       = &Private->PxeBc;

+  Mode        = PxeBc->Mode;

+  Status      = EFI_SUCCESS;

+  *BufferSize = 0;

+

+  //

+  // Get the last received Dhcp6 reply packet.

+  //

+  if (Mode->PxeReplyReceived) {

+    Cache6 = &Private->PxeReply.Dhcp6;

+  } else if (Mode->ProxyOfferReceived) {

+    Cache6 = &Private->ProxyOffer.Dhcp6;

+  } else {

+    Cache6 = &Private->DhcpAck.Dhcp6;

+  }

+

+  ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);

+

+  //

+  // Parse (m)tftp server ip address and bootfile name.

+  //

+  Status = PxeBcExtractBootFileUrl (

+             &Private->BootFileName,

+             &Private->ServerIp.v6,

+             (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),

+             NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Parse the value of boot file size.

+  //

+  if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {

+    //

+    // Parse it out if have the boot file parameter option.

+    //

+    Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+    //

+    // The field of boot file size is 512 bytes in unit.

+    //

+    *BufferSize = 512 * Value;

+  } else {

+    //

+    // Send get file size command by tftp if option unavailable.

+    //

+    Status = PxeBc->Mtftp (

+                      PxeBc,

+                      EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,

+                      NULL,

+                      FALSE,

+                      BufferSize,

+                      &Private->BlockSize,

+                      &Private->ServerIp,

+                      Private->BootFileName,

+                      NULL,

+                      FALSE

+                      );

+  }

+

+  //

+  // Save the value of boot file size.

+  //

+  Private->BootFileSize = (UINTN) *BufferSize;

+

+  //

+  // Display all the information: boot server address, boot file name and boot file size.

+  //

+  AsciiPrint ("\n  Server IP address is ");

+  PxeBcShowIp6Addr (&Private->ServerIp.v6);

+  AsciiPrint ("\n  NBP filename is %a", Private->BootFileName);

+  AsciiPrint ("\n  NBP filesize is %d Bytes", Private->BootFileSize);

+

+  return Status;

+}

+

+

+/**

+  Extract the discover information and boot server entry from the

+  cached packets if unspecified.

+

+  @param[in]      Private      Pointer to PxeBc private data.

+  @param[in]      Type         The type of bootstrap to perform.

+  @param[in, out] Info         Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.

+  @param[out]     BootEntry    Pointer to PXEBC_BOOT_SVR_ENTRY.

+  @param[out]     SrvList      Pointer to EFI_PXE_BASE_CODE_SRVLIST.

+

+  @retval EFI_SUCCESS       Successfully extracted the information.

+  @retval EFI_DEVICE_ERROR  Failed to extract the information.

+

+**/

+EFI_STATUS

+PxeBcExtractDiscoverInfo (

+  IN     PXEBC_PRIVATE_DATA               *Private,

+  IN     UINT16                           Type,

+  IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO  *Info,

+     OUT PXEBC_BOOT_SVR_ENTRY             **BootEntry,

+     OUT EFI_PXE_BASE_CODE_SRVLIST        **SrvList

+  )

+{

+  EFI_PXE_BASE_CODE_MODE          *Mode;

+  PXEBC_DHCP4_PACKET_CACHE        *Cache4;

+  PXEBC_VENDOR_OPTION             *VendorOpt;

+  PXEBC_BOOT_SVR_ENTRY            *Entry;

+  BOOLEAN                         IsFound;

+

+  Mode = Private->PxeBc.Mode;

+

+  if (Mode->UsingIpv6) {

+    Info->IpCnt    = 1;

+    Info->UseUCast = TRUE;

+

+    Info->SrvList[0].Type              = Type;

+    Info->SrvList[0].AcceptAnyResponse = FALSE;

+

+    //

+    // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.

+    //

+    CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));

+

+    *SrvList  = Info->SrvList;

+  } else {

+    Entry     = NULL;

+    IsFound   = FALSE;

+    Cache4    = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;

+    VendorOpt = &Cache4->VendorOpt;

+

+    if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {

+      //

+      // Address is not acquired or no discovery options.

+      //

+      return EFI_INVALID_PARAMETER;

+    }

+

+    //

+    // Parse the boot server entry from the vendor option in the last cached packet.

+    //

+    Info->UseMCast    = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);

+    Info->UseBCast    = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);

+    Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);

+    Info->UseUCast    = Info->MustUseList;

+

+    if (Info->UseMCast) {

+      //

+      // Get the multicast discover ip address from vendor option if has.

+      //

+      CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));

+    }

+

+    Info->IpCnt = 0;

+

+    if (Info->MustUseList) {

+      Entry = VendorOpt->BootSvr;

+

+      while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {

+        if (Entry->Type == HTONS (Type)) {

+          IsFound = TRUE;

+          break;

+        }

+        Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);

+      }

+

+      if (!IsFound) {

+        return EFI_DEVICE_ERROR;

+      }

+

+      Info->IpCnt = Entry->IpCnt;

+    }

+

+    *BootEntry = Entry;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Build the discover packet and send out for boot server.

+

+  @param[in]  Private               Pointer to PxeBc private data.

+  @param[in]  Type                  PxeBc option boot item type.

+  @param[in]  Layer                 Pointer to option boot item layer.

+  @param[in]  UseBis                Use BIS or not.

+  @param[in]  DestIp                Pointer to the destination address.

+  @param[in]  IpCount               The count of the server address.

+  @param[in]  SrvList               Pointer to the server address list.

+

+  @retval     EFI_SUCCESS           Successfully discovered boot file.

+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.

+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.

+  @retval     Others                Failed to discover boot file.

+

+**/

+EFI_STATUS

+PxeBcDiscoverBootServer (

+  IN  PXEBC_PRIVATE_DATA                *Private,

+  IN  UINT16                            Type,

+  IN  UINT16                            *Layer,

+  IN  BOOLEAN                           UseBis,

+  IN  EFI_IP_ADDRESS                    *DestIp,

+  IN  UINT16                            IpCount,

+  IN  EFI_PXE_BASE_CODE_SRVLIST         *SrvList

+  )

+{

+  if (Private->PxeBc.Mode->UsingIpv6) {

+    return PxeBcDhcp6Discover (

+             Private,

+             Type,

+             Layer,

+             UseBis,

+             DestIp

+             );

+  } else {

+    return PxeBcDhcp4Discover (

+             Private,

+             Type,

+             Layer,

+             UseBis,

+             DestIp,

+             IpCount,

+             SrvList

+             );

+  }

+}

+

+

+/**

+  Discover all the boot information for boot file.

+

+  @param[in, out] Private      Pointer to PxeBc private data.

+  @param[out]     BufferSize   Size of the boot file to be downloaded.

+

+  @retval EFI_SUCCESS          Successfully obtained all the boot information .

+  @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.

+  @retval EFI_ABORTED          User cancel current operation.

+  @retval Others               Failed to parse out the boot information.

+

+**/

+EFI_STATUS

+PxeBcDiscoverBootFile (

+  IN OUT PXEBC_PRIVATE_DATA   *Private,

+     OUT UINT64               *BufferSize

+  )

+{

+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;

+  EFI_PXE_BASE_CODE_MODE      *Mode;

+  EFI_STATUS                  Status;

+  UINT16                      Type;

+  UINT16                      Layer;

+  BOOLEAN                     UseBis;

+

+  PxeBc = &Private->PxeBc;

+  Mode  = PxeBc->Mode;

+  Type  = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;

+  Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;

+

+  //

+  // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and

+  // other pxe boot information.

+  //

+  Status = PxeBc->Dhcp (PxeBc, TRUE);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Select a boot server from boot server list.

+  //

+  Status = PxeBcSelectBootPrompt (Private);

+

+  if (Status == EFI_SUCCESS) {

+    //

+    // Choose by user's input.

+    //

+    Status = PxeBcSelectBootMenu (Private, &Type, TRUE);

+  } else if (Status == EFI_TIMEOUT) {

+    //

+    // Choose by default item.

+    //

+    Status = PxeBcSelectBootMenu (Private, &Type, FALSE);

+  }

+

+  if (!EFI_ERROR (Status)) {

+

+    if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {

+      //

+      // Local boot(PXE bootstrap server) need abort

+      //

+      return EFI_ABORTED;

+    }

+

+    //

+    // Start to discover the boot server to get (m)tftp server ip address, bootfile

+    // name and bootfile size.

+    //

+    UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);

+    Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+  }

+

+  //

+  // Parse the boot information.

+  //

+  if (Mode->UsingIpv6) {

+    Status = PxeBcDhcp6BootInfo (Private, BufferSize);

+  } else {

+    Status = PxeBcDhcp4BootInfo (Private, BufferSize);

+  }

+

+  return Status;

+}

+

+

+/**

+  Install PxeBaseCodeCallbackProtocol if not installed before.

+

+  @param[in, out] Private           Pointer to PxeBc private data.

+  @param[out]     NewMakeCallback   If TRUE, it is a new callback.

+                                    Otherwise, it is not new callback.

+  @retval EFI_SUCCESS          PxeBaseCodeCallbackProtocol installed succesfully.

+  @retval Others               Failed to install PxeBaseCodeCallbackProtocol.

+

+**/

+EFI_STATUS

+PxeBcInstallCallback (

+  IN OUT PXEBC_PRIVATE_DATA   *Private,

+     OUT BOOLEAN              *NewMakeCallback

+  )

+{

+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;

+  EFI_STATUS                  Status;

+

+  //

+  // Check whether PxeBaseCodeCallbackProtocol already installed.

+  //

+  PxeBc  = &Private->PxeBc;

+  Status = gBS->HandleProtocol (

+                  Private->Controller,

+                  &gEfiPxeBaseCodeCallbackProtocolGuid,

+                  (VOID **) &Private->PxeBcCallback

+                  );

+  if (Status == EFI_UNSUPPORTED) {

+

+    CopyMem (

+      &Private->LoadFileCallback,

+      &gPxeBcCallBackTemplate,

+      sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)

+      );

+

+    //

+    // Install a default callback if user didn't offer one.

+    //

+    Status = gBS->InstallProtocolInterface (

+                    &Private->Controller,

+                    &gEfiPxeBaseCodeCallbackProtocolGuid,

+                    EFI_NATIVE_INTERFACE,

+                    &Private->LoadFileCallback

+                    );

+

+    (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);

+

+    Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);

+    if (EFI_ERROR (Status)) {

+      PxeBc->Stop (PxeBc);

+      return Status;

+    }

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Uninstall PxeBaseCodeCallbackProtocol.

+

+  @param[in]  Private           Pointer to PxeBc private data.

+  @param[in]  NewMakeCallback   If TRUE, it is a new callback.

+                                Otherwise, it is not new callback.

+

+**/

+VOID

+PxeBcUninstallCallback (

+  IN PXEBC_PRIVATE_DATA        *Private,

+  IN BOOLEAN                   NewMakeCallback

+  )

+{

+  EFI_PXE_BASE_CODE_PROTOCOL   *PxeBc;

+

+  PxeBc = &Private->PxeBc;

+

+  if (NewMakeCallback) {

+

+    NewMakeCallback = FALSE;

+

+    PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);

+

+    gBS->UninstallProtocolInterface (

+          Private->Controller,

+          &gEfiPxeBaseCodeCallbackProtocolGuid,

+          &Private->LoadFileCallback

+          );

+  }

+}

+

+

+/**

+  Download one of boot file in the list, and it's special for IPv6.

+

+  @param[in]      Private           Pointer to PxeBc private data.

+  @param[in, out] BufferSize        Size of user buffer for input;

+                                    required buffer size for output.

+  @param[in]      Buffer            Pointer to user buffer.

+

+  @retval EFI_SUCCESS               Read one of boot file in the list successfully.

+  @retval EFI_BUFFER_TOO_SMALL      The buffer size is not enough for boot file.

+  @retval EFI_NOT_FOUND             There is no proper boot file available.

+  @retval Others                    Failed to download boot file in the list.

+

+**/

+EFI_STATUS

+PxeBcReadBootFileList (

+  IN     PXEBC_PRIVATE_DATA           *Private,

+  IN OUT UINT64                       *BufferSize,

+  IN     VOID                         *Buffer           OPTIONAL

+  )

+{

+  EFI_STATUS                          Status;

+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;

+

+  PxeBc        = &Private->PxeBc;

+

+  //

+  // Try to download the boot file if everything is ready.

+  //

+  if (Buffer != NULL) {

+    Status = PxeBc->Mtftp (

+                      PxeBc,

+                      EFI_PXE_BASE_CODE_TFTP_READ_FILE,

+                      Buffer,

+                      FALSE,

+                      BufferSize,

+                      &Private->BlockSize,

+                      &Private->ServerIp,

+                      Private->BootFileName,

+                      NULL,

+                      FALSE

+                      );

+

+

+  } else {

+    Status      = EFI_BUFFER_TOO_SMALL;

+  }

+

+  return Status;

+}

+

+

+/**

+  Load boot file into user buffer.

+

+  @param[in]      Private           Pointer to PxeBc private data.

+  @param[in, out] BufferSize        Size of user buffer for input;

+                                    required buffer size for output.

+  @param[in]      Buffer            Pointer to user buffer.

+

+  @retval EFI_SUCCESS          Get all the boot information successfully.

+  @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.

+  @retval EFI_ABORTED          User cancelled the current operation.

+  @retval Others               Failed to parse out the boot information.

+

+**/

+EFI_STATUS

+PxeBcLoadBootFile (

+  IN     PXEBC_PRIVATE_DATA           *Private,

+  IN OUT UINTN                        *BufferSize,

+  IN     VOID                         *Buffer         OPTIONAL

+  )

+{

+  BOOLEAN                             NewMakeCallback;

+  UINT64                              RequiredSize;

+  UINT64                              CurrentSize;

+  EFI_STATUS                          Status;

+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;

+  EFI_PXE_BASE_CODE_MODE              *PxeBcMode;

+

+  NewMakeCallback = FALSE;

+  PxeBc           = &Private->PxeBc;

+  PxeBcMode       = &Private->Mode;

+  CurrentSize     = *BufferSize;

+  RequiredSize    = 0;

+

+  //

+  // Install pxebc callback protocol if hasn't been installed yet.

+  //

+  Status = PxeBcInstallCallback (Private, &NewMakeCallback);

+  if (EFI_ERROR(Status)) {

+    return Status;

+  }

+

+  if (Private->BootFileSize == 0) {

+    //

+    // Discover the boot information about the bootfile if hasn't.

+    //

+    Status = PxeBcDiscoverBootFile (Private, &RequiredSize);

+

+    if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {

+      //

+      // It's error if the required buffer size is beyond the system scope.

+      //

+      Status = EFI_DEVICE_ERROR;

+      goto ON_EXIT;

+    } else if (RequiredSize > 0) {

+      //

+      // Get the right buffer size of the bootfile required.

+      //

+      if (CurrentSize < RequiredSize || Buffer == NULL) {

+        //

+        // It's buffer too small if the size of user buffer is smaller than the required.

+        //

+        CurrentSize = RequiredSize;

+        Status      = EFI_BUFFER_TOO_SMALL;

+        goto ON_EXIT;

+      }

+      CurrentSize = RequiredSize;

+    } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {

+      //

+      // Try to download another bootfile in list if failed to get the filesize of the last one.

+      // It's special for the case of IPv6.

+      //

+      Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);

+      goto ON_EXIT;

+    }

+  } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {

+    //

+    // It's buffer too small if the size of user buffer is smaller than the required.

+    //

+    CurrentSize = Private->BootFileSize;

+    Status      = EFI_BUFFER_TOO_SMALL;

+    goto ON_EXIT;

+  }

+

+  //

+  // Begin to download the bootfile if everything is ready.

+  //

+  AsciiPrint ("\n Downloading NBP file...\n");

+  if (PxeBcMode->UsingIpv6) {

+    Status = PxeBcReadBootFileList (

+               Private,

+               &CurrentSize,

+               Buffer

+               );

+  } else {

+    Status = PxeBc->Mtftp (

+                      PxeBc,

+                      EFI_PXE_BASE_CODE_TFTP_READ_FILE,

+                      Buffer,

+                      FALSE,

+                      &CurrentSize,

+                      &Private->BlockSize,

+                      &Private->ServerIp,

+                      Private->BootFileName,

+                      NULL,

+                      FALSE

+                      );

+  }

+

+ON_EXIT:

+  *BufferSize = (UINTN) CurrentSize;

+  PxeBcUninstallCallback(Private, NewMakeCallback);

+

+  if (Status == EFI_SUCCESS) {

+    AsciiPrint ("\n  Succeed to download NBP file.\n");

+    return EFI_SUCCESS;

+  } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {

+    AsciiPrint ("\n  PXE-E05: Buffer size is smaller than the requested file.\n");

+  } else if (Status == EFI_DEVICE_ERROR) {

+    AsciiPrint ("\n  PXE-E07: Network device error.\n");

+  } else if (Status == EFI_OUT_OF_RESOURCES) {

+    AsciiPrint ("\n  PXE-E09: Could not allocate I/O buffers.\n");

+  } else if (Status == EFI_NO_MEDIA) {

+    AsciiPrint ("\n  PXE-E12: Could not detect network connection.\n");

+  } else if (Status == EFI_NO_RESPONSE) {

+    AsciiPrint ("\n  PXE-E16: No offer received.\n");

+  } else if (Status == EFI_TIMEOUT) {

+    AsciiPrint ("\n  PXE-E18: Server response timeout.\n");

+  } else if (Status == EFI_ABORTED) {

+    AsciiPrint ("\n  PXE-E21: Remote boot cancelled.\n");

+  } else if (Status == EFI_ICMP_ERROR) {

+    AsciiPrint ("\n  PXE-E22: Client received ICMP error from server.\n");

+  } else if (Status == EFI_TFTP_ERROR) {

+    AsciiPrint ("\n  PXE-E23: Client received TFTP error from server.\n");

+  } else if (Status != EFI_BUFFER_TOO_SMALL) {

+    AsciiPrint ("\n  PXE-E99: Unexpected network error.\n");

+  }

+

+  return Status;

+}

+

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h
new file mode 100644
index 0000000..ef18907
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h
@@ -0,0 +1,100 @@
+/** @file

+  Boot functions declaration for UefiPxeBc Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_PXEBC_BOOT_H__

+#define __EFI_PXEBC_BOOT_H__

+

+#define PXEBC_DISPLAY_MAX_LINE             70

+#define PXEBC_DEFAULT_UDP_OVERHEAD_SIZE    8

+#define PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE   4

+

+#define PXEBC_IS_SIZE_OVERFLOWED(x)   ((sizeof (UINTN) < sizeof (UINT64)) && ((x) > 0xFFFFFFFF))

+

+

+/**

+  Extract the discover information and boot server entry from the

+  cached packets if unspecified.

+

+  @param[in]      Private      Pointer to PxeBc private data.

+  @param[in]      Type         The type of bootstrap to perform.

+  @param[in, out] Info         Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.

+  @param[out]     BootEntry    Pointer to PXEBC_BOOT_SVR_ENTRY.

+  @param[out]     SrvList      Pointer to EFI_PXE_BASE_CODE_SRVLIST.

+

+  @retval EFI_SUCCESS       Successfully extracted the information.

+  @retval EFI_DEVICE_ERROR  Failed to extract the information.

+

+**/

+EFI_STATUS

+PxeBcExtractDiscoverInfo (

+  IN     PXEBC_PRIVATE_DATA               *Private,

+  IN     UINT16                           Type,

+  IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO  *Info,

+     OUT PXEBC_BOOT_SVR_ENTRY             **BootEntry,

+     OUT EFI_PXE_BASE_CODE_SRVLIST        **SrvList

+  );

+

+

+/**

+  Build the discover packet and send out for boot.

+

+  @param[in]  Private               Pointer to PxeBc private data.

+  @param[in]  Type                  PxeBc option boot item type.

+  @param[in]  Layer                 Pointer to option boot item layer.

+  @param[in]  UseBis                Use BIS or not.

+  @param[in]  DestIp                Pointer to the server address.

+  @param[in]  IpCount               The total count of the server address.

+  @param[in]  SrvList               Pointer to the server address list.

+

+  @retval     EFI_SUCCESS           Successfully discovered boot file.

+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.

+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.

+  @retval     Others                Failed to discover boot file.

+

+**/

+EFI_STATUS

+PxeBcDiscoverBootServer (

+  IN  PXEBC_PRIVATE_DATA                *Private,

+  IN  UINT16                            Type,

+  IN  UINT16                            *Layer,

+  IN  BOOLEAN                           UseBis,

+  IN  EFI_IP_ADDRESS                    *DestIp,

+  IN  UINT16                            IpCount,

+  IN  EFI_PXE_BASE_CODE_SRVLIST         *SrvList

+  );

+

+

+/**

+  Load boot file into user buffer.

+

+  @param[in]      Private           Pointer to PxeBc private data.

+  @param[in, out] BufferSize        Size of user buffer for input;

+                                    required buffer size for output.

+  @param[in]      Buffer            Pointer to user buffer.

+

+  @retval EFI_SUCCESS          Successfully obtained all the boot information.

+  @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.

+  @retval EFI_ABORTED          User cancelled the current operation.

+  @retval Others               Failed to parse out the boot information.

+

+**/

+EFI_STATUS

+PxeBcLoadBootFile (

+  IN     PXEBC_PRIVATE_DATA           *Private,

+  IN OUT UINTN                        *BufferSize,

+  IN     VOID                         *Buffer         OPTIONAL

+  );

+

+#endif

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c
new file mode 100644
index 0000000..08415d9
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c
@@ -0,0 +1,1599 @@
+/** @file

+  Functions implementation related with DHCPv4 for UefiPxeBc Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "PxeBcImpl.h"

+

+//

+// This is a map from the interested DHCP4 option tags' index to the tag value.

+//

+UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {

+  PXEBC_DHCP4_TAG_BOOTFILE_LEN,

+  PXEBC_DHCP4_TAG_VENDOR,

+  PXEBC_DHCP4_TAG_OVERLOAD,

+  PXEBC_DHCP4_TAG_MSG_TYPE,

+  PXEBC_DHCP4_TAG_SERVER_ID,

+  PXEBC_DHCP4_TAG_CLASS_ID,

+  PXEBC_DHCP4_TAG_BOOTFILE

+};

+

+//

+// There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec.

+//

+UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32};

+

+

+/**

+  Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer.

+

+  @param[in]  Buffer              Pointer to the option buffer.

+  @param[in]  Length              Length of the option buffer.

+  @param[in]  OptTag              Tag of the required option.

+

+  @retval     NULL                Failed to find the required option.

+  @retval     Others              The position of the required option.

+

+**/

+EFI_DHCP4_PACKET_OPTION *

+PxeBcParseDhcp4Options (

+  IN UINT8                      *Buffer,

+  IN UINT32                     Length,

+  IN UINT8                      OptTag

+  )

+{

+  EFI_DHCP4_PACKET_OPTION       *Option;

+  UINT32                        Offset;

+

+  Option  = (EFI_DHCP4_PACKET_OPTION *) Buffer;

+  Offset  = 0;

+

+  while (Offset < Length && Option->OpCode != PXEBC_DHCP4_TAG_EOP) {

+

+    if (Option->OpCode == OptTag) {

+      //

+      // Found the required option.

+      //

+      return Option;

+    }

+

+    //

+    // Skip the current option to the next.

+    //

+    if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) {

+      Offset++;

+    } else {

+      Offset += Option->Length + 2;

+    }

+

+    Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);

+  }

+

+  return NULL;

+}

+

+

+/**

+  Parse the PXE vender options and extract the information from them.

+

+  @param[in]  Dhcp4Option        Pointer to vendor options in buffer.

+  @param[in]  VendorOption       Pointer to structure to store information in vendor options.

+

+**/

+VOID

+PxeBcParseVendorOptions (

+  IN EFI_DHCP4_PACKET_OPTION    *Dhcp4Option,

+  IN PXEBC_VENDOR_OPTION        *VendorOption

+  )

+{

+  UINT32                        *BitMap;

+  UINT8                         VendorOptionLen;

+  EFI_DHCP4_PACKET_OPTION       *PxeOption;

+  UINT8                         Offset;

+

+  BitMap          = VendorOption->BitMap;

+  VendorOptionLen = Dhcp4Option->Length;

+  PxeOption       = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0];

+  Offset          = 0;

+

+  ASSERT (PxeOption != NULL);

+

+  while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) {

+    //

+    // Parse all the interesting PXE vendor options one by one.

+    //

+    switch (PxeOption->OpCode) {

+

+    case PXEBC_VENDOR_TAG_MTFTP_IP:

+

+      CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));

+      break;

+

+    case PXEBC_VENDOR_TAG_MTFTP_CPORT:

+

+      CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort));

+      break;

+

+    case PXEBC_VENDOR_TAG_MTFTP_SPORT:

+

+      CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort));

+      break;

+

+    case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT:

+

+      VendorOption->MtftpTimeout = *PxeOption->Data;

+      break;

+

+    case PXEBC_VENDOR_TAG_MTFTP_DELAY:

+

+      VendorOption->MtftpDelay = *PxeOption->Data;

+      break;

+

+    case PXEBC_VENDOR_TAG_DISCOVER_CTRL:

+

+      VendorOption->DiscoverCtrl = *PxeOption->Data;

+      break;

+

+    case PXEBC_VENDOR_TAG_DISCOVER_MCAST:

+

+      CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));

+      break;

+

+    case PXEBC_VENDOR_TAG_BOOT_SERVERS:

+

+      VendorOption->BootSvrLen  = PxeOption->Length;

+      VendorOption->BootSvr     = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data;

+      break;

+

+    case PXEBC_VENDOR_TAG_BOOT_MENU:

+

+      VendorOption->BootMenuLen = PxeOption->Length;

+      VendorOption->BootMenu    = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data;

+      break;

+

+    case PXEBC_VENDOR_TAG_MENU_PROMPT:

+

+      VendorOption->MenuPromptLen = PxeOption->Length;

+      VendorOption->MenuPrompt    = (PXEBC_MENU_PROMPT *) PxeOption->Data;

+      break;

+

+    case PXEBC_VENDOR_TAG_MCAST_ALLOC:

+

+      CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));

+      CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock));

+      CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange));

+      break;

+

+    case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES:

+

+      VendorOption->CredTypeLen = PxeOption->Length;

+      VendorOption->CredType    = (UINT32 *) PxeOption->Data;

+      break;

+

+    case PXEBC_VENDOR_TAG_BOOT_ITEM:

+

+      CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType));

+      CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer));

+      break;

+

+    default:

+      //

+      // Not interesting PXE vendor options.

+      //

+      break;

+    }

+

+    //

+    // Set the bit map for the special PXE options.

+    //

+    SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode);

+

+    //

+    // Continue to the next option.

+    //

+    if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) {

+      Offset++;

+    } else {

+      Offset = (UINT8) (Offset + PxeOption->Length + 2);

+    }

+

+    PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset);

+  }

+}

+

+

+/**

+  Build the options buffer for the DHCPv4 request packet.

+

+  @param[in]  Private             Pointer to PxeBc private data.

+  @param[out] OptList             Pointer to the option pointer array.

+  @param[in]  Buffer              Pointer to the buffer to contain the option list.

+  @param[in]  NeedMsgType         If TRUE, it is necessary to include the Msg type option.

+                                  Otherwise, it is not necessary.

+

+  @return     Index               The count of the built-in options.

+

+**/

+UINT32

+PxeBcBuildDhcp4Options (

+  IN  PXEBC_PRIVATE_DATA       *Private,

+  OUT EFI_DHCP4_PACKET_OPTION  **OptList,

+  IN  UINT8                    *Buffer,

+  IN  BOOLEAN                  NeedMsgType

+  )

+{

+  UINT32                       Index;

+  PXEBC_DHCP4_OPTION_ENTRY     OptEnt;

+  UINT16                       Value;

+

+  Index      = 0;

+  OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer;

+

+  if (NeedMsgType) {

+    //

+    // Append message type.

+    //

+    OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_MSG_TYPE;

+    OptList[Index]->Length  = 1;

+    OptEnt.Mesg             = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data;

+    OptEnt.Mesg->Type       = PXEBC_DHCP4_MSG_TYPE_REQUEST;

+    Index++;

+    OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);

+

+    //

+    // Append max message size.

+    //

+    OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_MAXMSG;

+    OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE);

+    OptEnt.MaxMesgSize      = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data;

+    Value                   = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);

+    CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16));

+    Index++;

+    OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);

+  }

+

+  //

+  // Append parameter request list option.

+  //

+  OptList[Index]->OpCode    = PXEBC_DHCP4_TAG_PARA_LIST;

+  OptList[Index]->Length    = 35;

+  OptEnt.Para               = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;

+  OptEnt.Para->ParaList[0]  = PXEBC_DHCP4_TAG_NETMASK;

+  OptEnt.Para->ParaList[1]  = PXEBC_DHCP4_TAG_TIME_OFFSET;

+  OptEnt.Para->ParaList[2]  = PXEBC_DHCP4_TAG_ROUTER;

+  OptEnt.Para->ParaList[3]  = PXEBC_DHCP4_TAG_TIME_SERVER;

+  OptEnt.Para->ParaList[4]  = PXEBC_DHCP4_TAG_NAME_SERVER;

+  OptEnt.Para->ParaList[5]  = PXEBC_DHCP4_TAG_DNS_SERVER;

+  OptEnt.Para->ParaList[6]  = PXEBC_DHCP4_TAG_HOSTNAME;

+  OptEnt.Para->ParaList[7]  = PXEBC_DHCP4_TAG_BOOTFILE_LEN;

+  OptEnt.Para->ParaList[8]  = PXEBC_DHCP4_TAG_DOMAINNAME;

+  OptEnt.Para->ParaList[9]  = PXEBC_DHCP4_TAG_ROOTPATH;

+  OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH;

+  OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU;

+  OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL;

+  OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST;

+  OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN;

+  OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER;

+  OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER;

+  OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR;

+  OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP;

+  OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE;

+  OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID;

+  OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1;

+  OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2;

+  OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID;

+  OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP;

+  OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE;

+  OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID;

+  OptEnt.Para->ParaList[27] = 0x80;

+  OptEnt.Para->ParaList[28] = 0x81;

+  OptEnt.Para->ParaList[29] = 0x82;

+  OptEnt.Para->ParaList[30] = 0x83;

+  OptEnt.Para->ParaList[31] = 0x84;

+  OptEnt.Para->ParaList[32] = 0x85;

+  OptEnt.Para->ParaList[33] = 0x86;

+  OptEnt.Para->ParaList[34] = 0x87;

+  Index++;

+  OptList[Index]            = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);

+

+  //

+  // Append UUID/Guid-based client identifier option

+  //

+  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_UUID;

+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID);

+  OptEnt.Uuid             = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data;

+  OptEnt.Uuid->Type       = 0;

+  Index++;

+  OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);

+

+  if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {

+    //

+    // Zero the Guid to indicate NOT programable if failed to get system Guid.

+    //

+    ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));

+  }

+

+  //

+  // Append client network device interface option

+  //

+  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_UNDI;

+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI);

+  OptEnt.Undi             = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data;

+

+  if (Private->Nii != NULL) {

+    OptEnt.Undi->Type     = Private->Nii->Type;

+    OptEnt.Undi->MajorVer = Private->Nii->MajorVer;

+    OptEnt.Undi->MinorVer = Private->Nii->MinorVer;

+  } else {

+    OptEnt.Undi->Type     = DEFAULT_UNDI_TYPE;

+    OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;

+    OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;

+  }

+

+  Index++;

+  OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);

+

+  //

+  // Append client system architecture option

+  //

+  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_ARCH;

+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH);

+  OptEnt.Arch             = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data;

+  Value                   = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);

+  CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));

+  Index++;

+  OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);

+

+  //

+  // Append vendor class identify option

+  //

+  OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_CLASS_ID;

+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID);

+  OptEnt.Clid             = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data;

+  CopyMem (

+    OptEnt.Clid,

+    DEFAULT_CLASS_ID_DATA,

+    sizeof (PXEBC_DHCP4_OPTION_CLID)

+    );

+  PxeBcUintnToAscDecWithFormat (

+    EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,

+    OptEnt.Clid->ArchitectureType,

+    sizeof (OptEnt.Clid->ArchitectureType)

+    );

+

+  if (Private->Nii != NULL) {

+    CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));

+    PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));

+    PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));

+  }

+

+  Index++;

+

+  return Index;

+}

+

+

+/**

+  Create a template DHCPv4 packet as a seed.

+

+  @param[out] Seed           Pointer to the seed packet.

+  @param[in]  Udp4           Pointer to EFI_UDP4_PROTOCOL.

+

+**/

+VOID

+PxeBcSeedDhcp4Packet (

+  OUT EFI_DHCP4_PACKET       *Seed,

+  IN  EFI_UDP4_PROTOCOL      *Udp4

+  )

+{

+  EFI_SIMPLE_NETWORK_MODE    Mode;

+  EFI_DHCP4_HEADER           *Header;

+

+  //

+  // Get IfType and HwAddressSize from SNP mode data.

+  //

+  Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode);

+

+  Seed->Size            = sizeof (EFI_DHCP4_PACKET);

+  Seed->Length          = sizeof (Seed->Dhcp4);

+  Header                = &Seed->Dhcp4.Header;

+  ZeroMem (Header, sizeof (EFI_DHCP4_HEADER));

+  Header->OpCode        = PXEBC_DHCP4_OPCODE_REQUEST;

+  Header->HwType        = Mode.IfType;

+  Header->HwAddrLen     = (UINT8) Mode.HwAddressSize;

+  CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen);

+

+  Seed->Dhcp4.Magik     = PXEBC_DHCP4_MAGIC;

+  Seed->Dhcp4.Option[0] = PXEBC_DHCP4_TAG_EOP;

+}

+

+

+/**

+  Cache the DHCPv4 packet.

+

+  @param[in]  Dst          Pointer to the cache buffer for DHCPv4 packet.

+  @param[in]  Src          Pointer to the DHCPv4 packet to be cached.

+

+**/

+VOID

+PxeBcCacheDhcp4Packet (

+  IN EFI_DHCP4_PACKET     *Dst,

+  IN EFI_DHCP4_PACKET     *Src

+  )

+{

+  ASSERT (Dst->Size >= Src->Length);

+

+  CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);

+  Dst->Length = Src->Length;

+}

+

+

+/**

+  Parse the cached DHCPv4 packet, including all the options.

+

+  @param[in]  Cache4           Pointer to cached DHCPv4 packet.

+

+  @retval     EFI_SUCCESS      Parsed the DHCPv4 packet successfully.

+  @retval     EFI_DEVICE_ERROR Failed to parse and invalid packet.

+

+**/

+EFI_STATUS

+PxeBcParseDhcp4Packet (

+  IN PXEBC_DHCP4_PACKET_CACHE    *Cache4

+  )

+{

+  EFI_DHCP4_PACKET               *Offer;

+  EFI_DHCP4_PACKET_OPTION        **Options;

+  EFI_DHCP4_PACKET_OPTION        *Option;

+  PXEBC_OFFER_TYPE               OfferType;

+  UINTN                          Index;

+  BOOLEAN                        IsProxyOffer;

+  BOOLEAN                        IsPxeOffer;

+  UINT8                          *Ptr8;

+

+  IsProxyOffer = FALSE;

+  IsPxeOffer   = FALSE;

+

+  ZeroMem (Cache4->OptList, sizeof (Cache4->OptList));

+  ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt));

+

+  Offer   = &Cache4->Packet.Offer;

+  Options = Cache4->OptList;

+

+  //

+  // Parse DHCPv4 options in this offer, and store the pointers.

+  //

+  for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {

+    Options[Index] = PxeBcParseDhcp4Options (

+                       Offer->Dhcp4.Option,

+                       GET_OPTION_BUFFER_LEN (Offer),

+                       mInterestedDhcp4Tags[Index]

+                       );

+  }

+

+  //

+  // The offer with "yiaddr" is a proxy offer.

+  //

+  if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) {

+    IsProxyOffer = TRUE;

+  }

+

+  //

+  // The offer with "PXEClient" is a PXE offer.

+  //

+  Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID];

+  if ((Option != NULL) && (Option->Length >= 9) &&

+      (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {

+    IsPxeOffer = TRUE;

+  }

+

+  //

+  // Parse PXE vendor options in this offer, and store the contents/pointers.

+  //

+  Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR];

+  if (IsPxeOffer && Option != NULL) {

+    PxeBcParseVendorOptions (Option, &Cache4->VendorOpt);

+  }

+

+  //

+  // Check whether bootfilename and serverhostname overloaded, refers to rfc-2132 in details.

+  // If overloaded, parse the buffer as nested DHCPv4 options, or else just parse as bootfilename

+  // and serverhostname option.

+  //

+  Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD];

+  if (Option != NULL && (Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) {

+

+    Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = PxeBcParseDhcp4Options (

+                                                (UINT8 *) Offer->Dhcp4.Header.BootFileName,

+                                                sizeof (Offer->Dhcp4.Header.BootFileName),

+                                                PXEBC_DHCP4_TAG_BOOTFILE

+                                                );

+    //

+    // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null

+    // terminated string. So force to append null terminated character at the end of string.

+    //

+    if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {

+      Ptr8 =  (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];

+      Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length;

+      *Ptr8 =  '\0';

+    }

+

+  } else if ((Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) &&

+            (Offer->Dhcp4.Header.BootFileName[0] != 0)) {

+    //

+    // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it.

+    // Do not count dhcp option header here, or else will destory the serverhostname.

+    //

+    Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *)

+                                                (&Offer->Dhcp4.Header.BootFileName[0] -

+                                                OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));

+

+  }

+

+  //

+  // Determine offer type of the DHCPv4 packet.

+  //

+  Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE];

+  if (Option == NULL || Option->Data[0] == 0) {

+    //

+    // It's a Bootp offer.

+    //

+    OfferType = PxeOfferTypeBootp;

+

+    Option    = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE];

+    if (Option == NULL) {

+      //

+      // If the Bootp offer without bootfilename, discard it.

+      //

+      return EFI_DEVICE_ERROR;

+    }

+  } else {

+

+    if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {

+      //

+      // It's a PXE10 offer with PXEClient and discover vendor option.

+      //

+      OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10;

+    } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {

+      //

+      // It's a WFM11a offer with PXEClient and mtftp vendor option.

+      // But multi-cast download is not supported currently, so discard it.

+      //

+      return EFI_DEVICE_ERROR;

+    } else if (IsPxeOffer) {

+      //

+      // It's a BINL offer only with PXEClient.

+      //

+      OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;

+    } else {

+      //

+      // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet.

+      //

+      OfferType = PxeOfferTypeDhcpOnly;

+    }

+  }

+

+  Cache4->OfferType = OfferType;

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Cache the DHCPv4 ack packet, and parse it on demand.

+

+  @param[in]  Private             Pointer to PxeBc private data.

+  @param[in]  Ack                 Pointer to the DHCPv4 ack packet.

+  @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.

+

+**/

+VOID

+PxeBcCopyDhcp4Ack (

+  IN PXEBC_PRIVATE_DATA   *Private,

+  IN EFI_DHCP4_PACKET     *Ack,

+  IN BOOLEAN              Verified

+  )

+{

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+

+  Mode = Private->PxeBc.Mode;

+

+  PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack);

+

+  if (Verified) {

+    //

+    // Parse the ack packet and store it into mode data if needed.

+    //

+    PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4);

+    CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length);

+    Mode->DhcpAckReceived = TRUE;

+  }

+}

+

+

+/**

+  Cache the DHCPv4 proxy offer packet according to the received order.

+

+  @param[in]  Private               Pointer to PxeBc private data.

+  @param[in]  OfferIndex            The received order of offer packets.

+

+**/

+VOID

+PxeBcCopyProxyOffer (

+  IN PXEBC_PRIVATE_DATA   *Private,

+  IN UINT32               OfferIndex

+  )

+{

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+  EFI_DHCP4_PACKET        *Offer;

+

+  ASSERT (OfferIndex < Private->OfferNum);

+  ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);

+

+  Mode  = Private->PxeBc.Mode;

+  Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer;

+

+  //

+  // Cache the proxy offer packet and parse it.

+  //

+  PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer);

+  PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4);

+

+  //

+  // Store this packet into mode data.

+  //

+  CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length);

+  Mode->ProxyOfferReceived = TRUE;

+}

+

+

+/**

+  Retry to request bootfile name by the BINL offer.

+

+  @param[in]  Private              Pointer to PxeBc private data.

+  @param[in]  Index                The received order of offer packets.

+

+  @retval     EFI_SUCCESS          Successfully retried to request bootfile name.

+  @retval     EFI_DEVICE_ERROR     Failed to retry bootfile name.

+

+**/

+EFI_STATUS

+PxeBcRetryBinlOffer (

+  IN PXEBC_PRIVATE_DATA     *Private,

+  IN UINT32                 Index

+  )

+{

+  EFI_DHCP4_PACKET          *Offer;

+  EFI_IP_ADDRESS            ServerIp;

+  EFI_STATUS                Status;

+  PXEBC_DHCP4_PACKET_CACHE  *Cache4;

+  EFI_DHCP4_PACKET          *Reply;

+

+  ASSERT (Index < PXEBC_OFFER_MAX_NUM);

+  ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl ||

+          Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl);

+

+  Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;

+

+  //

+  // Prefer to siaddr in header as next server address. If it's zero, then use option 54.

+  //

+  if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) {

+    CopyMem (

+      &ServerIp.Addr[0],

+      Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,

+      sizeof (EFI_IPv4_ADDRESS)

+      );

+  } else {

+    CopyMem (

+      &ServerIp.Addr[0],

+      &Offer->Dhcp4.Header.ServerAddr,

+      sizeof (EFI_IPv4_ADDRESS)

+      );

+  }

+

+  Private->IsDoDiscover = FALSE;

+  Cache4                = &Private->ProxyOffer.Dhcp4;

+  Reply                 = &Cache4->Packet.Offer;

+

+  //

+  // Send another request packet for bootfile name.

+  //

+  Status = PxeBcDhcp4Discover (

+             Private,

+             0,

+             NULL,

+             FALSE,

+             &ServerIp,

+             0,

+             NULL

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Parse the reply for the last request packet.

+  //

+  Status = PxeBcParseDhcp4Packet (Cache4);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (Cache4->OfferType != PxeOfferTypeProxyPxe10 &&

+      Cache4->OfferType != PxeOfferTypeProxyWfm11a &&

+      Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {

+    //

+    // This BINL ack doesn't have discovery option set or multicast option set

+    // or bootfile name specified.

+    //

+    return EFI_DEVICE_ERROR;

+  }

+

+  //

+  // Store the reply into mode data.

+  //

+  Private->PxeBc.Mode->ProxyOfferReceived = TRUE;

+  CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length);

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount.

+

+  @param[in]  Private               Pointer to PxeBc private data.

+  @param[in]  RcvdOffer             Pointer to the received offer packet.

+

+**/

+VOID

+PxeBcCacheDhcp4Offer (

+  IN PXEBC_PRIVATE_DATA     *Private,

+  IN EFI_DHCP4_PACKET       *RcvdOffer

+  )

+{

+  PXEBC_DHCP4_PACKET_CACHE  *Cache4;

+  EFI_DHCP4_PACKET          *Offer;

+  PXEBC_OFFER_TYPE          OfferType;

+

+  ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM);

+  Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;

+  Offer  = &Cache4->Packet.Offer;

+

+  //

+  // Cache the content of DHCPv4 packet firstly.

+  //

+  PxeBcCacheDhcp4Packet (Offer, RcvdOffer);

+

+  //

+  // Validate the DHCPv4 packet, and parse the options and offer type.

+  //

+  if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) {

+    return;

+  }

+

+  //

+  // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.

+  //

+  OfferType = Cache4->OfferType;

+  ASSERT (OfferType < PxeOfferTypeMax);

+

+  if (OfferType == PxeOfferTypeBootp) {

+    //

+    // It's a Bootp offer, only cache the first one, and discard the others.

+    //

+    if (Private->OfferCount[OfferType] == 0) {

+      Private->OfferIndex[OfferType][0] = Private->OfferNum;

+      Private->OfferCount[OfferType]    = 1;

+    } else {

+      return;

+    }

+  } else {

+    ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);

+    if (IS_PROXY_DHCP_OFFER (Offer)) {

+      //

+      // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.

+      //

+      Private->IsProxyRecved = TRUE;

+

+      if (OfferType == PxeOfferTypeProxyBinl) {

+        //

+        // Cache all proxy BINL offers.

+        //

+        Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;

+        Private->OfferCount[OfferType]++;

+      } else if (Private->OfferCount[OfferType] > 0) {

+        //

+        // Only cache the first PXE10/WFM11a offer, and discard the others.

+        //

+        Private->OfferIndex[OfferType][0] = Private->OfferNum;

+        Private->OfferCount[OfferType]    = 1;

+      } else {

+        return ;

+      }

+    } else {

+      //

+      // It's a DHCPv4 offer with yiaddr, and cache them all.

+      //

+      Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;

+      Private->OfferCount[OfferType]++;

+    }

+  }

+

+  Private->OfferNum++;

+}

+

+

+/**

+  Select an DHCPv4 offer, and record SelectIndex and SelectProxyType.

+

+  @param[in]  Private             Pointer to PxeBc private data.

+

+**/

+VOID

+PxeBcSelectDhcp4Offer (

+  IN PXEBC_PRIVATE_DATA       *Private

+  )

+{

+  UINT32                      Index;

+  UINT32                      OfferIndex;

+  EFI_DHCP4_PACKET            *Offer;

+

+  Private->SelectIndex = 0;

+

+  if (Private->IsOfferSorted) {

+    //

+    // Select offer by default policy.

+    //

+    if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {

+      //

+      // 1. DhcpPxe10 offer

+      //

+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {

+      //

+      // 2. DhcpWfm11a offer

+      //

+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&

+               Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {

+      //

+      // 3. DhcpOnly offer and ProxyPxe10 offer.

+      //

+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;

+      Private->SelectProxyType = PxeOfferTypeProxyPxe10;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&

+               Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {

+      //

+      // 4. DhcpOnly offer and ProxyWfm11a offer.

+      //

+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;

+      Private->SelectProxyType = PxeOfferTypeProxyWfm11a;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {

+      //

+      // 5. DhcpBinl offer.

+      //

+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&

+               Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {

+      //

+      // 6. DhcpOnly offer and ProxyBinl offer.

+      //

+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;

+      Private->SelectProxyType = PxeOfferTypeProxyBinl;

+

+    } else {

+      //

+      // 7. DhcpOnly offer with bootfilename.

+      //

+      for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {

+        OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];

+        if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {

+          Private->SelectIndex = OfferIndex + 1;

+          break;

+        }

+      }

+      //

+      // 8. Bootp offer with bootfilename.

+      //

+      OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0];

+      if (Private->SelectIndex == 0 &&

+          Private->OfferCount[PxeOfferTypeBootp] > 0 &&

+          Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {

+        Private->SelectIndex = OfferIndex + 1;

+      }

+    }

+  } else {

+    //

+    // Select offer by received order.

+    //

+    for (Index = 0; Index < Private->OfferNum; Index++) {

+

+      Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;

+

+      if (IS_PROXY_DHCP_OFFER (Offer)) {

+        //

+        // Skip proxy offers

+        //

+        continue;

+      }

+

+      if (!Private->IsProxyRecved &&

+          Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly &&

+          Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {

+        //

+        // Skip if DhcpOnly offer without any other proxy offers or bootfilename.

+        //

+        continue;

+      }

+

+      //

+      // Record the index of the select offer.

+      //

+      Private->SelectIndex = Index + 1;

+      break;

+    }

+  }

+}

+

+

+/**

+  Handle the DHCPv4 offer packet.

+

+  @param[in]  Private             Pointer to PxeBc private data.

+

+  @retval     EFI_SUCCESS         Handled the DHCPv4 offer packet successfully.

+  @retval     EFI_NO_RESPONSE     No response to the following request packet.

+

+**/

+EFI_STATUS

+PxeBcHandleDhcp4Offer (

+  IN PXEBC_PRIVATE_DATA     *Private

+  )

+{

+  PXEBC_DHCP4_PACKET_CACHE  *Cache4;

+  EFI_DHCP4_PACKET_OPTION   **Options;

+  UINT32                    Index;

+  EFI_DHCP4_PACKET          *Offer;

+  PXEBC_OFFER_TYPE          OfferType;

+  UINT32                    ProxyIndex;

+  UINT32                    SelectIndex;

+  EFI_STATUS                Status;

+  EFI_PXE_BASE_CODE_MODE    *Mode;

+  EFI_DHCP4_PACKET          *Ack;

+

+  ASSERT (Private->SelectIndex > 0);

+  SelectIndex = (UINT32) (Private->SelectIndex - 1);

+  ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);

+  Cache4      = &Private->OfferBuffer[SelectIndex].Dhcp4;

+  Options     = Cache4->OptList;

+  Status      = EFI_SUCCESS;

+

+  if (Cache4->OfferType == PxeOfferTypeDhcpBinl) {

+    //

+    // DhcpBinl offer is selected, so need try to request bootfilename by this offer.

+    //

+    if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) {

+      Status = EFI_NO_RESPONSE;

+    }

+  } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) {

+

+    if (Private->IsProxyRecved) {

+      //

+      // DhcpOnly offer is selected, so need try to request bootfile name.

+      //

+      ProxyIndex = 0;

+      if (Private->IsOfferSorted) {

+        //

+        // The proxy offer should be determined if select by default policy.

+        // IsOfferSorted means all offers are labeled by OfferIndex.

+        //

+        ASSERT (Private->SelectProxyType < PxeOfferTypeMax);

+        ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);

+

+        if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {

+          //

+          // Try all the cached ProxyBinl offer one by one to request bootfile name.

+          //

+          for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {

+            ASSERT (Index < PXEBC_OFFER_MAX_NUM);

+            ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];

+            if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) {

+              break;

+            }

+          }

+          if (Index == Private->OfferCount[Private->SelectProxyType]) {

+            Status = EFI_NO_RESPONSE;

+          }

+        } else {

+          //

+          // For other proxy offers, only one is buffered.

+          //

+          ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];

+        }

+      } else {

+        //

+        // The proxy offer should not be determined if select by received order.

+        //

+        Status = EFI_NO_RESPONSE;

+

+        for (Index = 0; Index < Private->OfferNum; Index++) {

+          ASSERT (Index < PXEBC_OFFER_MAX_NUM);

+          Offer     = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;

+          OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType;

+          if (!IS_PROXY_DHCP_OFFER (Offer)) {

+            //

+            // Skip non proxy DHCPv4 offers.

+            //

+            continue;

+          }

+

+          if (OfferType == PxeOfferTypeProxyBinl) {

+            //

+            // Try all the cached ProxyBinl offer one by one to request bootfile name.

+            //

+            if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) {

+              continue;

+            }

+          }

+

+          Private->SelectProxyType = OfferType;

+          ProxyIndex               = Index;

+          Status                   = EFI_SUCCESS;

+          break;

+        }

+      }

+

+      if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {

+        //

+        // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.

+        //

+        PxeBcCopyProxyOffer (Private, ProxyIndex);

+      }

+    } else {

+      //

+      //  Othewise, the bootfile name must be included in DhcpOnly offer.

+      //

+      ASSERT (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);

+    }

+  }

+

+  if (!EFI_ERROR (Status)) {

+    //

+    // All PXE boot information is ready by now.

+    //

+    Mode  = Private->PxeBc.Mode;

+    Offer = &Cache4->Packet.Offer;

+    Ack   = &Private->DhcpAck.Dhcp4.Packet.Ack;

+    if (Cache4->OfferType == PxeOfferTypeBootp) {

+      //

+      // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply

+      // should be taken as ack.

+      //

+      Ack = Offer;

+    }

+

+    PxeBcCopyDhcp4Ack (Private, Ack, TRUE);

+    Mode->DhcpDiscoverValid = TRUE;

+  }

+

+  return Status;

+}

+

+

+/**

+  EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver

+  to intercept events that occurred in the configuration process.

+

+  @param[in]  This              Pointer to the EFI DHCPv4 Protocol.

+  @param[in]  Context           Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure().

+  @param[in]  CurrentState      The current operational state of the EFI DHCPv4 Protocol driver.

+  @param[in]  Dhcp4Event        The event that occurs in the current state, which usually means a

+                                state transition.

+  @param[in]  Packet            The DHCPv4 packet that is going to be sent or already received.

+  @param[out] NewPacket         The packet that is used to replace the above Packet.

+

+  @retval EFI_SUCCESS           Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.

+  @retval EFI_NOT_READY         Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol

+                                driver will continue to wait for more DHCPOFFER packets until the

+                                retry timeout expires.

+  @retval EFI_ABORTED           Tells the EFI DHCPv4 Protocol driver to abort the current process

+                                and return to the Dhcp4Init or Dhcp4InitReboot state.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcDhcp4CallBack (

+  IN  EFI_DHCP4_PROTOCOL               *This,

+  IN  VOID                             *Context,

+  IN  EFI_DHCP4_STATE                  CurrentState,

+  IN  EFI_DHCP4_EVENT                  Dhcp4Event,

+  IN  EFI_DHCP4_PACKET                 *Packet            OPTIONAL,

+  OUT EFI_DHCP4_PACKET                 **NewPacket        OPTIONAL

+  )

+{

+  PXEBC_PRIVATE_DATA                   *Private;

+  EFI_PXE_BASE_CODE_MODE               *Mode;

+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  *Callback;

+  EFI_DHCP4_PACKET_OPTION              *MaxMsgSize;

+  UINT16                               Value;

+  EFI_STATUS                           Status;

+  BOOLEAN                              Received;

+

+  if ((Dhcp4Event != Dhcp4RcvdOffer) &&

+      (Dhcp4Event != Dhcp4SelectOffer) &&

+      (Dhcp4Event != Dhcp4SendDiscover) &&

+      (Dhcp4Event != Dhcp4RcvdAck)) {

+    return EFI_SUCCESS;

+  }

+

+  Private   = (PXEBC_PRIVATE_DATA *) Context;

+  Mode      = Private->PxeBc.Mode;

+  Callback  = Private->PxeBcCallback;

+

+  //

+  // Override the Maximum DHCP Message Size.

+  //

+  MaxMsgSize = PxeBcParseDhcp4Options (

+                 Packet->Dhcp4.Option,

+                 GET_OPTION_BUFFER_LEN (Packet),

+                 PXEBC_DHCP4_TAG_MAXMSG

+                 );

+  if (MaxMsgSize != NULL) {

+    Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);

+    CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));

+  }

+

+  //

+  // Callback to user if any packets sent or received.

+  //

+  if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) {

+    Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck);

+    Status = Callback->Callback (

+                         Callback,

+                         Private->Function,

+                         Received,

+                         Packet->Length,

+                         (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4

+                         );

+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {

+      return EFI_ABORTED;

+    }

+  }

+

+  Status = EFI_SUCCESS;

+

+  switch (Dhcp4Event) {

+

+  case Dhcp4SendDiscover:

+    //

+    // Cache the DHCPv4 discover packet to mode data directly.

+    // It need to check SendGuid as well as Dhcp4SendRequest.

+    //

+    CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length);

+

+  case Dhcp4SendRequest:

+    if (Mode->SendGUID) {

+      //

+      // Send the system Guid instead of the MAC address as the hardware address if required.

+      //

+      if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) {

+        //

+        // Zero the Guid to indicate NOT programable if failed to get system Guid.

+        //

+        ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));

+      }

+      Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID);

+    }

+    break;

+

+  case Dhcp4RcvdOffer:

+    Status = EFI_NOT_READY;

+    if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {

+      //

+      // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record

+      // the OfferIndex and OfferCount.

+      //

+      PxeBcCacheDhcp4Offer (Private, Packet);

+    }

+    break;

+

+  case Dhcp4SelectOffer:

+    //

+    // Select offer by the default policy or by order, and record the SelectIndex

+    // and SelectProxyType.

+    //

+    PxeBcSelectDhcp4Offer (Private);

+

+    if (Private->SelectIndex == 0) {

+      Status = EFI_ABORTED;

+    } else {

+      *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer;

+    }

+    break;

+

+  case Dhcp4RcvdAck:

+    //

+    // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data

+    // without verification.

+    //

+    ASSERT (Private->SelectIndex != 0);

+

+    PxeBcCopyDhcp4Ack (Private, Packet, FALSE);

+    break;

+

+  default:

+    break;

+  }

+

+  return Status;

+}

+

+

+/**

+  Build and send out the request packet for the bootfile, and parse the reply.

+

+  @param[in]  Private               Pointer to PxeBc private data.

+  @param[in]  Type                  PxeBc option boot item type.

+  @param[in]  Layer                 Pointer to option boot item layer.

+  @param[in]  UseBis                Use BIS or not.

+  @param[in]  DestIp                Pointer to the server address.

+  @param[in]  IpCount               The total count of the server address.

+  @param[in]  SrvList               Pointer to EFI_PXE_BASE_CODE_SRVLIST.

+

+  @retval     EFI_SUCCESS           Successfully discovered boot file.

+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.

+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.

+  @retval     Others                Failed to discover boot file.

+

+**/

+EFI_STATUS

+PxeBcDhcp4Discover (

+  IN  PXEBC_PRIVATE_DATA              *Private,

+  IN  UINT16                          Type,

+  IN  UINT16                          *Layer,

+  IN  BOOLEAN                         UseBis,

+  IN  EFI_IP_ADDRESS                  *DestIp,

+  IN  UINT16                          IpCount,

+  IN  EFI_PXE_BASE_CODE_SRVLIST       *SrvList

+  )

+{

+  EFI_PXE_BASE_CODE_UDP_PORT          Sport;

+  EFI_PXE_BASE_CODE_MODE              *Mode;

+  EFI_DHCP4_PROTOCOL                  *Dhcp4;

+  EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN    Token;

+  BOOLEAN                             IsBCast;

+  EFI_STATUS                          Status;

+  UINT16                              RepIndex;

+  UINT16                              SrvIndex;

+  UINT16                              TryIndex;

+  EFI_DHCP4_LISTEN_POINT              ListenPoint;

+  EFI_DHCP4_PACKET                    *Response;

+  UINT8                               Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];

+  EFI_DHCP4_PACKET_OPTION             *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];

+  UINT32                              OptCount;

+  EFI_DHCP4_PACKET_OPTION             *PxeOpt;

+  PXEBC_OPTION_BOOT_ITEM              *PxeBootItem;

+  UINT8                               VendorOptLen;

+  UINT32                              Xid;

+

+  Mode      = Private->PxeBc.Mode;

+  Dhcp4     = Private->Dhcp4;

+  Status    = EFI_SUCCESS;

+

+  ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));

+

+  //

+  // Use broadcast if destination address not specified.

+  //

+  if (DestIp == NULL) {

+    Sport   = PXEBC_DHCP4_S_PORT;

+    IsBCast = TRUE;

+  } else {

+    Sport   = PXEBC_BS_DISCOVER_PORT;

+    IsBCast = FALSE;

+  }

+

+  if (!UseBis && Layer != NULL) {

+    *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;

+  }

+

+  //

+  // Build all the options for the request packet.

+  //

+  OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE);

+

+  if (Private->IsDoDiscover) {

+    //

+    // Add vendor option of PXE_BOOT_ITEM

+    //

+    VendorOptLen      = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1);

+    OptList[OptCount] = AllocateZeroPool (VendorOptLen);

+    if (OptList[OptCount] == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    OptList[OptCount]->OpCode     = PXEBC_DHCP4_TAG_VENDOR;

+    OptList[OptCount]->Length     = (UINT8) (VendorOptLen - 2);

+    PxeOpt                        = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data;

+    PxeOpt->OpCode                = PXEBC_VENDOR_TAG_BOOT_ITEM;

+    PxeOpt->Length                = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM);

+    PxeBootItem                   = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data;

+    PxeBootItem->Type             = HTONS (Type);

+    PxeOpt->Data[PxeOpt->Length]  = PXEBC_DHCP4_TAG_EOP;

+

+    if (Layer != NULL) {

+      PxeBootItem->Layer          = HTONS (*Layer);

+    }

+

+    OptCount++;

+  }

+

+  //

+  // Build the request packet with seed packet and option list.

+  //

+  Status = Dhcp4->Build (

+                    Dhcp4,

+                    &Private->SeedPacket,

+                    0,

+                    NULL,

+                    OptCount,

+                    OptList,

+                    &Token.Packet

+                    );

+  //

+  // Free the vendor option of PXE_BOOT_ITEM.

+  //

+  if (Private->IsDoDiscover) {

+    FreePool (OptList[OptCount - 1]);

+  }

+

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (Mode->SendGUID) {

+    if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) {

+      //

+      // Zero the Guid to indicate NOT programable if failed to get system Guid.

+      //

+      ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));

+    }

+    Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8)  sizeof (EFI_GUID);

+  }

+

+  //

+  // Set fields of the token for the request packet.

+  //

+  Xid                                 = NET_RANDOM (NetRandomInitSeed ());

+  Token.Packet->Dhcp4.Header.Xid      = HTONL (Xid);

+  Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0));

+  CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));

+

+  Token.RemotePort = Sport;

+

+  if (IsBCast) {

+    SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);

+  } else {

+    CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));

+  }

+

+  CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));

+

+  if (!IsBCast) {

+    Token.ListenPointCount            = 1;

+    Token.ListenPoints                = &ListenPoint;

+    Token.ListenPoints[0].ListenPort  = PXEBC_BS_DISCOVER_PORT;

+    CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS));

+    CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS));

+  }

+

+  //

+  // Send out the request packet to discover the bootfile.

+  //

+  for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) {

+

+    Token.TimeoutValue                  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex);

+    Token.Packet->Dhcp4.Header.Seconds  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1));

+

+    Status = Dhcp4->TransmitReceive (Dhcp4, &Token);

+    if (Token.Status != EFI_TIMEOUT) {

+      break;

+    }

+  }

+

+  if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) {

+    //

+    // No server response our PXE request

+    //

+    Status = EFI_TIMEOUT;

+  }

+

+  if (!EFI_ERROR (Status)) {

+

+    RepIndex  = 0;

+    SrvIndex  = 0;

+    Response  = Token.ResponseList;

+    //

+    // Find the right PXE Reply according to server address.

+    //

+    while (RepIndex < Token.ResponseCount) {

+

+      while (SrvIndex < IpCount) {

+        if (SrvList[SrvIndex].AcceptAnyResponse) {

+          break;

+        }

+        if ((SrvList[SrvIndex].Type == Type) &&

+            EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &Private->ServerIp)) {

+          break;

+        }

+        SrvIndex++;

+      }

+

+      if ((IpCount != SrvIndex) || (IpCount == 0)) {

+        break;

+      }

+

+      SrvIndex = 0;

+      RepIndex++;

+

+      Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);

+    }

+

+    if (RepIndex < Token.ResponseCount) {

+      //

+      // Cache the right PXE reply packet here, set valid flag later.

+      // Especially for PXE discover packet, store it into mode data here.

+      //

+      if (Private->IsDoDiscover) {

+        PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response);

+        CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length);

+      } else {

+        PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response);

+      }

+    } else {

+      //

+      // Not found the right PXE reply packet.

+      //

+      Status = EFI_NOT_FOUND;

+    }

+    if (Token.ResponseList != NULL) {

+      FreePool (Token.ResponseList);

+    }

+  }

+

+  FreePool (Token.Packet);

+  return Status;

+}

+

+

+/**

+  Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.

+

+  @param[in]  Private           Pointer to PxeBc private data.

+  @param[in]  Dhcp4             Pointer to the EFI_DHCP4_PROTOCOL

+

+  @retval EFI_SUCCESS           The D.O.R.A process successfully finished.

+  @retval Others                Failed to finish the D.O.R.A process.

+

+**/

+EFI_STATUS

+PxeBcDhcp4Dora (

+  IN PXEBC_PRIVATE_DATA        *Private,

+  IN EFI_DHCP4_PROTOCOL        *Dhcp4

+  )

+{

+  EFI_PXE_BASE_CODE_MODE       *PxeMode;

+  EFI_DHCP4_CONFIG_DATA        Config;

+  EFI_DHCP4_MODE_DATA          Mode;

+  EFI_DHCP4_PACKET_OPTION      *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];

+  UINT8                        Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];

+  UINT32                       OptCount;

+  EFI_STATUS                   Status;

+

+  ASSERT (Dhcp4 != NULL);

+

+  Status   = EFI_SUCCESS;

+  PxeMode  = Private->PxeBc.Mode;

+

+  //

+  // Build option list for the request packet.

+  //

+  OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE);

+  ASSERT (OptCount> 0);

+

+  ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA));

+  ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));

+

+  Config.OptionCount      = OptCount;

+  Config.OptionList       = OptList;

+  Config.Dhcp4Callback    = PxeBcDhcp4CallBack;

+  Config.CallbackContext  = Private;

+  Config.DiscoverTryCount = PXEBC_DHCP_RETRIES;

+  Config.DiscoverTimeout  = mPxeDhcpTimeout;

+

+  //

+  // Configure the DHCPv4 instance for PXE boot.

+  //

+  Status = Dhcp4->Configure (Dhcp4, &Config);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Initialize the record fields for DHCPv4 offer in private data.

+  //

+  Private->IsProxyRecved = FALSE;

+  Private->OfferNum      = 0;

+  ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));

+  ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));

+

+  //

+  // Start DHCPv4 D.O.R.A. process to acquire IPv4 address.

+  //

+  Status = Dhcp4->Start (Dhcp4, NULL);

+  if (EFI_ERROR (Status)) {

+    if (Status == EFI_ICMP_ERROR) {

+      PxeMode->IcmpErrorReceived = TRUE;

+    }

+    goto ON_EXIT;

+  }

+

+  //

+  // Get the acquired IPv4 address and store them.

+  //

+  Status = Dhcp4->GetModeData (Dhcp4, &Mode);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  ASSERT (Mode.State == Dhcp4Bound);

+

+  CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));

+  CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));

+  CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));

+  CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));

+  CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));

+

+  Status = PxeBcFlushStaionIp (Private, &Private->StationIp, &Private->SubnetMask);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Check the selected offer whether BINL retry is needed.

+  //

+  Status = PxeBcHandleDhcp4Offer (Private);

+

+  AsciiPrint ("\n  Station IP address is ");

+

+  PxeBcShowIp4Addr (&Private->StationIp.v4);

+

+ON_EXIT:

+  if (EFI_ERROR (Status)) {

+    Dhcp4->Stop (Dhcp4);

+    Dhcp4->Configure (Dhcp4, NULL);

+  } else {

+    ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));

+    Dhcp4->Configure (Dhcp4, &Config);

+    Private->IsAddressOk = TRUE;

+  }

+

+  return Status;

+}

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h
new file mode 100644
index 0000000..bc21b21
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h
@@ -0,0 +1,389 @@
+/** @file

+  Functions declaration related with DHCPv4 for UefiPxeBc Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_PXEBC_DHCP4_H__

+#define __EFI_PXEBC_DHCP4_H__

+

+#define PXEBC_DHCP4_OPTION_MAX_NUM         16

+#define PXEBC_DHCP4_OPTION_MAX_SIZE        312

+#define PXEBC_DHCP4_PACKET_MAX_SIZE        1472

+#define PXEBC_DHCP4_S_PORT                 67

+#define PXEBC_DHCP4_C_PORT                 68

+#define PXEBC_BS_DOWNLOAD_PORT             69

+#define PXEBC_BS_DISCOVER_PORT             4011

+#define PXEBC_DHCP4_OPCODE_REQUEST         1

+#define PXEBC_DHCP4_OPCODE_REPLY           2

+#define PXEBC_DHCP4_MSG_TYPE_REQUEST       3

+#define PXEBC_DHCP4_MAGIC                  0x63538263 // network byte order

+

+//

+// Dhcp Options

+//

+#define PXEBC_DHCP4_TAG_PAD                0    // Pad Option

+#define PXEBC_DHCP4_TAG_EOP                255  // End Option

+#define PXEBC_DHCP4_TAG_NETMASK            1    // Subnet Mask

+#define PXEBC_DHCP4_TAG_TIME_OFFSET        2    // Time Offset from UTC

+#define PXEBC_DHCP4_TAG_ROUTER             3    // Router option,

+#define PXEBC_DHCP4_TAG_TIME_SERVER        4    // Time Server

+#define PXEBC_DHCP4_TAG_NAME_SERVER        5    // Name Server

+#define PXEBC_DHCP4_TAG_DNS_SERVER         6    // Domain Name Server

+#define PXEBC_DHCP4_TAG_HOSTNAME           12   // Host Name

+#define PXEBC_DHCP4_TAG_BOOTFILE_LEN       13   // Boot File Size

+#define PXEBC_DHCP4_TAG_DUMP               14   // Merit Dump File

+#define PXEBC_DHCP4_TAG_DOMAINNAME         15   // Domain Name

+#define PXEBC_DHCP4_TAG_ROOTPATH           17   // Root path

+#define PXEBC_DHCP4_TAG_EXTEND_PATH        18   // Extensions Path

+#define PXEBC_DHCP4_TAG_EMTU               22   // Maximum Datagram Reassembly Size

+#define PXEBC_DHCP4_TAG_TTL                23   // Default IP Time-to-live

+#define PXEBC_DHCP4_TAG_BROADCAST          28   // Broadcast Address

+#define PXEBC_DHCP4_TAG_NIS_DOMAIN         40   // Network Information Service Domain

+#define PXEBC_DHCP4_TAG_NIS_SERVER         41   // Network Information Servers

+#define PXEBC_DHCP4_TAG_NTP_SERVER         42   // Network Time Protocol Servers

+#define PXEBC_DHCP4_TAG_VENDOR             43   // Vendor Specific Information

+#define PXEBC_DHCP4_TAG_REQUEST_IP         50   // Requested IP Address

+#define PXEBC_DHCP4_TAG_LEASE              51   // IP Address Lease Time

+#define PXEBC_DHCP4_TAG_OVERLOAD           52   // Option Overload

+#define PXEBC_DHCP4_TAG_MSG_TYPE           53   // DHCP Message Type

+#define PXEBC_DHCP4_TAG_SERVER_ID          54   // Server Identifier

+#define PXEBC_DHCP4_TAG_PARA_LIST          55   // Parameter Request List

+#define PXEBC_DHCP4_TAG_MAXMSG             57   // Maximum DHCP Message Size

+#define PXEBC_DHCP4_TAG_T1                 58   // Renewal (T1) Time Value

+#define PXEBC_DHCP4_TAG_T2                 59   // Rebinding (T2) Time Value

+#define PXEBC_DHCP4_TAG_CLASS_ID           60   // Vendor class identifier

+#define PXEBC_DHCP4_TAG_CLIENT_ID          61   // Client-identifier

+#define PXEBC_DHCP4_TAG_TFTP               66   // TFTP server name

+#define PXEBC_DHCP4_TAG_BOOTFILE           67   // Bootfile name

+#define PXEBC_PXE_DHCP4_TAG_ARCH           93

+#define PXEBC_PXE_DHCP4_TAG_UNDI           94

+#define PXEBC_PXE_DHCP4_TAG_UUID           97

+//

+// Sub-Options in Dhcp Vendor Option

+//

+#define PXEBC_VENDOR_TAG_MTFTP_IP          1

+#define PXEBC_VENDOR_TAG_MTFTP_CPORT       2

+#define PXEBC_VENDOR_TAG_MTFTP_SPORT       3

+#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT     4

+#define PXEBC_VENDOR_TAG_MTFTP_DELAY       5

+#define PXEBC_VENDOR_TAG_DISCOVER_CTRL     6

+#define PXEBC_VENDOR_TAG_DISCOVER_MCAST    7

+#define PXEBC_VENDOR_TAG_BOOT_SERVERS      8

+#define PXEBC_VENDOR_TAG_BOOT_MENU         9

+#define PXEBC_VENDOR_TAG_MENU_PROMPT       10

+#define PXEBC_VENDOR_TAG_MCAST_ALLOC       11

+#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES  12

+#define PXEBC_VENDOR_TAG_BOOT_ITEM         71

+

+#define PXEBC_BOOT_REQUEST_TIMEOUT         1

+#define PXEBC_BOOT_REQUEST_RETRIES         4

+

+#define PXEBC_DHCP4_OVERLOAD_FILE          1

+#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME   2

+

+

+//

+// The array index of the DHCP4 option tag interested

+//

+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN 0

+#define PXEBC_DHCP4_TAG_INDEX_VENDOR       1

+#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD     2

+#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE     3

+#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID    4

+#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID     5

+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE     6

+#define PXEBC_DHCP4_TAG_INDEX_MAX          7

+

+//

+// Dhcp4 and Dhcp6 share this definition, and corresponding

+// relatioinship is as follows:

+//

+//   Dhcp4Discover <> Dhcp6Solicit

+//   Dhcp4Offer    <> Dhcp6Advertise

+//   Dhcp4Request  <> Dhcp6Request

+//   Dhcp4Ack      <> DHcp6Reply

+//

+typedef enum {

+  PxeOfferTypeDhcpOnly,

+  PxeOfferTypeDhcpPxe10,

+  PxeOfferTypeDhcpWfm11a,

+  PxeOfferTypeDhcpBinl,

+  PxeOfferTypeProxyPxe10,

+  PxeOfferTypeProxyWfm11a,

+  PxeOfferTypeProxyBinl,

+  PxeOfferTypeBootp,

+  PxeOfferTypeMax

+} PXEBC_OFFER_TYPE;

+

+#define BIT(x)                (1 << x)

+#define CTRL(x)               (0x1F & (x))

+#define DEFAULT_CLASS_ID_DATA "PXEClient:Arch:?????:????:??????"

+#define DEFAULT_UNDI_TYPE     1

+#define DEFAULT_UNDI_MAJOR    3

+#define DEFAULT_UNDI_MINOR    0

+

+#define MTFTP_VENDOR_OPTION_BIT_MAP \

+  (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \

+   BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \

+   BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \

+   BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \

+   BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY))

+

+#define DISCOVER_VENDOR_OPTION_BIT_MAP \

+  (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \

+   BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \

+   BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \

+   BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \

+   BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))

+

+#define IS_VALID_BOOT_PROMPT(x) \

+  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) \

+   == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))

+

+#define IS_VALID_BOOT_MENU(x) \

+  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) \

+   == BIT (PXEBC_VENDOR_TAG_BOOT_MENU))

+

+#define IS_VALID_MTFTP_VENDOR_OPTION(x) \

+  (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) \

+   == MTFTP_VENDOR_OPTION_BIT_MAP)

+

+#define IS_VALID_DISCOVER_VENDOR_OPTION(x) \

+  (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0)

+

+#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \

+  (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) \

+   == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES))

+

+#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \

+  (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & \

+     BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \

+    == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32))

+

+#define SET_VENDOR_OPTION_BIT_MAP(x, y) \

+  (*(x + ((y) / 32)) = (UINT32) ((UINT32) ((x)[(y) / 32]) | BIT ((y) % 32)))

+

+#define GET_NEXT_DHCP_OPTION(Opt) \

+  (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \

+   sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1)

+

+#define GET_OPTION_BUFFER_LEN(Pkt) \

+  ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4)

+

+#define GET_NEXT_BOOT_SVR_ENTRY(Ent) \

+  (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + \

+   ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS))

+

+#define IS_PROXY_DHCP_OFFER(Offer) \

+  EFI_IP4_EQUAL (&(Offer)->Dhcp4.Header.YourAddr, &mZeroIp4Addr)

+

+#define IS_DISABLE_BCAST_DISCOVER(x) \

+  (((x) & BIT (0)) == BIT (0))

+

+#define IS_DISABLE_MCAST_DISCOVER(x) \

+  (((x) & BIT (1)) == BIT (1))

+

+#define IS_ENABLE_USE_SERVER_LIST(x) \

+  (((x) & BIT (2)) == BIT (2))

+

+#define IS_ENABLE_BOOT_FILE_NAME(x) \

+  (((x) & BIT (3)) == BIT (3))

+

+

+#pragma pack(1)

+typedef struct {

+  UINT8 ParaList[135];

+} PXEBC_DHCP4_OPTION_PARA;

+

+typedef struct {

+  UINT16  Size;

+} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE;

+

+typedef struct {

+  UINT8 Type;

+  UINT8 MajorVer;

+  UINT8 MinorVer;

+} PXEBC_DHCP4_OPTION_UNDI;

+

+typedef struct {

+  UINT8 Type;

+} PXEBC_DHCP4_OPTION_MESG;

+

+typedef struct {

+  UINT16 Type;

+} PXEBC_DHCP4_OPTION_ARCH;

+

+typedef struct {

+  UINT8 ClassIdentifier[10];

+  UINT8 ArchitecturePrefix[5];

+  UINT8 ArchitectureType[5];

+  UINT8 Lit3[1];

+  UINT8 InterfaceName[4];

+  UINT8 Lit4[1];

+  UINT8 UndiMajor[3];

+  UINT8 UndiMinor[3];

+} PXEBC_DHCP4_OPTION_CLID;

+

+typedef struct {

+  UINT8 Type;

+  UINT8 Guid[16];

+} PXEBC_DHCP4_OPTION_UUID;

+

+typedef struct {

+  UINT16 Type;

+  UINT16 Layer;

+} PXEBC_OPTION_BOOT_ITEM;

+

+#pragma pack()

+

+typedef union {

+  PXEBC_DHCP4_OPTION_PARA           *Para;

+  PXEBC_DHCP4_OPTION_UNDI           *Undi;

+  PXEBC_DHCP4_OPTION_ARCH           *Arch;

+  PXEBC_DHCP4_OPTION_CLID           *Clid;

+  PXEBC_DHCP4_OPTION_UUID           *Uuid;

+  PXEBC_DHCP4_OPTION_MESG           *Mesg;

+  PXEBC_DHCP4_OPTION_MAX_MESG_SIZE  *MaxMesgSize;

+} PXEBC_DHCP4_OPTION_ENTRY;

+

+typedef struct {

+  UINT16            Type;

+  UINT8             IpCnt;

+  EFI_IPv4_ADDRESS  IpAddr[1];

+} PXEBC_BOOT_SVR_ENTRY;

+

+typedef struct {

+  UINT16            Type;

+  UINT8             DescLen;

+  UINT8             DescStr[1];

+} PXEBC_BOOT_MENU_ENTRY;

+

+typedef struct {

+  UINT8             Timeout;

+  UINT8             Prompt[1];

+} PXEBC_MENU_PROMPT;

+

+typedef struct {

+  UINT32                BitMap[8];

+  EFI_IPv4_ADDRESS      MtftpIp;

+  UINT16                MtftpCPort;

+  UINT16                MtftpSPort;

+  UINT8                 MtftpTimeout;

+  UINT8                 MtftpDelay;

+  UINT8                 DiscoverCtrl;

+  EFI_IPv4_ADDRESS      DiscoverMcastIp;

+  EFI_IPv4_ADDRESS      McastIpBase;

+  UINT16                McastIpBlock;

+  UINT16                McastIpRange;

+  UINT16                BootSrvType;

+  UINT16                BootSrvLayer;

+  PXEBC_BOOT_SVR_ENTRY  *BootSvr;

+  UINT8                 BootSvrLen;

+  PXEBC_BOOT_MENU_ENTRY *BootMenu;

+  UINT8                 BootMenuLen;

+  PXEBC_MENU_PROMPT     *MenuPrompt;

+  UINT8                 MenuPromptLen;

+  UINT32                *CredType;

+  UINT8                 CredTypeLen;

+} PXEBC_VENDOR_OPTION;

+

+typedef union {

+  EFI_DHCP4_PACKET        Offer;

+  EFI_DHCP4_PACKET        Ack;

+  UINT8                   Buffer[PXEBC_DHCP4_PACKET_MAX_SIZE];

+} PXEBC_DHCP4_PACKET;

+

+typedef struct {

+  PXEBC_DHCP4_PACKET      Packet;

+  PXEBC_OFFER_TYPE        OfferType;

+  EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_TAG_INDEX_MAX];

+  PXEBC_VENDOR_OPTION     VendorOpt;

+} PXEBC_DHCP4_PACKET_CACHE;

+

+

+/**

+  Create a template DHCPv4 packet as a seed.

+

+  @param[out] Seed           Pointer to the seed packet.

+  @param[in]  Udp4           Pointer to EFI_UDP4_PROTOCOL.

+

+**/

+VOID

+PxeBcSeedDhcp4Packet (

+  OUT EFI_DHCP4_PACKET       *Seed,

+  IN  EFI_UDP4_PROTOCOL      *Udp4

+  );

+

+

+/**

+  Parse the cached DHCPv4 packet, including all the options.

+

+  @param[in]  Cache4             Pointer to cached DHCPv4 packet.

+

+  @retval     EFI_SUCCESS        Parsed the DHCPv4 packet successfully.

+  @retval     EFI_DEVICE_ERROR   Failed to parse and invalid packet.

+

+**/

+EFI_STATUS

+PxeBcParseDhcp4Packet (

+  IN PXEBC_DHCP4_PACKET_CACHE    *Cache4

+  );

+

+

+/**

+  Build and send out the request packet for the bootfile, and parse the reply.

+

+  @param[in]  Private               Pointer to PxeBc private data.

+  @param[in]  Type                  PxeBc option boot item type.

+  @param[in]  Layer                 Pointer to option boot item layer.

+  @param[in]  UseBis                Use BIS or not.

+  @param[in]  DestIp                Pointer to the server address.

+  @param[in]  IpCount               The total count of the server address.

+  @param[in]  SrvList               Pointer to EFI_PXE_BASE_CODE_SRVLIST.

+

+  @retval     EFI_SUCCESS           Successfully discovered boot file.

+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.

+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.

+  @retval     Others                Failed to discover boot file.

+

+**/

+EFI_STATUS

+PxeBcDhcp4Discover (

+  IN  PXEBC_PRIVATE_DATA              *Private,

+  IN  UINT16                          Type,

+  IN  UINT16                          *Layer,

+  IN  BOOLEAN                         UseBis,

+  IN  EFI_IP_ADDRESS                  *DestIp,

+  IN  UINT16                          IpCount,

+  IN  EFI_PXE_BASE_CODE_SRVLIST       *SrvList

+  );

+

+

+/**

+  Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.

+

+  @param[in]  Private           Pointer to PxeBc private data.

+  @param[in]  Dhcp4             Pointer to the EFI_DHCP4_PROTOCOL

+

+  @retval EFI_SUCCESS           The D.O.R.A process successfully finished.

+  @retval Others                Failed to finish the D.O.R.A process.

+

+**/

+EFI_STATUS

+PxeBcDhcp4Dora (

+  IN PXEBC_PRIVATE_DATA         *Private,

+  IN EFI_DHCP4_PROTOCOL         *Dhcp4

+  );

+

+#endif

+

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
new file mode 100644
index 0000000..0b7cf1f
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
@@ -0,0 +1,1531 @@
+/** @file

+  Functions implementation related with DHCPv6 for UefiPxeBc Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "PxeBcImpl.h"

+

+

+/**

+  Parse out a DHCPv6 option by OptTag, and find the position in buffer.

+

+  @param[in]  Buffer        The pointer to the option buffer.

+  @param[in]  Length        Length of the option buffer.

+  @param[in]  OptTag        The required option tag.

+

+  @retval     NULL          Failed to parse the required option.

+  @retval     Others        The postion of the required option in buffer.

+

+**/

+EFI_DHCP6_PACKET_OPTION *

+PxeBcParseDhcp6Options (

+  IN UINT8                       *Buffer,

+  IN UINT32                      Length,

+  IN UINT16                      OptTag

+  )

+{

+  EFI_DHCP6_PACKET_OPTION        *Option;

+  UINT32                         Offset;

+

+  Option  = (EFI_DHCP6_PACKET_OPTION *) Buffer;

+  Offset  = 0;

+

+  //

+  // OpLen and OpCode here are both stored in network order.

+  //

+  while (Offset < Length) {

+

+    if (NTOHS (Option->OpCode) == OptTag) {

+

+      return Option;

+    }

+

+    Offset += (NTOHS(Option->OpLen) + 4);

+    Option  = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);

+  }

+

+  return NULL;

+}

+

+

+/**

+  Build the options buffer for the DHCPv6 request packet.

+

+  @param[in]  Private             The pointer to PxeBc private data.

+  @param[out] OptList             The pointer to the option pointer array.

+  @param[in]  Buffer              The pointer to the buffer to contain the option list.

+

+  @return     Index               The count of the built-in options.

+

+**/

+UINT32

+PxeBcBuildDhcp6Options (

+  IN  PXEBC_PRIVATE_DATA           *Private,

+  OUT EFI_DHCP6_PACKET_OPTION      **OptList,

+  IN  UINT8                        *Buffer

+  )

+{

+  PXEBC_DHCP6_OPTION_ENTRY         OptEnt;

+  UINT32                           Index;

+  UINT16                           Value;

+

+  Index       = 0;

+  OptList[0]  = (EFI_DHCP6_PACKET_OPTION *) Buffer;

+

+  //

+  // Append client option request option

+  //

+  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_ORO);

+  OptList[Index]->OpLen      = HTONS (4);

+  OptEnt.Oro                 = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;

+  OptEnt.Oro->OpCode[0]      = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL);

+  OptEnt.Oro->OpCode[1]      = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM);

+  Index++;

+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);

+

+  //

+  // Append client network device interface option

+  //

+  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_UNDI);

+  OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_UNDI));

+  OptEnt.Undi                = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;

+

+  if (Private->Nii != NULL) {

+    OptEnt.Undi->Type        = Private->Nii->Type;

+    OptEnt.Undi->MajorVer    = Private->Nii->MajorVer;

+    OptEnt.Undi->MinorVer    = Private->Nii->MinorVer;

+  } else {

+    OptEnt.Undi->Type        = DEFAULT_UNDI_TYPE;

+    OptEnt.Undi->MajorVer    = DEFAULT_UNDI_MAJOR;

+    OptEnt.Undi->MinorVer    = DEFAULT_UNDI_MINOR;

+  }

+

+  OptEnt.Undi->Reserved      = 0;

+  Index++;

+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);

+

+  //

+  // Append client system architecture option

+  //

+  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_ARCH);

+  OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));

+  OptEnt.Arch                = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;

+  Value                      = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);

+  CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));

+  Index++;

+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);

+

+  //

+  // Append vendor class option to store the PXE class identifier.

+  //

+  OptList[Index]->OpCode       = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS);

+  OptList[Index]->OpLen        = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));

+  OptEnt.VendorClass           = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;

+  OptEnt.VendorClass->Vendor   = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);

+  OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID));

+  CopyMem (

+    &OptEnt.VendorClass->ClassId,

+    DEFAULT_CLASS_ID_DATA,

+    sizeof (PXEBC_CLASS_ID)

+    );

+  PxeBcUintnToAscDecWithFormat (

+    EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,

+    OptEnt.VendorClass->ClassId.ArchitectureType,

+    sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)

+    );

+

+  if (Private->Nii != NULL) {

+    CopyMem (

+      OptEnt.VendorClass->ClassId.InterfaceName,

+      Private->Nii->StringId,

+      sizeof (OptEnt.VendorClass->ClassId.InterfaceName)

+      );

+    PxeBcUintnToAscDecWithFormat (

+      Private->Nii->MajorVer,

+      OptEnt.VendorClass->ClassId.UndiMajor,

+      sizeof (OptEnt.VendorClass->ClassId.UndiMajor)

+      );

+    PxeBcUintnToAscDecWithFormat (

+      Private->Nii->MinorVer,

+      OptEnt.VendorClass->ClassId.UndiMinor,

+      sizeof (OptEnt.VendorClass->ClassId.UndiMinor)

+      );

+  }

+

+  Index++;

+

+  return Index;

+}

+

+

+/**

+  Cache the DHCPv6 packet.

+

+  @param[in]  Dst          The pointer to the cache buffer for DHCPv6 packet.

+  @param[in]  Src          The pointer to the DHCPv6 packet to be cached.

+

+**/

+VOID

+PxeBcCacheDhcp6Packet (

+  IN EFI_DHCP6_PACKET          *Dst,

+  IN EFI_DHCP6_PACKET          *Src

+  )

+{

+  ASSERT (Dst->Size >= Src->Length);

+

+  CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);

+  Dst->Length = Src->Length;

+}

+

+

+/**

+  Free all the nodes in the list for boot file.

+

+  @param[in]  Head            The pointer to the head of list.

+

+**/

+VOID

+PxeBcFreeBootFileOption (

+  IN LIST_ENTRY               *Head

+  )

+{

+  LIST_ENTRY                  *Entry;

+  LIST_ENTRY                  *NextEntry;

+  PXEBC_DHCP6_OPTION_NODE     *Node;

+

+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) {

+    Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link);

+    RemoveEntryList (Entry);

+    FreePool (Node);

+  }

+}

+

+

+/**

+  Parse the Boot File URL option.

+

+  @param[out]     FileName     The pointer to the boot file name.

+  @param[in, out] SrvAddr      The pointer to the boot server address.

+  @param[in]      BootFile     The pointer to the boot file URL option data.

+  @param[in]      Length       The length of the boot file URL option data.

+

+  @retval EFI_ABORTED     User cancel operation.

+  @retval EFI_SUCCESS     Selected the boot menu successfully.

+  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.

+

+**/

+EFI_STATUS

+PxeBcExtractBootFileUrl (

+     OUT UINT8               **FileName,

+  IN OUT EFI_IPv6_ADDRESS    *SrvAddr,

+  IN     CHAR8               *BootFile,

+  IN     UINT16              Length

+  )

+{

+  UINT16                     PrefixLen;

+  UINT8                      *BootFileNamePtr;

+  UINT8                      *BootFileName;

+  UINT16                     BootFileNameLen;

+  CHAR8                      *TmpStr;

+  CHAR8                      *ServerAddressOption;

+  CHAR8                      *ServerAddress;

+  EFI_STATUS                 Status;

+

+  //

+  // The format of the Boot File URL option is:

+  //

+  //  0                   1                   2                   3

+  //  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

+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  // |       OPT_BOOTFILE_URL        |            option-len         |

+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  // |                                                               |

+  // .                  bootfile-url  (variable length)              .

+  // |                                                               |

+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+  //

+

+  //

+  // Based upon RFC 5970 and UEFI errata that will appear in chapter 21.3 of UEFI 2.3

+  // specification after 2.3 errata B and future UEFI Specifications after 2.3.

+  // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME

+  // As an example where the BOOTFILE_NAME is the EFI loader and

+  // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.

+  //

+  PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);

+

+  if (Length <= PrefixLen ||

+      CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) {

+    return EFI_NOT_FOUND;

+  }

+

+  BootFile = BootFile + PrefixLen;

+  Length   = (UINT16) (Length - PrefixLen);

+

+  TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1);

+  if (TmpStr == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  CopyMem (TmpStr, BootFile, Length);

+  TmpStr[Length] = '\0';

+

+  //

+  // Get the part of SERVER_ADDRESS string.

+  //

+  ServerAddressOption = TmpStr;

+  if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) {

+    FreePool (TmpStr);

+    return EFI_INVALID_PARAMETER;

+  }

+

+  ServerAddressOption ++;

+  ServerAddress = ServerAddressOption;

+  while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {

+    ServerAddress++;

+  }

+

+  if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {

+    FreePool (TmpStr);

+    return EFI_INVALID_PARAMETER;

+  }

+

+  *ServerAddress = '\0';

+

+  //

+  // Convert the string of server address to Ipv6 address format and store it.

+  //

+  Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);

+  if (EFI_ERROR (Status)) {

+    FreePool (TmpStr);

+    return Status;

+  }

+

+  //

+  // Get the part of BOOTFILE_NAME string.

+  //

+  BootFileNamePtr = (UINT8*)((UINTN)ServerAddress + 1);

+  if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {

+    FreePool (TmpStr);

+    return EFI_INVALID_PARAMETER;

+  }

+

+  ++BootFileNamePtr;

+  BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);

+  if (BootFileNameLen != 0 || FileName != NULL) {

+    BootFileName = (UINT8 *) AllocateZeroPool (BootFileNameLen);

+    if (BootFileName == NULL) {

+      FreePool (TmpStr);

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    CopyMem (BootFileName, BootFileNamePtr, BootFileNameLen);

+    BootFileName[BootFileNameLen - 1] = '\0';

+    *FileName = BootFileName;

+  }

+

+

+  FreePool (TmpStr);

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Parse the Boot File Parameter option.

+

+  @param[in]  BootFilePara      The pointer to boot file parameter option data.

+  @param[out] BootFileSize      The pointer to the parsed boot file size.

+

+  @retval EFI_SUCCESS     Successfully obtained the boot file size from parameter option.

+  @retval EFI_NOT_FOUND   Failed to extract the boot file size from parameter option.

+

+**/

+EFI_STATUS

+PxeBcExtractBootFileParam (

+  IN  CHAR8                  *BootFilePara,

+  OUT UINT16                 *BootFileSize

+  )

+{

+  UINT16                     Length;

+  UINT8                      Index;

+  UINT8                      Digit;

+  UINT32                     Size;

+

+  CopyMem (&Length, BootFilePara, sizeof (UINT16));

+  Length = NTOHS (Length);

+

+  //

+  // The BootFile Size should be 1~5 byte ASCII strings

+  //

+  if (Length < 1 || Length > 5) {

+    return EFI_NOT_FOUND;

+  }

+

+  //

+  // Extract the value of BootFile Size.

+  //

+  BootFilePara = BootFilePara + sizeof (UINT16);

+  Size         = 0;

+  for (Index = 0; Index < Length; Index++) {

+    if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {

+      return EFI_NOT_FOUND;

+    }

+

+    Size = (Size + Digit) * 10;

+  }

+

+  Size = Size / 10;

+  if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {

+    return EFI_NOT_FOUND;

+  }

+

+  *BootFileSize = (UINT16) Size;

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Parse the cached DHCPv6 packet, including all the options.

+

+  @param[in]  Cache6           The pointer to a cached DHCPv6 packet.

+

+  @retval     EFI_SUCCESS      Parsed the DHCPv6 packet successfully.

+  @retval     EFI_DEVICE_ERROR Failed to parse and invalid the packet.

+

+**/

+EFI_STATUS

+PxeBcParseDhcp6Packet (

+  IN PXEBC_DHCP6_PACKET_CACHE  *Cache6

+  )

+{

+  EFI_DHCP6_PACKET             *Offer;

+  EFI_DHCP6_PACKET_OPTION      **Options;

+  EFI_DHCP6_PACKET_OPTION      *Option;

+  PXEBC_OFFER_TYPE             OfferType;

+  BOOLEAN                      IsProxyOffer;

+  BOOLEAN                      IsPxeOffer;

+  UINT32                       Offset;

+  UINT32                       Length;

+  UINT32                       EnterpriseNum;

+

+  IsProxyOffer = TRUE;

+  IsPxeOffer   = FALSE;

+  Offer        = &Cache6->Packet.Offer;

+  Options      = Cache6->OptList;

+

+  ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));

+

+  Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);

+  Offset  = 0;

+  Length  = GET_DHCP6_OPTION_SIZE (Offer);

+

+  //

+  // OpLen and OpCode here are both stored in network order, since they are from original packet.

+  //

+  while (Offset < Length) {

+

+    if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) {

+      Options[PXEBC_DHCP6_IDX_IA_NA] = Option;

+    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) {

+      //

+      // The server sends this option to inform the client about an URL to a boot file.

+      //

+      Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;

+    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) {

+      Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;

+    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) {

+      Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;

+    }

+

+    Offset += (NTOHS (Option->OpLen) + 4);

+    Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);

+  }

+

+  //

+  // The offer with assigned client address is a proxy offer.

+  // An ia_na option, embeded with valid ia_addr option and a status_code of success.

+  //

+  Option = Options[PXEBC_DHCP6_IDX_IA_NA];

+  if (Option != NULL && NTOHS(Option->OpLen) >= 12) {

+    Option = PxeBcParseDhcp6Options (

+               Option->Data + 12,

+               NTOHS (Option->OpLen),

+               PXEBC_DHCP6_OPT_STATUS_CODE

+               );

+    if (Option != NULL && Option->Data[0] == 0) {

+      IsProxyOffer = FALSE;

+    }

+  }

+

+  //

+  // The offer with "PXEClient" is a pxe offer.

+  //

+  Option        = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];

+  EnterpriseNum = PXEBC_DHCP6_ENTERPRISE_NUM;

+  if (Option != NULL &&

+      NTOHS(Option->OpLen) >= 13 &&

+      CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&

+      CompareMem (&Option->Data[4], DEFAULT_CLASS_ID_DATA, 9) == 0) {

+    IsPxeOffer = TRUE;

+  }

+

+  //

+  // Determine offer type of the dhcp6 packet.

+  //

+  if (IsPxeOffer) {

+    //

+    // It's a binl offer only with PXEClient.

+    //

+    OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;

+  } else {

+    //

+    // It's a dhcp only offer, which is a pure dhcp6 offer packet.

+    //

+    OfferType = PxeOfferTypeDhcpOnly;

+  }

+

+  Cache6->OfferType = OfferType;

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Cache the DHCPv6 ack packet, and parse it on demand.

+

+  @param[in]  Private             The pointer to PxeBc private data.

+  @param[in]  Ack                 The pointer to the DHCPv6 ack packet.

+  @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.

+

+**/

+VOID

+PxeBcCopyDhcp6Ack (

+  IN PXEBC_PRIVATE_DATA   *Private,

+  IN EFI_DHCP6_PACKET     *Ack,

+  IN BOOLEAN              Verified

+  )

+{

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+

+  Mode = Private->PxeBc.Mode;

+

+  PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);

+

+  if (Verified) {

+    //

+    // Parse the ack packet and store it into mode data if needed.

+    //

+    PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);

+    CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);

+    Mode->DhcpAckReceived = TRUE;

+  }

+}

+

+

+/**

+  Cache the DHCPv6 proxy offer packet according to the received order.

+

+  @param[in]  Private               The pointer to PxeBc private data.

+  @param[in]  OfferIndex            The received order of offer packets.

+

+**/

+VOID

+PxeBcCopyDhcp6Proxy (

+  IN PXEBC_PRIVATE_DATA     *Private,

+  IN UINT32                 OfferIndex

+  )

+{

+  EFI_PXE_BASE_CODE_MODE    *Mode;

+  EFI_DHCP6_PACKET          *Offer;

+

+  ASSERT (OfferIndex < Private->OfferNum);

+  ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);

+

+  Mode  = Private->PxeBc.Mode;

+  Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;

+

+  //

+  // Cache the proxy offer packet and parse it.

+  //

+  PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);

+  PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);

+

+  //

+  // Store this packet into mode data.

+  //

+  CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);

+  Mode->ProxyOfferReceived = TRUE;

+}

+

+

+/**

+  Retry to request bootfile name by the BINL offer.

+

+  @param[in]  Private              The pointer to PxeBc private data.

+  @param[in]  Index                The received order of offer packets.

+

+  @retval     EFI_SUCCESS          Successfully retried a request for the bootfile name.

+  @retval     EFI_DEVICE_ERROR     Failed to retry the bootfile name.

+

+**/

+EFI_STATUS

+PxeBcRetryDhcp6Binl (

+  IN PXEBC_PRIVATE_DATA  *Private,

+  IN UINT32              Index

+  )

+{

+  EFI_PXE_BASE_CODE_MODE    *Mode;

+  PXEBC_DHCP6_PACKET_CACHE  *Offer;

+  PXEBC_DHCP6_PACKET_CACHE  *Cache6;

+  EFI_IP_ADDRESS            ServerIp;

+  EFI_STATUS                Status;

+

+  ASSERT (Index < PXEBC_OFFER_MAX_NUM);

+  ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||

+          Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl);

+

+  Mode                  = Private->PxeBc.Mode;

+  Private->IsDoDiscover = FALSE;

+  Offer                 = &Private->OfferBuffer[Index].Dhcp6;

+

+  ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);

+  //

+  // Parse out the next server address from the last offer, and store it

+  //

+  Status = PxeBcExtractBootFileUrl (

+             NULL,

+             &ServerIp.v6,

+             (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),

+             NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.

+  //

+  Status = PxeBcDhcp6Discover (

+             Private,

+             0,

+             NULL,

+             FALSE,

+             &ServerIp

+             );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Cache6 = &Private->ProxyOffer.Dhcp6;

+  Status = PxeBcParseDhcp6Packet (Cache6);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (Cache6->OfferType != PxeOfferTypeProxyPxe10 &&

+      Cache6->OfferType != PxeOfferTypeProxyWfm11a &&

+      Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {

+    //

+    // This BINL ack doesn't have discovery option set or multicast option set

+    // or bootfile name specified.

+    //

+    return EFI_DEVICE_ERROR;

+  }

+

+  Mode->ProxyOfferReceived = TRUE;

+  CopyMem (

+    &Mode->ProxyOffer.Dhcpv6,

+    &Cache6->Packet.Offer.Dhcp6,

+    Cache6->Packet.Offer.Length

+    );

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.

+

+  @param[in]  Private               The pointer to PXEBC_PRIVATE_DATA.

+  @param[in]  RcvdOffer             The pointer to the received offer packet.

+

+**/

+VOID

+PxeBcCacheDhcp6Offer (

+  IN PXEBC_PRIVATE_DATA     *Private,

+  IN EFI_DHCP6_PACKET       *RcvdOffer

+  )

+{

+  PXEBC_DHCP6_PACKET_CACHE  *Cache6;

+  EFI_DHCP6_PACKET          *Offer;

+  PXEBC_OFFER_TYPE          OfferType;

+

+  Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;

+  Offer  = &Cache6->Packet.Offer;

+

+  //

+  // Cache the content of DHCPv6 packet firstly.

+  //

+  PxeBcCacheDhcp6Packet (Offer, RcvdOffer);

+

+  //

+  // Validate the DHCPv6 packet, and parse the options and offer type.

+  //

+  if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {

+    return ;

+  }

+

+  //

+  // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.

+  //

+  OfferType = Cache6->OfferType;

+  ASSERT (OfferType < PxeOfferTypeMax);

+  ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);

+

+  if (IS_PROXY_OFFER (OfferType)) {

+    //

+    // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.

+    //

+    Private->IsProxyRecved = TRUE;

+

+    if (OfferType == PxeOfferTypeProxyBinl) {

+      //

+      // Cache all proxy BINL offers.

+      //

+      Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;

+      Private->OfferCount[OfferType]++;

+    } else if (Private->OfferCount[OfferType] > 0) {

+      //

+      // Only cache the first PXE10/WFM11a offer, and discard the others.

+      //

+      Private->OfferIndex[OfferType][0] = Private->OfferNum;

+      Private->OfferCount[OfferType]    = 1;

+    } else {

+      return;

+    }

+  } else {

+    //

+    // It's a DHCPv6 offer with yiaddr, and cache them all.

+    //

+    Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;

+    Private->OfferCount[OfferType]++;

+  }

+

+  Private->OfferNum++;

+}

+

+

+/**

+  Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.

+

+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.

+

+**/

+VOID

+PxeBcSelectDhcp6Offer (

+  IN PXEBC_PRIVATE_DATA     *Private

+  )

+{

+  UINT32                Index;

+  UINT32                OfferIndex;

+  PXEBC_OFFER_TYPE      OfferType;

+

+  Private->SelectIndex = 0;

+

+  if (Private->IsOfferSorted) {

+    //

+    // Select offer by default policy.

+    //

+    if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {

+      //

+      // 1. DhcpPxe10 offer

+      //

+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {

+      //

+      // 2. DhcpWfm11a offer

+      //

+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&

+               Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {

+      //

+      // 3. DhcpOnly offer and ProxyPxe10 offer.

+      //

+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;

+      Private->SelectProxyType = PxeOfferTypeProxyPxe10;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&

+               Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {

+      //

+      // 4. DhcpOnly offer and ProxyWfm11a offer.

+      //

+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;

+      Private->SelectProxyType = PxeOfferTypeProxyWfm11a;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {

+      //

+      // 5. DhcpBinl offer.

+      //

+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;

+

+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&

+               Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {

+      //

+      // 6. DhcpOnly offer and ProxyBinl offer.

+      //

+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;

+      Private->SelectProxyType = PxeOfferTypeProxyBinl;

+

+    } else {

+      //

+      // 7. DhcpOnly offer with bootfilename.

+      //

+      for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {

+        OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];

+        if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) {

+          Private->SelectIndex = OfferIndex + 1;

+          break;

+        }

+      }

+    }

+  } else {

+    //

+    // Select offer by received order.

+    //

+    for (Index = 0; Index < Private->OfferNum; Index++) {

+

+      OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;

+

+      if (IS_PROXY_OFFER (OfferType)) {

+        //

+        // Skip proxy offers

+        //

+        continue;

+      }

+

+      if (!Private->IsProxyRecved &&

+          OfferType == PxeOfferTypeDhcpOnly &&

+          Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {

+        //

+        // Skip if DhcpOnly offer without any other proxy offers or bootfilename.

+        //

+        continue;

+      }

+

+      Private->SelectIndex = Index + 1;

+      break;

+    }

+  }

+}

+

+

+/**

+  Handle the DHCPv6 offer packet.

+

+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.

+

+  @retval     EFI_SUCCESS         Handled the DHCPv6 offer packet successfully.

+  @retval     EFI_NO_RESPONSE     No response to the following request packet.

+

+**/

+EFI_STATUS

+PxeBcHandleDhcp6Offer (

+  IN PXEBC_PRIVATE_DATA            *Private

+  )

+{

+  PXEBC_DHCP6_PACKET_CACHE         *Cache6;

+  EFI_STATUS                       Status;

+  PXEBC_OFFER_TYPE                 OfferType;

+  UINT32                           ProxyIndex;

+  UINT32                           SelectIndex;

+  UINT32                           Index;

+

+  ASSERT (Private->SelectIndex > 0);

+  SelectIndex = (UINT32) (Private->SelectIndex - 1);

+  ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);

+  Cache6      = &Private->OfferBuffer[SelectIndex].Dhcp6;

+  Status      = EFI_SUCCESS;

+

+  if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {

+    //

+    // DhcpBinl offer is selected, so need try to request bootfilename by this offer.

+    //

+    if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {

+      Status = EFI_NO_RESPONSE;

+    }

+  } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {

+

+    if (Private->IsProxyRecved) {

+      //

+      // DhcpOnly offer is selected, so need try to request bootfilename.

+      //

+      ProxyIndex = 0;

+      if (Private->IsOfferSorted) {

+        //

+        // The proxy offer should be determined if select by default policy.

+        // IsOfferSorted means all offers are labeled by OfferIndex.

+        //

+        ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);

+

+        if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {

+          //

+          // Try all the cached ProxyBinl offer one by one to request bootfilename.

+          //

+          for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {

+

+            ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];

+            if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {

+              break;

+            }

+          }

+          if (Index == Private->OfferCount[Private->SelectProxyType]) {

+            Status = EFI_NO_RESPONSE;

+          }

+        } else {

+          //

+          // For other proxy offers (pxe10 or wfm11a), only one is buffered.

+          //

+          ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];

+        }

+      } else {

+        //

+        // The proxy offer should not be determined if select by received order.

+        //

+        Status = EFI_NO_RESPONSE;

+

+        for (Index = 0; Index < Private->OfferNum; Index++) {

+

+          OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;

+

+          if (!IS_PROXY_OFFER (OfferType)) {

+            //

+            // Skip non proxy dhcp offers.

+            //

+            continue;

+          }

+

+          if (OfferType == PxeOfferTypeProxyBinl) {

+            //

+            // Try all the cached ProxyBinl offer one by one to request bootfilename.

+            //

+            if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {

+              continue;

+            }

+          }

+

+          Private->SelectProxyType = OfferType;

+          ProxyIndex               = Index;

+          Status                   = EFI_SUCCESS;

+          break;

+        }

+      }

+

+      if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {

+        //

+        // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.

+        //

+        PxeBcCopyDhcp6Proxy (Private, ProxyIndex);

+      }

+    } else {

+      //

+      //  Othewise, the bootfilename must be included in DhcpOnly offer.

+      //

+      ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);

+    }

+  }

+

+  if (!EFI_ERROR (Status)) {

+    //

+    // All PXE boot information is ready by now.

+    //

+    PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);

+    Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;

+  }

+

+  return Status;

+}

+

+

+/**

+  Unregister the address by Ip6Config protocol.

+

+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.

+

+**/

+VOID

+PxeBcUnregisterIp6Address (

+  IN PXEBC_PRIVATE_DATA           *Private

+  )

+{

+  if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {

+    //

+    // PXE driver change the policy of IP6 driver, it's a chance to recover.

+    // Keep the point and there is no enough requirements to do recovery.

+    //

+  }

+}

+

+

+/**

+  Register the ready address by Ip6Config protocol.

+

+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.

+  @param[in]  Address             The pointer to the ready address.

+

+  @retval     EFI_SUCCESS         Registered the address succesfully.

+  @retval     Others              Failed to register the address.

+

+**/

+EFI_STATUS

+PxeBcRegisterIp6Address (

+  IN PXEBC_PRIVATE_DATA            *Private,

+  IN EFI_IPv6_ADDRESS              *Address

+  )

+{

+  EFI_IP6_PROTOCOL                 *Ip6;

+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;

+  EFI_IP6_CONFIG_POLICY            Policy;

+  EFI_IP6_CONFIG_MANUAL_ADDRESS    CfgAddr;

+  UINTN                            DataSize;

+  EFI_EVENT                        TimeOutEvt;

+  EFI_EVENT                        MappedEvt;

+  EFI_STATUS                       Status;

+

+  Status     = EFI_SUCCESS;

+  TimeOutEvt = NULL;

+  MappedEvt  = NULL;

+  DataSize   = sizeof (EFI_IP6_CONFIG_POLICY);

+  Ip6Cfg     = Private->Ip6Cfg;

+  Ip6        = Private->Ip6;

+

+  ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));

+  CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));

+

+  //

+  // Get and store the current policy of IP6 driver.

+  //

+  Status = Ip6Cfg->GetData (

+                     Ip6Cfg,

+                     Ip6ConfigDataTypePolicy,

+                     &DataSize,

+                     &Private->Ip6Policy

+                     );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // There is no channel between IP6 and PXE driver about address setting,

+  // so it has to set the new address by Ip6ConfigProtocol manually.

+  //

+  Policy = Ip6ConfigPolicyManual;

+  Status = Ip6Cfg->SetData (

+                     Ip6Cfg,

+                     Ip6ConfigDataTypePolicy,

+                     sizeof(EFI_IP6_CONFIG_POLICY),

+                     &Policy

+                     );

+  if (EFI_ERROR (Status)) {

+    //

+    // There is no need to recover later.

+    //

+    Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;

+    goto ON_EXIT;

+  }

+

+  //

+  // Create a timer as setting address timeout event since DAD in IP6 driver.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_TIMER,

+                  TPL_CALLBACK,

+                  NULL,

+                  NULL,

+                  &TimeOutEvt

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Create a notify event to set address flag when DAD if IP6 driver succeeded.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  PxeBcCommonNotify,

+                  &Private->IsAddressOk,

+                  &MappedEvt

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  Status = Ip6Cfg->RegisterDataNotify (

+                     Ip6Cfg,

+                     Ip6ConfigDataTypeManualAddress,

+                     MappedEvt

+                     );

+  if (EFI_ERROR(Status)) {

+    goto ON_EXIT;

+  }

+

+  Status = Ip6Cfg->SetData (

+                     Ip6Cfg,

+                     Ip6ConfigDataTypeManualAddress,

+                     sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS),

+                     &CfgAddr

+                     );

+  if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Start the 5 secondes timer to wait for setting address.

+  //

+  Status = EFI_NO_MAPPING;

+  gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT);

+

+  while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {

+    Ip6->Poll (Ip6);

+    if (Private->IsAddressOk) {

+      Status = EFI_SUCCESS;

+      break;

+    }

+  }

+

+ON_EXIT:

+  if (MappedEvt != NULL) {

+    Ip6Cfg->UnregisterDataNotify (

+              Ip6Cfg,

+              Ip6ConfigDataTypeManualAddress,

+              MappedEvt

+              );

+    gBS->CloseEvent (MappedEvt);

+  }

+  if (TimeOutEvt != NULL) {

+    gBS->CloseEvent (TimeOutEvt);

+  }

+  return Status;

+}

+

+

+/**

+  EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver

+  to intercept events that occurred in the configuration process.

+

+  @param[in]  This              The pointer to the EFI DHCPv6 Protocol.

+  @param[in]  Context           The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().

+  @param[in]  CurrentState      The current operational state of the EFI DHCPv Protocol driver.

+  @param[in]  Dhcp6Event        The event that occurs in the current state, which usually means a

+                                state transition.

+  @param[in]  Packet            The DHCPv6 packet that is going to be sent or was already received.

+  @param[out] NewPacket         The packet that is used to replace the Packet above.

+

+  @retval EFI_SUCCESS           Told the EFI DHCPv6 Protocol driver to continue the DHCP process.

+  @retval EFI_NOT_READY         Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol

+                                driver will continue to wait for more packets.

+  @retval EFI_ABORTED           Told the EFI DHCPv6 Protocol driver to abort the current process.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcDhcp6CallBack (

+  IN  EFI_DHCP6_PROTOCOL           *This,

+  IN  VOID                         *Context,

+  IN  EFI_DHCP6_STATE              CurrentState,

+  IN  EFI_DHCP6_EVENT              Dhcp6Event,

+  IN  EFI_DHCP6_PACKET             *Packet,

+  OUT EFI_DHCP6_PACKET             **NewPacket     OPTIONAL

+  )

+{

+  PXEBC_PRIVATE_DATA                  *Private;

+  EFI_PXE_BASE_CODE_MODE              *Mode;

+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;

+  EFI_DHCP6_PACKET                    *SelectAd;

+  EFI_STATUS                          Status;

+  BOOLEAN                             Received;

+

+  if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&

+      (Dhcp6Event != Dhcp6SelectAdvertise) &&

+      (Dhcp6Event != Dhcp6SendSolicit) &&

+      (Dhcp6Event != Dhcp6SendRequest) &&

+      (Dhcp6Event != Dhcp6RcvdReply)) {

+    return EFI_SUCCESS;

+  }

+

+  ASSERT (Packet != NULL);

+

+  Private   = (PXEBC_PRIVATE_DATA *) Context;

+  Mode      = Private->PxeBc.Mode;

+  Callback  = Private->PxeBcCallback;

+

+  //

+  // Callback to user when any traffic ocurred if has.

+  //

+  if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) {

+    Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);

+    Status = Callback->Callback (

+                         Callback,

+                         Private->Function,

+                         Received,

+                         Packet->Length,

+                         (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6

+                         );

+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {

+      return EFI_ABORTED;

+    }

+  }

+

+  Status = EFI_SUCCESS;

+

+  switch (Dhcp6Event) {

+

+  case Dhcp6SendSolicit:

+    //

+    // Cache the dhcp discover packet to mode data directly.

+    //

+    CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);

+    break;

+

+  case Dhcp6RcvdAdvertise:

+    Status = EFI_NOT_READY;

+    if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {

+      //

+      // Cache the dhcp offers to OfferBuffer[] for select later, and record

+      // the OfferIndex and OfferCount.

+      //

+      PxeBcCacheDhcp6Offer (Private, Packet);

+    }

+    break;

+

+  case Dhcp6SendRequest:

+    //

+    // Store the request packet as seed packet for discover.

+    //

+    if (Private->Dhcp6Request != NULL) {

+      FreePool (Private->Dhcp6Request);

+    }

+    Private->Dhcp6Request = AllocateZeroPool (Packet->Size);

+    if (Private->Dhcp6Request != NULL) {

+      CopyMem (Private->Dhcp6Request, Packet, Packet->Size);

+    }

+    break;

+

+  case Dhcp6SelectAdvertise:

+    //

+    // Select offer by the default policy or by order, and record the SelectIndex

+    // and SelectProxyType.

+    //

+    PxeBcSelectDhcp6Offer (Private);

+

+    if (Private->SelectIndex == 0) {

+      Status = EFI_ABORTED;

+    } else {

+      ASSERT (NewPacket != NULL);

+      SelectAd   = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;

+      *NewPacket = AllocateZeroPool (SelectAd->Size);

+      ASSERT (*NewPacket != NULL);

+      CopyMem (*NewPacket, SelectAd, SelectAd->Size);

+    }

+    break;

+

+  case Dhcp6RcvdReply:

+    //

+    // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data

+    // without verification.

+    //

+    ASSERT (Private->SelectIndex != 0);

+    PxeBcCopyDhcp6Ack (Private, Packet, FALSE);

+    break;

+

+  default:

+    ASSERT (0);

+  }

+

+  return Status;

+}

+

+

+/**

+  Build and send out the request packet for the bootfile, and parse the reply.

+

+  @param[in]  Private               The pointer to PxeBc private data.

+  @param[in]  Type                  PxeBc option boot item type.

+  @param[in]  Layer                 The pointer to option boot item layer.

+  @param[in]  UseBis                Use BIS or not.

+  @param[in]  DestIp                The pointer to the server address.

+

+  @retval     EFI_SUCCESS           Successfully discovered the boot file.

+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.

+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.

+  @retval     Others                Failed to discover the boot file.

+

+**/

+EFI_STATUS

+PxeBcDhcp6Discover (

+  IN  PXEBC_PRIVATE_DATA              *Private,

+  IN  UINT16                          Type,

+  IN  UINT16                          *Layer,

+  IN  BOOLEAN                         UseBis,

+  IN  EFI_IP_ADDRESS                  *DestIp

+  )

+{

+  EFI_PXE_BASE_CODE_UDP_PORT          SrcPort;

+  EFI_PXE_BASE_CODE_UDP_PORT          DestPort;

+  EFI_PXE_BASE_CODE_MODE              *Mode;

+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;

+  EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;

+  UINTN                               DiscoverLen;

+  EFI_DHCP6_PACKET                    *Request;

+  UINTN                               RequestLen;

+  EFI_DHCP6_PACKET                    *Reply;

+  UINT8                               *RequestOpt;

+  UINT8                               *DiscoverOpt;

+  UINTN                               ReadSize;

+  UINT16                              OpFlags;

+  UINT16                              OpCode;

+  UINT16                              OpLen;

+  UINT32                              Xid;

+  EFI_STATUS                          Status;

+

+  PxeBc       = &Private->PxeBc;

+  Mode        = PxeBc->Mode;

+  Request     = Private->Dhcp6Request;

+  SrcPort     = PXEBC_BS_DISCOVER_PORT;

+  DestPort    = PXEBC_BS_DISCOVER_PORT;

+  OpFlags     = 0;

+

+  if (!UseBis && Layer != NULL) {

+    *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;

+  }

+

+  if (Request == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));

+  if (Discover == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  //

+  // Build the discover packet by the cached request packet before.

+  //

+  Xid                     = NET_RANDOM (NetRandomInitSeed ());

+  Discover->TransactionId = HTONL (Xid);

+  Discover->MessageType   = Request->Dhcp6.Header.MessageType;

+  RequestOpt              = Request->Dhcp6.Option;

+  DiscoverOpt             = Discover->DhcpOptions;

+  DiscoverLen             = sizeof (EFI_DHCP6_HEADER);

+  RequestLen              = DiscoverLen;

+

+  while (RequestLen < Request->Length) {

+    OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);

+    OpLen  = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);

+    if (OpCode != EFI_DHCP6_IA_TYPE_NA &&

+        OpCode != EFI_DHCP6_IA_TYPE_TA) {

+      //

+      // Copy all the options except IA option.

+      //

+      CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);

+      DiscoverOpt += (OpLen + 4);

+      DiscoverLen += (OpLen + 4);

+    }

+    RequestOpt += (OpLen + 4);

+    RequestLen += (OpLen + 4);

+  }

+

+  Status = PxeBc->UdpWrite (

+                    PxeBc,

+                    OpFlags,

+                    &Private->ServerIp,

+                    &DestPort,

+                    NULL,

+                    &Private->StationIp,

+                    &SrcPort,

+                    NULL,

+                    NULL,

+                    &DiscoverLen,

+                    (VOID *) Discover

+                    );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Cache the right PXE reply packet here, set valid flag later.

+  // Especially for PXE discover packet, store it into mode data here.

+  //

+  if (Private->IsDoDiscover) {

+    CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);

+    Reply = &Private->PxeReply.Dhcp6.Packet.Ack;

+  } else {

+    Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;

+  }

+  ReadSize = (UINTN) Reply->Size;

+

+  Status = PxeBc->UdpRead (

+                    PxeBc,

+                    OpFlags,

+                    &Private->StationIp,

+                    &SrcPort,

+                    &Private->ServerIp,

+                    &DestPort,

+                    NULL,

+                    NULL,

+                    &ReadSize,

+                    (VOID *) &Reply->Dhcp6

+                    );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.

+

+  @param[in]  Private           The pointer to PxeBc private data.

+  @param[in]  Dhcp6             The pointer to the EFI_DHCP6_PROTOCOL

+

+  @retval EFI_SUCCESS           The S.A.R.R. process successfully finished.

+  @retval Others                Failed to finish the S.A.R.R. process.

+

+**/

+EFI_STATUS

+PxeBcDhcp6Sarr (

+  IN PXEBC_PRIVATE_DATA            *Private,

+  IN EFI_DHCP6_PROTOCOL            *Dhcp6

+  )

+{

+  EFI_PXE_BASE_CODE_MODE           *PxeMode;

+  EFI_DHCP6_CONFIG_DATA            Config;

+  EFI_DHCP6_MODE_DATA              Mode;

+  EFI_DHCP6_RETRANSMISSION         *Retransmit;

+  EFI_DHCP6_PACKET_OPTION          *OptList[PXEBC_DHCP6_OPTION_MAX_NUM];

+  UINT8                            Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];

+  UINT32                           OptCount;

+  EFI_STATUS                       Status;

+

+  Status     = EFI_SUCCESS;

+  PxeMode    = Private->PxeBc.Mode;

+

+  //

+  // Build option list for the request packet.

+  //

+  OptCount   = PxeBcBuildDhcp6Options (Private, OptList, Buffer);

+  ASSERT (OptCount> 0);

+

+  Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));

+  if (Retransmit == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));

+  ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));

+

+  Config.OptionCount           = OptCount;

+  Config.OptionList            = OptList;

+  Config.Dhcp6Callback         = PxeBcDhcp6CallBack;

+  Config.CallbackContext       = Private;

+  Config.IaInfoEvent           = NULL;

+  Config.RapidCommit           = FALSE;

+  Config.ReconfigureAccept     = FALSE;

+  Config.IaDescriptor.IaId     = 1;

+  Config.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;

+  Config.SolicitRetransmission = Retransmit;

+  Retransmit->Irt              = 4;

+  Retransmit->Mrc              = 4;

+  Retransmit->Mrt              = 32;

+  Retransmit->Mrd              = 60;

+

+  //

+  // Configure the DHCPv6 instance for PXE boot.

+  //

+  Status = Dhcp6->Configure (Dhcp6, &Config);

+  if (EFI_ERROR (Status)) {

+    FreePool (Retransmit);

+    return Status;

+  }

+

+  //

+  // Initialize the record fields for DHCPv6 offer in private data.

+  //

+  Private->IsProxyRecved = FALSE;

+  Private->OfferNum      = 0;

+  Private->SelectIndex   = 0;

+  ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));

+  ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));

+

+

+  //

+  // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.

+  //

+  Status = Dhcp6->Start (Dhcp6);

+  if (EFI_ERROR (Status)) {

+    if (Status == EFI_ICMP_ERROR) {

+      PxeMode->IcmpErrorReceived = TRUE;

+    }

+    Dhcp6->Configure (Dhcp6, NULL);

+    return Status;

+  }

+

+  //

+  // Get the acquired IPv6 address and store them.

+  //

+  Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);

+  if (EFI_ERROR (Status)) {

+    Dhcp6->Stop (Dhcp6);

+    return Status;

+  }

+

+  ASSERT (Mode.Ia->State == Dhcp6Bound);

+  CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));

+  CopyMem (&PxeMode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));

+

+  Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);

+  if (EFI_ERROR (Status)) {

+    Dhcp6->Stop (Dhcp6);

+    return Status;

+  }

+

+  Status = PxeBcFlushStaionIp (Private, &Private->StationIp, NULL);

+  if (EFI_ERROR (Status)) {

+    PxeBcUnregisterIp6Address (Private);

+    Dhcp6->Stop (Dhcp6);

+    return Status;

+  }

+

+  //

+  // Check the selected offer whether BINL retry is needed.

+  //

+  Status = PxeBcHandleDhcp6Offer (Private);

+  if (EFI_ERROR (Status)) {

+    PxeBcUnregisterIp6Address (Private);

+    Dhcp6->Stop (Dhcp6);

+    return Status;

+  }

+

+  AsciiPrint ("\n  Station IP address is ");

+

+  PxeBcShowIp6Addr (&Private->StationIp.v6);

+

+  return EFI_SUCCESS;

+}

+

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h
new file mode 100644
index 0000000..0eccacb
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h
@@ -0,0 +1,277 @@
+/** @file

+  Functions declaration related with DHCPv6 for UefiPxeBc Driver.

+

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

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_PXEBC_DHCP6_H__

+#define __EFI_PXEBC_DHCP6_H__

+

+#define PXEBC_DHCP6_OPTION_MAX_NUM        16

+#define PXEBC_DHCP6_OPTION_MAX_SIZE       312

+#define PXEBC_DHCP6_PACKET_MAX_SIZE       1472

+#define PXEBC_DHCP6_MAPPING_TIMEOUT       50000000   // 5 seconds, unit is 10nanosecond.

+#define PXEBC_IP6_POLICY_MAX              0xff

+

+#define PXEBC_DHCP6_S_PORT                547

+#define PXEBC_DHCP6_C_PORT                546

+

+#define PXEBC_DHCP6_OPT_CLIENT_ID         1

+#define PXEBC_DHCP6_OPT_SERVER_ID         2

+#define PXEBC_DHCP6_OPT_IA_NA             3

+#define PXEBC_DHCP6_OPT_IA_TA             4

+#define PXEBC_DHCP6_OPT_IAADDR            5

+#define PXEBC_DHCP6_OPT_ORO               6

+#define PXEBC_DHCP6_OPT_PREFERENCE        7

+#define PXEBC_DHCP6_OPT_ELAPSED_TIME      8

+#define PXEBC_DHCP6_OPT_REPLAY_MSG        9

+#define PXEBC_DHCP6_OPT_AUTH              11

+#define PXEBC_DHCP6_OPT_UNICAST           12

+#define PXEBC_DHCP6_OPT_STATUS_CODE       13

+#define PXEBC_DHCP6_OPT_RAPID_COMMIT      14

+#define PXEBC_DHCP6_OPT_USER_CLASS        15

+#define PXEBC_DHCP6_OPT_VENDOR_CLASS      16

+#define PXEBC_DHCP6_OPT_VENDOR_OPTS       17

+#define PXEBC_DHCP6_OPT_INTERFACE_ID      18

+#define PXEBC_DHCP6_OPT_RECONFIG_MSG      19

+#define PXEBC_DHCP6_OPT_RECONFIG_ACCEPT   20

+#define PXEBC_DHCP6_OPT_BOOT_FILE_URL     59    // Assigned by IANA, RFC 5970

+#define PXEBC_DHCP6_OPT_BOOT_FILE_PARAM   60    // Assigned by IANA, RFC 5970

+#define PXEBC_DHCP6_OPT_ARCH              61    // Assigned by IANA, RFC 5970

+#define PXEBC_DHCP6_OPT_UNDI              62    // Assigned by IANA, RFC 5970

+#define PXEBC_DHCP6_ENTERPRISE_NUM        343   // TODO: IANA TBD: temporarily using Intel's

+#define PXEBC_DHCP6_MAX_BOOT_FILE_SIZE    65535 //   It's a limitation of bit length, 65535*512 bytes.

+

+

+#define PXEBC_DHCP6_IDX_IA_NA             0

+#define PXEBC_DHCP6_IDX_BOOT_FILE_URL     1

+#define PXEBC_DHCP6_IDX_BOOT_FILE_PARAM   2

+#define PXEBC_DHCP6_IDX_VENDOR_CLASS      3

+#define PXEBC_DHCP6_IDX_MAX               4

+

+#define PXEBC_DHCP6_BOOT_FILE_URL_PREFIX  "tftp://"

+#define PXEBC_TFTP_URL_SEPARATOR          '/'

+#define PXEBC_ADDR_START_DELIMITER        '['

+#define PXEBC_ADDR_END_DELIMITER          ']'

+

+#define GET_NEXT_DHCP6_OPTION(Opt) \

+  (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \

+  sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1)

+

+#define GET_DHCP6_OPTION_SIZE(Pkt)  \

+  ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER))

+

+#define IS_PROXY_OFFER(Type) \

+  ((Type) == PxeOfferTypeProxyBinl || \

+   (Type) == PxeOfferTypeProxyPxe10 || \

+   (Type) == PxeOfferTypeProxyWfm11a)

+

+

+#pragma pack(1)

+typedef struct {

+  UINT16 OpCode[256];

+} PXEBC_DHCP6_OPTION_ORO;

+

+typedef struct {

+  UINT8 Type;

+  UINT8 MajorVer;

+  UINT8 MinorVer;

+  UINT8 Reserved;

+} PXEBC_DHCP6_OPTION_UNDI;

+

+typedef struct {

+  UINT16 Type;

+} PXEBC_DHCP6_OPTION_ARCH;

+

+typedef struct {

+  UINT8 ClassIdentifier[10];

+  UINT8 ArchitecturePrefix[5];

+  UINT8 ArchitectureType[5];

+  UINT8 Lit3[1];

+  UINT8 InterfaceName[4];

+  UINT8 Lit4[1];

+  UINT8 UndiMajor[3];

+  UINT8 UndiMinor[3];

+} PXEBC_CLASS_ID;

+

+typedef struct {

+  UINT32         Vendor;

+  UINT16         ClassLen;

+  PXEBC_CLASS_ID ClassId;

+} PXEBC_DHCP6_OPTION_VENDOR_CLASS;

+

+#pragma pack()

+

+typedef union {

+  PXEBC_DHCP6_OPTION_ORO            *Oro;

+  PXEBC_DHCP6_OPTION_UNDI           *Undi;

+  PXEBC_DHCP6_OPTION_ARCH           *Arch;

+  PXEBC_DHCP6_OPTION_VENDOR_CLASS   *VendorClass;

+} PXEBC_DHCP6_OPTION_ENTRY;

+

+typedef struct {

+  LIST_ENTRY              Link;

+  EFI_DHCP6_PACKET_OPTION *Option;

+  UINT8                   Precedence;

+} PXEBC_DHCP6_OPTION_NODE;

+

+typedef union {

+  EFI_DHCP6_PACKET        Offer;

+  EFI_DHCP6_PACKET        Ack;

+  UINT8                   Buffer[PXEBC_DHCP6_PACKET_MAX_SIZE];

+} PXEBC_DHCP6_PACKET;

+

+typedef struct {

+  PXEBC_DHCP6_PACKET      Packet;

+  PXEBC_OFFER_TYPE        OfferType;

+  EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_IDX_MAX];

+} PXEBC_DHCP6_PACKET_CACHE;

+

+

+/**

+  Free all the nodes in the boot file list.

+

+  @param[in]  Head            The pointer to the head of the list.

+

+**/

+VOID

+PxeBcFreeBootFileOption (

+  IN LIST_ENTRY               *Head

+  );

+

+

+/**

+  Parse the Boot File URL option.

+

+  @param[out]     FileName     The pointer to the boot file name.

+  @param[in, out] SrvAddr      The pointer to the boot server address.

+  @param[in]      BootFile     The pointer to the boot file URL option data.

+  @param[in]      Length       Length of the boot file URL option data.

+

+  @retval EFI_ABORTED     User canceled the operation.

+  @retval EFI_SUCCESS     Selected the boot menu successfully.

+  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.

+

+**/

+EFI_STATUS

+PxeBcExtractBootFileUrl (

+     OUT UINT8               **FileName,

+  IN OUT EFI_IPv6_ADDRESS    *SrvAddr,

+  IN     CHAR8               *BootFile,

+  IN     UINT16              Length

+  );

+

+

+/**

+  Parse the Boot File Parameter option.

+

+  @param[in]  BootFilePara      The pointer to the boot file parameter option data.

+  @param[out] BootFileSize      The pointer to the parsed boot file size.

+

+  @retval EFI_SUCCESS     Successfully obtained the boot file size from parameter option.

+  @retval EFI_NOT_FOUND   Failed to extract the boot file size from parameter option.

+

+**/

+EFI_STATUS

+PxeBcExtractBootFileParam (

+  IN  CHAR8                  *BootFilePara,

+  OUT UINT16                 *BootFileSize

+  );

+

+

+/**

+  Parse the cached DHCPv6 packet, including all the options.

+

+  @param[in]  Cache6           The pointer to a cached DHCPv6 packet.

+

+  @retval     EFI_SUCCESS      Parsed the DHCPv6 packet successfully.

+  @retval     EFI_DEVICE_ERROR Failed to parse and invalid packet.

+

+**/

+EFI_STATUS

+PxeBcParseDhcp6Packet (

+  IN PXEBC_DHCP6_PACKET_CACHE  *Cache6

+  );

+

+

+/**

+  Register the ready address by Ip6Config protocol.

+

+  @param[in]  Private             The pointer to the PxeBc private data.

+  @param[in]  Address             The pointer to the ready address.

+

+  @retval     EFI_SUCCESS         Registered the address succesfully.

+  @retval     Others              Failed to register the address.

+

+**/

+EFI_STATUS

+PxeBcRegisterIp6Address (

+  IN PXEBC_PRIVATE_DATA            *Private,

+  IN EFI_IPv6_ADDRESS              *Address

+  );

+

+

+/**

+  Unregister the address by Ip6Config protocol.

+

+  @param[in]  Private             The pointer to the PxeBc private data.

+

+**/

+VOID

+PxeBcUnregisterIp6Address (

+  IN PXEBC_PRIVATE_DATA            *Private

+  );

+

+

+/**

+  Build and send out the request packet for the bootfile, and parse the reply.

+

+  @param[in]  Private               The pointer to the PxeBc private data.

+  @param[in]  Type                  PxeBc option boot item type.

+  @param[in]  Layer                 The pointer to the option boot item layer.

+  @param[in]  UseBis                Use BIS or not.

+  @param[in]  DestIp                The pointer to the server address.

+

+  @retval     EFI_SUCCESS           Successfully discovered theboot file.

+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.

+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.

+  @retval     Others                Failed to discover boot file.

+

+**/

+EFI_STATUS

+PxeBcDhcp6Discover (

+  IN  PXEBC_PRIVATE_DATA            *Private,

+  IN  UINT16                        Type,

+  IN  UINT16                        *Layer,

+  IN  BOOLEAN                       UseBis,

+  IN  EFI_IP_ADDRESS                *DestIp

+  );

+

+

+/**

+  Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.

+

+  @param[in]  Private           The pointer to the PxeBc private data.

+  @param[in]  Dhcp6             The pointer to EFI_DHCP6_PROTOCOL.

+

+  @retval EFI_SUCCESS           The S.A.R.R. process successfully finished.

+  @retval Others                Failed to finish the S.A.R.R. process.

+

+**/

+EFI_STATUS

+PxeBcDhcp6Sarr (

+  IN PXEBC_PRIVATE_DATA            *Private,

+  IN EFI_DHCP6_PROTOCOL            *Dhcp6

+  );

+

+#endif

+

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c
new file mode 100644
index 0000000..f785255
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c
@@ -0,0 +1,1339 @@
+/** @file

+  Driver Binding functions implementationfor for UefiPxeBc Driver.

+

+  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "PxeBcImpl.h"

+

+

+EFI_DRIVER_BINDING_PROTOCOL gPxeBcDriverBinding = {

+  PxeBcDriverBindingSupported,

+  PxeBcDriverBindingStart,

+  PxeBcDriverBindingStop,

+  0xa,

+  NULL,

+  NULL

+};

+

+

+/**

+  Get the Nic handle using any child handle in the IPv4 stack.

+

+  @param[in]  ControllerHandle    Pointer to child handle over IPv4.

+

+  @return NicHandle               The pointer to the Nic handle.

+

+**/

+EFI_HANDLE

+PxeBcGetNicByIp4Children (

+  IN EFI_HANDLE                 ControllerHandle

+  )

+{

+  EFI_HANDLE                    NicHandle;

+

+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);

+  if (NicHandle == NULL) {

+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);

+    if (NicHandle == NULL) {

+      NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);

+      if (NicHandle == NULL) {

+        NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);

+        if (NicHandle == NULL) {

+          NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid);

+          if (NicHandle == NULL) {

+            return NULL;

+          }

+        }

+      }

+    }

+  }

+

+  return NicHandle;

+}

+

+

+/**

+  Get the Nic handle using any child handle in the IPv6 stack.

+

+  @param[in]  ControllerHandle    Pointer to child handle over IPv6.

+

+  @return NicHandle               The pointer to the Nic handle.

+

+**/

+EFI_HANDLE

+PxeBcGetNicByIp6Children (

+  IN EFI_HANDLE                  ControllerHandle

+  )

+{

+  EFI_HANDLE                     NicHandle;

+

+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid);

+  if (NicHandle == NULL) {

+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);

+    if (NicHandle == NULL) {

+      NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);

+      if (NicHandle == NULL) {

+        NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp6ProtocolGuid);

+        if (NicHandle == NULL) {

+          return NULL;

+        }

+      }

+    }

+  }

+

+  return NicHandle;

+}

+

+

+/**

+  Destroy the opened instances based on IPv4.

+

+  @param[in]  This              Pointer to the EFI_DRIVER_BINDING_PROTOCOL.

+  @param[in]  Private           Pointer to PXEBC_PRIVATE_DATA.

+

+**/

+VOID

+PxeBcDestroyIp4Children (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN PXEBC_PRIVATE_DATA           *Private

+  )

+{

+  ASSERT(Private != NULL);

+

+  if (Private->ArpChild != NULL) {

+    //

+    // Close Arp for PxeBc->Arp and destroy the instance.

+    //

+    gBS->CloseProtocol (

+           Private->ArpChild,

+           &gEfiArpProtocolGuid,

+           This->DriverBindingHandle,

+           Private->Controller

+           );

+

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiArpServiceBindingProtocolGuid,

+      Private->ArpChild

+      );

+  }

+

+  if (Private->Ip4Child != NULL) {

+    //

+    // Close Ip4 for background ICMP error message and destroy the instance.

+    //

+    gBS->CloseProtocol (

+           Private->Ip4Child,

+           &gEfiIp4ProtocolGuid,

+           This->DriverBindingHandle,

+           Private->Controller

+           );

+

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiIp4ServiceBindingProtocolGuid,

+      Private->Ip4Child

+      );

+  }

+

+  if (Private->Udp4WriteChild != NULL) {

+    //

+    // Close Udp4 for PxeBc->UdpWrite and destroy the instance.

+    //

+    gBS->CloseProtocol (

+           Private->Udp4WriteChild,

+           &gEfiUdp4ProtocolGuid,

+           This->DriverBindingHandle,

+           Private->Controller

+           );

+

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiUdp4ServiceBindingProtocolGuid,

+      Private->Udp4WriteChild

+      );

+  }

+

+  if (Private->Udp4ReadChild != NULL) {

+    //

+    // Close Udp4 for PxeBc->UdpRead and destroy the instance.

+    //

+    gBS->CloseProtocol (

+          Private->Udp4ReadChild,

+          &gEfiUdp4ProtocolGuid,

+          This->DriverBindingHandle,

+          Private->Controller

+          );

+

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiUdp4ServiceBindingProtocolGuid,

+      Private->Udp4ReadChild

+      );

+  }

+

+  if (Private->Mtftp4Child != NULL) {

+    //

+    // Close Mtftp4 for PxeBc->Mtftp4 and destroy the instance.

+    //

+    gBS->CloseProtocol (

+          Private->Mtftp4Child,

+          &gEfiMtftp4ProtocolGuid,

+          This->DriverBindingHandle,

+          Private->Controller

+          );

+

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiMtftp4ServiceBindingProtocolGuid,

+      Private->Mtftp4Child

+      );

+  }

+

+  if (Private->Dhcp4Child != NULL) {

+    //

+    // Close Dhcp4 for PxeBc->Dhcp4 and destroy the instance.

+    //

+    gBS->CloseProtocol (

+          Private->Dhcp4Child,

+          &gEfiDhcp4ProtocolGuid,

+          This->DriverBindingHandle,

+          Private->Controller

+          );

+

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiDhcp4ServiceBindingProtocolGuid,

+      Private->Dhcp4Child

+      );

+  }

+

+  if (Private->Ip4Nic != NULL) {

+    //

+    // Close PxeBc from the parent Nic handle and destroy the virtual handle.

+    //

+    gBS->CloseProtocol (

+           Private->Controller,

+           &gEfiPxeBaseCodeProtocolGuid,

+           This->DriverBindingHandle,

+           Private->Ip4Nic->Controller

+           );

+

+    gBS->UninstallMultipleProtocolInterfaces (

+           Private->Ip4Nic->Controller,

+           &gEfiDevicePathProtocolGuid,

+           Private->Ip4Nic->DevicePath,

+           &gEfiLoadFileProtocolGuid,

+           &Private->Ip4Nic->LoadFile,

+           NULL

+           );

+    FreePool (Private->Ip4Nic);

+  }

+

+  Private->ArpChild         = NULL;

+  Private->Ip4Child         = NULL;

+  Private->Udp4WriteChild   = NULL;

+  Private->Udp4ReadChild    = NULL;

+  Private->Mtftp4Child      = NULL;

+  Private->Dhcp4Child       = NULL;

+  Private->Ip4Nic           = NULL;

+}

+

+

+/**

+  Destroy the opened instances based on IPv6.

+

+  @param[in]  This              Pointer to the EFI_DRIVER_BINDING_PROTOCOL.

+  @param[in]  Private           Pointer to PXEBC_PRIVATE_DATA.

+

+**/

+VOID

+PxeBcDestroyIp6Children (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN PXEBC_PRIVATE_DATA           *Private

+  )

+{

+  ASSERT(Private != NULL);

+

+  if (Private->Ip6Child != NULL) {

+    //

+    // Close Ip6 for Ip6->Ip6Config and destroy the instance.

+    //

+    gBS->CloseProtocol (

+          Private->Ip6Child,

+          &gEfiIp6ProtocolGuid,

+          This->DriverBindingHandle,

+          Private->Controller

+          );

+

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiIp6ServiceBindingProtocolGuid,

+      Private->Ip6Child

+      );

+  }

+

+  if (Private->Udp6WriteChild != NULL) {

+    //

+    // Close Udp6 for PxeBc->UdpWrite and destroy the instance.

+    //

+    gBS->CloseProtocol (

+           Private->Udp6WriteChild,

+           &gEfiUdp6ProtocolGuid,

+           This->DriverBindingHandle,

+           Private->Controller

+           );

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiUdp6ServiceBindingProtocolGuid,

+      Private->Udp6WriteChild

+      );

+  }

+

+  if (Private->Udp6ReadChild != NULL) {

+    //

+    // Close Udp6 for PxeBc->UdpRead and destroy the instance.

+    //

+    gBS->CloseProtocol (

+          Private->Udp6ReadChild,

+          &gEfiUdp6ProtocolGuid,

+          This->DriverBindingHandle,

+          Private->Controller

+          );

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiUdp6ServiceBindingProtocolGuid,

+      Private->Udp6ReadChild

+      );

+  }

+

+  if (Private->Mtftp6Child != NULL) {

+    //

+    // Close Mtftp6 for PxeBc->Mtftp and destroy the instance.

+    //

+    gBS->CloseProtocol (

+          Private->Mtftp6Child,

+          &gEfiMtftp6ProtocolGuid,

+          This->DriverBindingHandle,

+          Private->Controller

+          );

+

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiMtftp6ServiceBindingProtocolGuid,

+      Private->Mtftp6Child

+      );

+  }

+

+  if (Private->Dhcp6Child != NULL) {

+    //

+    // Close Dhcp6 for PxeBc->Dhcp and destroy the instance.

+    //

+    gBS->CloseProtocol (

+          Private->Dhcp6Child,

+          &gEfiDhcp6ProtocolGuid,

+          This->DriverBindingHandle,

+          Private->Controller

+          );

+

+    NetLibDestroyServiceChild (

+      Private->Controller,

+      This->DriverBindingHandle,

+      &gEfiDhcp6ServiceBindingProtocolGuid,

+      Private->Dhcp6Child

+      );

+  }

+

+  if (Private->Ip6Nic != NULL) {

+    //

+    // Close PxeBc from the parent Nic handle and destroy the virtual handle.

+    //

+    gBS->CloseProtocol (

+           Private->Controller,

+           &gEfiPxeBaseCodeProtocolGuid,

+           This->DriverBindingHandle,

+           Private->Ip6Nic->Controller

+           );

+    gBS->UninstallMultipleProtocolInterfaces (

+           Private->Ip6Nic->Controller,

+           &gEfiDevicePathProtocolGuid,

+           Private->Ip6Nic->DevicePath,

+           &gEfiLoadFileProtocolGuid,

+           &Private->Ip6Nic->LoadFile,

+           NULL

+           );

+    FreePool (Private->Ip6Nic);

+  }

+

+  Private->Ip6Child           = NULL;

+  Private->Udp6WriteChild     = NULL;

+  Private->Udp6ReadChild      = NULL;

+  Private->Mtftp6Child        = NULL;

+  Private->Dhcp6Child         = NULL;

+  Private->Ip6Nic             = NULL;

+  Private->Mode.Ipv6Available = FALSE;

+}

+

+

+/**

+  Create the opened instances based on IPv4.

+

+  @param[in]  This              Pointer to EFI_DRIVER_BINDING_PROTOCOL.

+  @param[in]  ControllerHandle  Handle of the child to destroy.

+  @param[in]  Private Handle    Pointer to PXEBC_PRIVATE_DATA.

+

+  @retval EFI_SUCCESS           The instances based on IPv4 were all created successfully.

+  @retval Others                An unexpected error occurred.

+

+**/

+EFI_STATUS

+PxeBcCreateIp4Children (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN PXEBC_PRIVATE_DATA           *Private

+  )

+{

+  EFI_STATUS                      Status;

+  IPv4_DEVICE_PATH                Ip4Node;

+  EFI_PXE_BASE_CODE_PROTOCOL      *PxeBc;

+  EFI_PXE_BASE_CODE_MODE          *Mode;

+  EFI_UDP4_CONFIG_DATA            *Udp4CfgData;

+  EFI_IP4_CONFIG_DATA             *Ip4CfgData;

+  EFI_IP4_MODE_DATA               Ip4ModeData;

+

+  if (Private->Ip4Nic != NULL) {

+    //

+    // Already created before.

+    //

+    return EFI_SUCCESS;

+  }

+

+  //

+  // Create Dhcp4 child and open Dhcp4 protocol for PxeBc->Dhcp.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiDhcp4ServiceBindingProtocolGuid,

+             &Private->Dhcp4Child

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Dhcp4Child,

+                  &gEfiDhcp4ProtocolGuid,

+                  (VOID **) &Private->Dhcp4,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create Mtftp4 child and open Mtftp4 protocol for PxeBc->Mtftp.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiMtftp4ServiceBindingProtocolGuid,

+             &Private->Mtftp4Child

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Mtftp4Child,

+                  &gEfiMtftp4ProtocolGuid,

+                  (VOID **) &Private->Mtftp4,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create Udp4 child and open Udp4 protocol for PxeBc->UdpRead.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiUdp4ServiceBindingProtocolGuid,

+             &Private->Udp4ReadChild

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Udp4ReadChild,

+                  &gEfiUdp4ProtocolGuid,

+                  (VOID **) &Private->Udp4Read,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create Udp4 child and open Udp4 protocol for PxeBc->UdpWrite.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiUdp4ServiceBindingProtocolGuid,

+             &Private->Udp4WriteChild

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Udp4WriteChild,

+                  &gEfiUdp4ProtocolGuid,

+                  (VOID **) &Private->Udp4Write,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create Arp child and open Arp protocol for PxeBc->Arp.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiArpServiceBindingProtocolGuid,

+             &Private->ArpChild

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->ArpChild,

+                  &gEfiArpProtocolGuid,

+                  (VOID **) &Private->Arp,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create Ip4 child and open Ip4 protocol for background ICMP packets.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiIp4ServiceBindingProtocolGuid,

+             &Private->Ip4Child

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Ip4Child,

+                  &gEfiIp4ProtocolGuid,

+                  (VOID **) &Private->Ip4,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Get max packet size from Ip4 to calculate block size for Tftp later.

+  //

+  Status = Private->Ip4->GetModeData (Private->Ip4, &Ip4ModeData, NULL, NULL);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Private->Ip4MaxPacketSize = Ip4ModeData.MaxPacketSize;

+

+  Private->Ip4Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));

+  if (Private->Ip4Nic == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Private->Ip4Nic->Private   = Private;

+  Private->Ip4Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;

+

+  //

+  // Create a device path node for Ipv4 virtual nic, and append it.

+  //

+  ZeroMem (&Ip4Node, sizeof (IPv4_DEVICE_PATH));

+  Ip4Node.Header.Type     = MESSAGING_DEVICE_PATH;

+  Ip4Node.Header.SubType  = MSG_IPv4_DP;

+  Ip4Node.StaticIpAddress = FALSE;

+

+  SetDevicePathNodeLength (&Ip4Node.Header, sizeof (Ip4Node));

+

+  Private->Ip4Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip4Node.Header);

+

+  if (Private->Ip4Nic->DevicePath == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_ERROR;

+  }

+

+  CopyMem (

+    &Private->Ip4Nic->LoadFile,

+    &gLoadFileProtocolTemplate,

+    sizeof (EFI_LOAD_FILE_PROTOCOL)

+    );

+

+  //

+  // Create a new handle for IPv4 virtual nic,

+  // and install PxeBaseCode, LoadFile and DevicePath protocols.

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &Private->Ip4Nic->Controller,

+                  &gEfiDevicePathProtocolGuid,

+                  Private->Ip4Nic->DevicePath,

+                  &gEfiLoadFileProtocolGuid,

+                  &Private->Ip4Nic->LoadFile,

+                  NULL

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Open PxeBaseCode protocol by child to setup a parent-child relationship between

+  // real NIC handle and the virtual IPv4 NIC handle.

+  //

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiPxeBaseCodeProtocolGuid,

+                  (VOID **) &PxeBc,

+                  This->DriverBindingHandle,

+                  Private->Ip4Nic->Controller,

+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Set default configure data for Udp4Read and Ip4 instance.

+  //

+  Mode                            = PxeBc->Mode;

+  Udp4CfgData                     = &Private->Udp4CfgData;

+  Ip4CfgData                      = &Private->Ip4CfgData;

+

+  Udp4CfgData->AcceptBroadcast    = TRUE;

+  Udp4CfgData->AcceptAnyPort      = TRUE;

+  Udp4CfgData->AllowDuplicatePort = TRUE;

+  Udp4CfgData->TypeOfService      = Mode->ToS;

+  Udp4CfgData->TimeToLive         = Mode->TTL;

+  Udp4CfgData->ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;

+  Udp4CfgData->TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;

+

+  Ip4CfgData->AcceptIcmpErrors    = TRUE;

+  Ip4CfgData->DefaultProtocol     = EFI_IP_PROTO_ICMP;

+  Ip4CfgData->TypeOfService       = Mode->ToS;

+  Ip4CfgData->TimeToLive          = Mode->TTL;

+  Ip4CfgData->ReceiveTimeout      = PXEBC_DEFAULT_LIFETIME;

+  Ip4CfgData->TransmitTimeout     = PXEBC_DEFAULT_LIFETIME;

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+  PxeBcDestroyIp4Children (This, Private);

+  return Status;

+}

+

+

+/**

+  Create the opened instances based on IPv6.

+

+  @param[in]  This              Pointer to EFI_DRIVER_BINDING_PROTOCOL.

+  @param[in]  ControllerHandle  Handle of the child to destroy.

+  @param[in]  Private Handle    Pointer to PXEBC_PRIVATE_DATA.

+

+  @retval EFI_SUCCESS           The instances based on IPv6 were all created successfully.

+  @retval Others                An unexpected error occurred.

+

+**/

+EFI_STATUS

+PxeBcCreateIp6Children (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN PXEBC_PRIVATE_DATA           *Private

+  )

+{

+  EFI_STATUS                      Status;

+  IPv6_DEVICE_PATH                Ip6Node;

+  EFI_PXE_BASE_CODE_PROTOCOL      *PxeBc;

+  EFI_UDP6_CONFIG_DATA            *Udp6CfgData;

+  EFI_IP6_CONFIG_DATA             *Ip6CfgData;

+  EFI_IP6_MODE_DATA               Ip6ModeData;

+

+  if (Private->Ip6Nic != NULL) {

+    //

+    // Already created before.

+    //

+    return EFI_SUCCESS;

+  }

+

+  Private->Ip6Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));

+

+  if (Private->Ip6Nic == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Private->Ip6Nic->Private   = Private;

+  Private->Ip6Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;

+

+  //

+  // Create Dhcp6 child and open Dhcp6 protocol for PxeBc->Dhcp.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiDhcp6ServiceBindingProtocolGuid,

+             &Private->Dhcp6Child

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Dhcp6Child,

+                  &gEfiDhcp6ProtocolGuid,

+                  (VOID **) &Private->Dhcp6,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create Mtftp6 child and open Mtftp6 protocol for PxeBc->Mtftp.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiMtftp6ServiceBindingProtocolGuid,

+             &Private->Mtftp6Child

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Mtftp6Child,

+                  &gEfiMtftp6ProtocolGuid,

+                  (VOID **) &Private->Mtftp6,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create Udp6 child and open Udp6 protocol for PxeBc->UdpRead.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiUdp6ServiceBindingProtocolGuid,

+             &Private->Udp6ReadChild

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Udp6ReadChild,

+                  &gEfiUdp6ProtocolGuid,

+                  (VOID **) &Private->Udp6Read,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create Udp6 child and open Udp6 protocol for PxeBc->UdpWrite.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiUdp6ServiceBindingProtocolGuid,

+             &Private->Udp6WriteChild

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Udp6WriteChild,

+                  &gEfiUdp6ProtocolGuid,

+                  (VOID **) &Private->Udp6Write,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create Ip6 child and open Ip6 protocol for background ICMP6 packets.

+  //

+  Status = NetLibCreateServiceChild (

+             ControllerHandle,

+             This->DriverBindingHandle,

+             &gEfiIp6ServiceBindingProtocolGuid,

+             &Private->Ip6Child

+             );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Private->Ip6Child,

+                  &gEfiIp6ProtocolGuid,

+                  (VOID **) &Private->Ip6,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Get max packet size from Ip6 to calculate block size for Tftp later.

+  //

+  Status = Private->Ip6->GetModeData (Private->Ip6, &Ip6ModeData, NULL, NULL);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Private->Ip6MaxPacketSize = Ip6ModeData.MaxPacketSize;

+

+  //

+  // Locate Ip6->Ip6Config and store it for set IPv6 address.

+  //

+  Status = gBS->HandleProtocol (

+                  ControllerHandle,

+                  &gEfiIp6ConfigProtocolGuid,

+                  (VOID **) &Private->Ip6Cfg

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Create a device path node for Ipv6 virtual nic, and append it.

+  //

+  ZeroMem (&Ip6Node, sizeof (IPv6_DEVICE_PATH));

+  Ip6Node.Header.Type     = MESSAGING_DEVICE_PATH;

+  Ip6Node.Header.SubType  = MSG_IPv6_DP;

+  Ip6Node.StaticIpAddress = FALSE;

+

+  SetDevicePathNodeLength (&Ip6Node.Header, sizeof (Ip6Node));

+

+  Private->Ip6Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip6Node.Header);

+

+  if (Private->Ip6Nic->DevicePath == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_ERROR;

+  }

+

+  CopyMem (

+    &Private->Ip6Nic->LoadFile,

+    &gLoadFileProtocolTemplate,

+    sizeof (EFI_LOAD_FILE_PROTOCOL)

+    );

+

+  //

+  // Create a new handle for IPv6 virtual nic,

+  // and install PxeBaseCode, LoadFile and DevicePath protocols.

+  //

+  Status = gBS->InstallMultipleProtocolInterfaces (

+                  &Private->Ip6Nic->Controller,

+                  &gEfiDevicePathProtocolGuid,

+                  Private->Ip6Nic->DevicePath,

+                  &gEfiLoadFileProtocolGuid,

+                  &Private->Ip6Nic->LoadFile,

+                  NULL

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Open PxeBaseCode protocol by child to setup a parent-child relationship between

+  // real NIC handle and the virtual IPv6 NIC handle.

+  //

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiPxeBaseCodeProtocolGuid,

+                  (VOID **) &PxeBc,

+                  This->DriverBindingHandle,

+                  Private->Ip6Nic->Controller,

+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Set IPv6 avaiable flag and set default configure data for

+  // Udp6Read and Ip6 instance.

+  //

+  Private->Mode.Ipv6Available     = TRUE;

+  Udp6CfgData                     = &Private->Udp6CfgData;

+  Ip6CfgData                      = &Private->Ip6CfgData;

+

+  Udp6CfgData->AcceptAnyPort      = TRUE;

+  Udp6CfgData->AllowDuplicatePort = TRUE;

+  Udp6CfgData->HopLimit           = PXEBC_DEFAULT_HOPLIMIT;

+  Udp6CfgData->ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;

+  Udp6CfgData->TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;

+

+  Ip6CfgData->AcceptIcmpErrors    = TRUE;

+  Ip6CfgData->DefaultProtocol     = IP6_ICMP;

+  Ip6CfgData->HopLimit            = PXEBC_DEFAULT_HOPLIMIT;

+  Ip6CfgData->ReceiveTimeout      = PXEBC_DEFAULT_LIFETIME;

+  Ip6CfgData->TransmitTimeout     = PXEBC_DEFAULT_LIFETIME;

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+  PxeBcDestroyIp6Children (This, Private);

+  return Status;

+}

+

+

+/**

+  The entry point for UefiPxeBc driver that installs the driver

+  binding and component name protocol on its image.

+

+  @param[in]  ImageHandle          The Image handle of the driver.

+  @param[in]  SystemTable          The system table.

+

+  @return EFI_SUCCESS

+  @return Others

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcDriverEntryPoint (

+  IN EFI_HANDLE             ImageHandle,

+  IN EFI_SYSTEM_TABLE       *SystemTable

+  )

+{

+  return EfiLibInstallDriverBindingComponentName2 (

+           ImageHandle,

+           SystemTable,

+           &gPxeBcDriverBinding,

+           ImageHandle,

+           &gPxeBcComponentName,

+           &gPxeBcComponentName2

+           );

+}

+

+

+/**

+  Test to see if this driver supports ControllerHandle. This service

+  is called by the EFI boot service ConnectController(). In

+  order to make drivers as small as possible, there are a few calling

+  restrictions for this service. ConnectController() must

+  follow these calling restrictions. If any other agent wishes to call

+  Supported() it must also follow these calling restrictions.

+

+  @param[in]  This                The pointer to the driver binding protocol.

+  @param[in]  ControllerHandle    The handle of device to be tested.

+  @param[in]  RemainingDevicePath Optional parameter used to pick a specific child

+                                  device to be started.

+

+  @retval EFI_SUCCESS         This driver supports this device.

+  @retval EFI_UNSUPPORTED     This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcDriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  )

+{

+  EFI_STATUS                      Ip4Status;

+  EFI_STATUS                      Ip6Status;

+

+  //

+  // Try to open the Mtftp4 and Dhcp4 protocol to test whether IPv4 stack is ready.

+  //

+  Ip4Status = gBS->OpenProtocol (

+                     ControllerHandle,

+                     &gEfiDhcp4ServiceBindingProtocolGuid,

+                     NULL,

+                     This->DriverBindingHandle,

+                     ControllerHandle,

+                     EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                     );

+  if (!EFI_ERROR (Ip4Status)) {

+    Ip4Status = gBS->OpenProtocol (

+                       ControllerHandle,

+                       &gEfiMtftp4ServiceBindingProtocolGuid,

+                       NULL,

+                       This->DriverBindingHandle,

+                       ControllerHandle,

+                       EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                       );

+  }

+

+  //

+  // Try to open the Mtftp6 and Dhcp6 protocol to test whether IPv4 stack is ready.

+  //

+  Ip6Status = gBS->OpenProtocol (

+                     ControllerHandle,

+                     &gEfiDhcp6ServiceBindingProtocolGuid,

+                     NULL,

+                     This->DriverBindingHandle,

+                     ControllerHandle,

+                     EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                     );

+  if (!EFI_ERROR (Ip6Status)) {

+    Ip6Status = gBS->OpenProtocol (

+                       ControllerHandle,

+                       &gEfiMtftp6ServiceBindingProtocolGuid,

+                       NULL,

+                       This->DriverBindingHandle,

+                       ControllerHandle,

+                       EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                       );

+  }

+

+  //

+  // It's unsupported case if both stack are not ready.

+  //

+  if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) {

+    return EFI_UNSUPPORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Start this driver on ControllerHandle. This service is called by the

+  EFI boot service ConnectController(). In order to make

+  drivers as small as possible, there are a few calling restrictions for

+  this service. ConnectController() must follow these

+  calling restrictions. If any other agent wishes to call Start() it

+  must also follow these calling restrictions.

+

+  @param[in]  This                 The pointer to the driver binding protocol.

+  @param[in]  ControllerHandle     The handle of device to be started.

+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child

+                                   device to be started.

+

+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.

+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.

+  @retval other                This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcDriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  )

+{

+  PXEBC_PRIVATE_DATA              *Private;

+  EFI_PXE_BASE_CODE_PROTOCOL      *PxeBc;

+  EFI_STATUS                      Status;

+  EFI_STATUS                      Ip4Status;

+  EFI_STATUS                      Ip6Status;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiPxeBaseCodeProtocolGuid,

+                  (VOID **) &PxeBc,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (!EFI_ERROR (Status)) {

+    //

+    // Skip the initialization if the driver has been started already.

+    //

+    Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc);

+  } else {

+    //

+    // If the driver has not been started yet, it should do initialization.

+    //

+    Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA));

+    if (Private == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    CopyMem (

+      &Private->PxeBc,

+      &gPxeBcProtocolTemplate,

+      sizeof (EFI_PXE_BASE_CODE_PROTOCOL)

+      );

+

+    Private->Signature          = PXEBC_PRIVATE_DATA_SIGNATURE;

+    Private->Controller         = ControllerHandle;

+    Private->Image              = This->ImageHandle;

+    Private->PxeBc.Mode         = &Private->Mode;

+    Private->Mode.Ipv6Supported = TRUE;

+    Private->Mode.AutoArp       = TRUE;

+    Private->Mode.TTL           = DEFAULT_TTL;

+    Private->Mode.ToS           = DEFAULT_ToS;

+

+    //

+    // Open device path to prepare for appending virtual NIC node.

+    //

+    Status = gBS->OpenProtocol (

+                    ControllerHandle,

+                    &gEfiDevicePathProtocolGuid,

+                    (VOID **) &Private->DevicePath,

+                    This->DriverBindingHandle,

+                    ControllerHandle,

+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                    );

+

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+

+    //

+    // Get the NII interface if it exists, it's not required.

+    //

+    Status = gBS->OpenProtocol (

+                    ControllerHandle,

+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,

+                    (VOID **) &Private->Nii,

+                    This->DriverBindingHandle,

+                    ControllerHandle,

+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                    );

+    if (EFI_ERROR (Status)) {

+      Private->Nii = NULL;

+    }

+

+    //

+    // Install PxeBaseCode protocol onto the real NIC handler.

+    //

+    Status = gBS->InstallProtocolInterface (

+                    &ControllerHandle,

+                    &gEfiPxeBaseCodeProtocolGuid,

+                    EFI_NATIVE_INTERFACE,

+                    &Private->PxeBc

+                    );

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+  }

+

+  //

+  // Try to create virtual NIC handle for IPv4.

+  //

+  Ip4Status = PxeBcCreateIp4Children (This, ControllerHandle, Private);

+

+  //

+  // Try to create virtual NIC handle for IPv6.

+  //

+  Ip6Status = PxeBcCreateIp6Children (This, ControllerHandle, Private);

+

+  if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) {

+    //

+    // Failed to start PXE driver if IPv4 and IPv6 stack are both not available.

+    //

+    Status = EFI_DEVICE_ERROR;

+    goto ON_ERROR;

+  }

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+  gBS->UninstallProtocolInterface (

+         ControllerHandle,

+         &gEfiPxeBaseCodeProtocolGuid,

+         &Private->PxeBc

+         );

+  PxeBcDestroyIp4Children (This, Private);

+  PxeBcDestroyIp6Children (This, Private);

+  FreePool (Private);

+

+  return Status;

+}

+

+

+/**

+  Stop this driver on ControllerHandle. This service is called by the

+  EFI boot service DisconnectController(). In order to

+  make drivers as small as possible, there are a few calling

+  restrictions for this service. DisconnectController()

+  must follow these calling restrictions. If any other agent wishes

+  to call Stop() it must also follow these calling restrictions.

+

+  @param[in]  This              Protocol instance pointer.

+  @param[in]  ControllerHandle  Handle of device to stop driver on.

+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of

+                                children is zero stop the entire bus driver.

+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS           This driver was removed ControllerHandle.

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+  @retval Others                This driver was not removed from this device

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcDriverBindingStop (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN UINTN                        NumberOfChildren,

+  IN EFI_HANDLE                   *ChildHandleBuffer

+  )

+{

+  PXEBC_PRIVATE_DATA              *Private;

+  PXEBC_VIRTUAL_NIC               *VirtualNic;

+  EFI_PXE_BASE_CODE_PROTOCOL      *PxeBc;

+  EFI_LOAD_FILE_PROTOCOL          *LoadFile;

+  EFI_STATUS                      Status;

+  EFI_HANDLE                      NicHandle;

+  BOOLEAN                         IsIpv6;

+

+  Private    = NULL;

+  NicHandle  = NULL;

+  VirtualNic = NULL;

+  LoadFile   = NULL;

+  PxeBc      = NULL;

+  IsIpv6     = FALSE;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiLoadFileProtocolGuid,

+                  (VOID **) &LoadFile,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    //

+    // Get the Nic handle by any pass-over service child handle.

+    //

+    NicHandle = PxeBcGetNicByIp4Children (ControllerHandle);

+    if (NicHandle == NULL) {

+      NicHandle = PxeBcGetNicByIp6Children (ControllerHandle);

+      if (NicHandle == NULL) {

+        return EFI_DEVICE_ERROR;

+      } else {

+        IsIpv6 = TRUE;

+      }

+    }

+

+    //

+    // Try to retrieve the private data by PxeBc protocol.

+    //

+    Status = gBS->OpenProtocol (

+                    NicHandle,

+                    &gEfiPxeBaseCodeProtocolGuid,

+                    (VOID **) &PxeBc,

+                    This->DriverBindingHandle,

+                    ControllerHandle,

+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                    );

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+    Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc);

+

+  } else {

+    //

+    // It's a virtual handle with LoadFileProtocol.

+    //

+    Status = gBS->OpenProtocol (

+                    ControllerHandle,

+                    &gEfiLoadFileProtocolGuid,

+                    (VOID **) &LoadFile,

+                    This->DriverBindingHandle,

+                    ControllerHandle,

+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                    );

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (LoadFile);

+    Private    = VirtualNic->Private;

+    NicHandle  = Private->Controller;

+

+    if (Private->Ip6Nic == VirtualNic) {

+      IsIpv6   = TRUE;

+    }

+  }

+

+  if (Private->Ip4Nic != NULL && !IsIpv6) {

+    PxeBcDestroyIp4Children (This, Private);

+  }

+

+  if (Private->Ip6Nic != NULL && IsIpv6) {

+    PxeBcDestroyIp6Children (This, Private);

+  }

+

+  if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) {

+    gBS->UninstallProtocolInterface (

+           NicHandle,

+           &gEfiPxeBaseCodeProtocolGuid,

+           &Private->PxeBc

+           );

+    FreePool (Private);

+  }

+

+  return EFI_SUCCESS;

+}

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h
new file mode 100644
index 0000000..8df4bae
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h
@@ -0,0 +1,104 @@
+/** @file

+  Driver Binding functions declaration for UefiPxeBc Driver.

+

+  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_PXEBC_DRIVER_H__

+#define __EFI_PXEBC_DRIVER_H__

+

+extern EFI_COMPONENT_NAME_PROTOCOL  gPxeBcComponentName;

+extern EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2;

+

+/**

+  Test to see if this driver supports ControllerHandle. This service

+  is called by the EFI boot service ConnectController(). In

+  order to make drivers as small as possible, there are a few calling

+  restrictions for this service. ConnectController() must

+  follow these calling restrictions. If any other agent wishes to call

+  Supported() it must also follow these calling restrictions.

+

+  @param[in]  This                The pointer to the driver binding protocol.

+  @param[in]  ControllerHandle    The handle of device to be tested.

+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child

+                                  device to be started.

+

+  @retval EFI_SUCCESS         This driver supports this device.

+  @retval EFI_UNSUPPORTED     This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcDriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+

+/**

+  Start this driver on ControllerHandle. This service is called by the

+  EFI boot service ConnectController(). In order to make

+  drivers as small as possible, there are a few calling restrictions for

+  this service. ConnectController() must follow these

+  calling restrictions. If any other agent wishes to call Start() it

+  must also follow these calling restrictions.

+

+  @param[in]  This                 The pointer to the driver binding protocol.

+  @param[in]  ControllerHandle     The handle of device to be started.

+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child

+                                   device to be started.

+

+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.

+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.

+  @retval other                This driver does not support this device.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcDriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+

+/**

+  Stop this driver on ControllerHandle. This service is called by the

+  EFI boot service DisconnectController(). In order to

+  make drivers as small as possible, there are a few calling

+  restrictions for this service. DisconnectController()

+  must follow these calling restrictions. If any other agent wishes

+  to call Stop() it must also follow these calling restrictions.

+

+  @param[in]  This              Protocol instance pointer.

+  @param[in]  ControllerHandle  Handle of device to stop driver on

+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of

+                                children is zero stop the entire bus driver.

+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.

+

+  @retval EFI_SUCCESS           This driver is removed ControllerHandle

+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.

+  @retval Others                This driver was not removed from this device.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcDriverBindingStop (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN UINTN                        NumberOfChildren,

+  IN EFI_HANDLE                   *ChildHandleBuffer

+  );

+

+#endif

+

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c
new file mode 100644
index 0000000..7e5b4d6
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c
@@ -0,0 +1,2200 @@
+/** @file

+  This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.

+

+  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "PxeBcImpl.h"

+

+

+/**

+  Enables the use of the PXE Base Code Protocol functions.

+

+  This function enables the use of the PXE Base Code Protocol functions. If the

+  Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then

+  EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted

+  addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted

+  addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported

+  field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will

+  be returned. If there is not enough memory or other resources to start the PXE

+  Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the

+  PXE Base Code Protocol will be started.

+

+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]  UseIpv6           Specifies the type of IP addresses that are to be

+                                used during the session that is being started.

+                                Set to TRUE for IPv6, and FALSE for IPv4.

+

+  @retval EFI_SUCCESS           The PXE Base Code Protocol was started.

+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.

+  @retval EFI_UNSUPPORTED       UseIpv6 is TRUE, but the Ipv6Supported field of the

+                                EFI_PXE_BASE_CODE_MODE structure is FALSE.

+  @retval EFI_ALREADY_STARTED   The PXE Base Code Protocol is already in the started state.

+  @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid

+                                EFI_PXE_BASE_CODE_PROTOCOL structure.

+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory or other resources to start the

+                                PXE Base Code Protocol.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcStart (

+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN BOOLEAN                          UseIpv6

+  )

+{

+  PXEBC_PRIVATE_DATA      *Private;

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+  UINTN                   Index;

+  EFI_STATUS              Status;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode    = Private->PxeBc.Mode;

+

+  if (Mode->Started) {

+    return EFI_ALREADY_STARTED;

+  }

+

+  //

+  // Detect whether using IPv6 or not, and set it into mode data.

+  //

+  if (UseIpv6 && Mode->Ipv6Available && Mode->Ipv6Supported && Private->Ip6Nic != NULL) {

+    Mode->UsingIpv6 = TRUE;

+  } else if (!UseIpv6 && Private->Ip4Nic != NULL) {

+    Mode->UsingIpv6 = FALSE;

+  } else {

+    return EFI_UNSUPPORTED;

+  }

+

+  if (Mode->UsingIpv6) {

+    AsciiPrint ("\n>>Start PXE over IPv6");

+    //

+    // Configure block size for TFTP as a default value to handle all link layers.

+    //

+    Private->BlockSize = (UINTN) (Private->Ip6MaxPacketSize -

+                           PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);

+

+    //

+    // PXE over IPv6 starts here, initialize the fields and list header.

+    //

+    Private->Ip6Policy                          = PXEBC_IP6_POLICY_MAX;

+    Private->ProxyOffer.Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;

+    Private->DhcpAck.Dhcp6.Packet.Ack.Size      = PXEBC_DHCP6_PACKET_MAX_SIZE;

+    Private->PxeReply.Dhcp6.Packet.Ack.Size     = PXEBC_DHCP6_PACKET_MAX_SIZE;

+

+    for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {

+      Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;

+    }

+

+    //

+    // Create event and set status for token to capture ICMP6 error message.

+    //

+    Private->Icmp6Token.Status = EFI_NOT_READY;

+    Status = gBS->CreateEvent (

+                    EVT_NOTIFY_SIGNAL,

+                    TPL_NOTIFY,

+                    PxeBcIcmp6ErrorUpdate,

+                    Private,

+                    &Private->Icmp6Token.Event

+                    );

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+  } else {

+    AsciiPrint ("\n>>Start PXE over IPv4");

+    //

+    // Configure block size for TFTP as a default value to handle all link layers.

+    //

+    Private->BlockSize = (UINTN) (Private->Ip4MaxPacketSize -

+                           PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);

+

+    //

+    // PXE over IPv4 starts here, initialize the fields.

+    //

+    Private->ProxyOffer.Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;

+    Private->DhcpAck.Dhcp4.Packet.Ack.Size      = PXEBC_DHCP4_PACKET_MAX_SIZE;

+    Private->PxeReply.Dhcp4.Packet.Ack.Size     = PXEBC_DHCP4_PACKET_MAX_SIZE;

+

+    for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {

+      Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;

+    }

+

+    PxeBcSeedDhcp4Packet (&Private->SeedPacket, Private->Udp4Read);

+

+    //

+    // Create the event for Arp cache update.

+    //

+    Status = gBS->CreateEvent (

+                    EVT_TIMER | EVT_NOTIFY_SIGNAL,

+                    TPL_CALLBACK,

+                    PxeBcArpCacheUpdate,

+                    Private,

+                    &Private->ArpUpdateEvent

+                    );

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+

+    //

+    // Start a periodic timer by second to update Arp cache.

+    //

+    Status = gBS->SetTimer (

+                    Private->ArpUpdateEvent,

+                    TimerPeriodic,

+                    TICKS_PER_SECOND

+                    );

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+

+    //

+    // Create event and set status for token to capture ICMP error message.

+    //

+    Private->Icmp6Token.Status = EFI_NOT_READY;

+    Status = gBS->CreateEvent (

+                    EVT_NOTIFY_SIGNAL,

+                    TPL_NOTIFY,

+                    PxeBcIcmpErrorUpdate,

+                    Private,

+                    &Private->IcmpToken.Event

+                    );

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+  }

+

+  //

+  // If PcdTftpBlockSize is set to non-zero, override the default value.

+  //

+  if (PcdGet64 (PcdTftpBlockSize) != 0) {

+    Private->BlockSize   = (UINTN) PcdGet64 (PcdTftpBlockSize);

+  }

+

+  //

+  // Create event for UdpRead/UdpWrite timeout since they are both blocking API.

+  //

+  Status = gBS->CreateEvent (

+                  EVT_TIMER,

+                  TPL_CALLBACK,

+                  NULL,

+                  NULL,

+                  &Private->UdpTimeOutEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Private->IsAddressOk = FALSE;

+  Mode->Started        = TRUE;

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+  if (Mode->UsingIpv6) {

+    if (Private->Icmp6Token.Event != NULL) {

+      gBS->CloseEvent (Private->Icmp6Token.Event);

+      Private->Icmp6Token.Event = NULL;

+    }

+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);

+    Private->Ip6->Configure (Private->Ip6, NULL);

+  } else {

+    if (Private->ArpUpdateEvent != NULL) {

+      gBS->CloseEvent (Private->ArpUpdateEvent);

+      Private->ArpUpdateEvent = NULL;

+    }

+    if (Private->IcmpToken.Event != NULL) {

+      gBS->CloseEvent (Private->IcmpToken.Event);

+      Private->IcmpToken.Event = NULL;

+    }

+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);

+    Private->Ip4->Configure (Private->Ip4, NULL);

+  }

+  return Status;

+}

+

+

+/**

+  Disable the use of the PXE Base Code Protocol functions.

+

+  This function stops all activity on the network device. All the resources allocated

+  in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is

+  set to FALSE, and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE

+  structure is already FALSE, then EFI_NOT_STARTED will be returned.

+

+  @param[in]  This               Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+

+  @retval EFI_SUCCESS           The PXE Base Code Protocol was stopped.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is already in the stopped state.

+  @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid

+                                EFI_PXE_BASE_CODE_PROTOCOL structure.

+  @retval Others

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcStop (

+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This

+  )

+{

+  PXEBC_PRIVATE_DATA      *Private;

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+  BOOLEAN                 Ipv6Supported;

+  BOOLEAN                 Ipv6Available;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private       = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode          = Private->PxeBc.Mode;

+  Ipv6Supported = Mode->Ipv6Supported;

+  Ipv6Available = Mode->Ipv6Available;

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (Mode->UsingIpv6) {

+    //

+    // Configure all the instances for IPv6 as NULL.

+    //

+    ZeroMem (&Private->Udp6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));

+    ZeroMem (&Private->Ip6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));

+    Private->Dhcp6->Stop (Private->Dhcp6);

+    Private->Dhcp6->Configure (Private->Dhcp6, NULL);

+    Private->Udp6Write->Configure (Private->Udp6Write, NULL);

+    Private->Udp6Read->Groups (Private->Udp6Read, FALSE, NULL);

+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);

+    Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);

+    Private->Ip6->Configure (Private->Ip6, NULL);

+    PxeBcUnregisterIp6Address (Private);

+    if (Private->Icmp6Token.Event != NULL) {

+      gBS->CloseEvent (Private->Icmp6Token.Event);

+      Private->Icmp6Token.Event = NULL;

+    }

+    if (Private->Dhcp6Request != NULL) {

+      FreePool (Private->Dhcp6Request);

+      Private->Dhcp6Request = NULL;

+    }

+    if (Private->BootFileName != NULL) {

+      FreePool (Private->BootFileName);

+      Private->BootFileName = NULL;

+    }

+  } else {

+    //

+    // Configure all the instances for IPv4 as NULL.

+    //

+    ZeroMem (&Private->Udp4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));

+    ZeroMem (&Private->Udp4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));

+    ZeroMem (&Private->Ip4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));

+    ZeroMem (&Private->Ip4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));

+    Private->Dhcp4->Stop (Private->Dhcp4);

+    Private->Dhcp4->Configure (Private->Dhcp4, NULL);

+    Private->Udp4Write->Configure (Private->Udp4Write, NULL);

+    Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL);

+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);

+    Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);

+    Private->Ip4->Configure (Private->Ip4, NULL);

+    if (Private->ArpUpdateEvent != NULL) {

+      gBS->CloseEvent (Private->ArpUpdateEvent);

+      Private->ArpUpdateEvent = NULL;

+    }

+    if (Private->IcmpToken.Event != NULL) {

+      gBS->CloseEvent (Private->IcmpToken.Event);

+      Private->IcmpToken.Event = NULL;

+    }

+  }

+

+  gBS->CloseEvent (Private->UdpTimeOutEvent);

+  Private->CurSrcPort   = 0;

+  Private->BootFileSize = 0;

+

+  //

+  // Reset the mode data.

+  //

+  ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE));

+  Mode->Ipv6Available = Ipv6Available;

+  Mode->Ipv6Supported = Ipv6Supported;

+  Mode->AutoArp       = TRUE;

+  Mode->TTL           = DEFAULT_TTL;

+  Mode->ToS           = DEFAULT_ToS;

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6

+  S.A.R.R (solicit / advertise / request / reply) sequence.

+

+  If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before

+  they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will

+  be tried in the order in which they are received. Please see the Preboot Execution

+  Environment (PXE) Specification and Unified Extensible Firmware Interface (UEFI)

+  Specification for additional details on the implementation of DHCP.

+  If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,

+  then the DHCP sequence will be stopped and EFI_ABORTED will be returned.

+

+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]  SortOffers        TRUE if the offers received should be sorted. Set to FALSE to

+                                try the offers in the order that they are received.

+

+  @retval EFI_SUCCESS           Valid DHCP has completed.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid

+                                EFI_PXE_BASE_CODE_PROTOCOL structure.

+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.

+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory to complete the DHCP Protocol.

+  @retval EFI_ABORTED           The callback function aborted the DHCP Protocol.

+  @retval EFI_TIMEOUT           The DHCP Protocol timed out.

+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the DHCP session.

+  @retval EFI_NO_RESPONSE       Valid PXE offer was not received.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcDhcp (

+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN BOOLEAN                          SortOffers

+  )

+{

+  PXEBC_PRIVATE_DATA           *Private;

+  EFI_PXE_BASE_CODE_MODE       *Mode;

+  EFI_STATUS                   Status;

+  EFI_PXE_BASE_CODE_IP_FILTER  IpFilter;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status                  = EFI_SUCCESS;

+  Private                 = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode                    = Private->PxeBc.Mode;

+  Mode->IcmpErrorReceived = FALSE;

+  Private->Function       = EFI_PXE_BASE_CODE_FUNCTION_DHCP;

+  Private->IsOfferSorted  = SortOffers;

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (Mode->UsingIpv6) {

+

+    //

+    // Stop Udp6Read instance

+    //

+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);

+

+    //

+    // Start S.A.R.R. process to get a IPv6 address and other boot information.

+    //

+    Status = PxeBcDhcp6Sarr (Private, Private->Dhcp6);

+  } else {

+

+    //

+    // Stop Udp4Read instance

+    //

+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);

+

+    //

+    // Start D.O.R.A. process to get a IPv4 address and other boot information.

+    //

+    Status = PxeBcDhcp4Dora (Private, Private->Dhcp4);

+  }

+

+  //

+  // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP

+  // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.

+  //

+  ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));

+  IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;

+  This->SetIpFilter (This, &IpFilter);

+

+  return Status;

+}

+

+

+/**

+  Attempts to complete the PXE Boot Server and/or boot image discovery sequence.

+

+  This function attempts to complete the PXE Boot Server and/or boot image discovery

+  sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the

+  PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the

+  EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the

+  PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure

+  will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE.

+  In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[],

+  has two uses: It is the Boot Server IP address list used for unicast discovery

+  (if the UseUCast field is TRUE), and it is the list used for Boot Server verification

+  (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure

+  is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot

+  Server reply of that type will be accepted. If the AcceptAnyResponse field is

+  FALSE, only responses from Boot Servers with matching IP addresses will be accepted.

+  This function can take at least 10 seconds to timeout and return control to the

+  caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be

+  returned. Please see the Preboot Execution Environment (PXE) Specification for

+  additional details on the implementation of the Discovery sequence.

+  If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,

+  then the Discovery sequence is stopped and EFI_ABORTED will be returned.

+

+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]  Type              The type of bootstrap to perform.

+  @param[in]  Layer             Pointer to the boot server layer number to discover, which must be

+                                PXE_BOOT_LAYER_INITIAL when a new server type is being

+                                discovered.

+  @param[in]  UseBis            TRUE if Boot Integrity Services are to be used. FALSE otherwise.

+  @param[in]  Info              Pointer to a data structure that contains additional information

+                                on the type of discovery operation that is to be performed.

+                                It is optional.

+

+  @retval EFI_SUCCESS           The Discovery sequence has been completed.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.

+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.

+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory to complete Discovery.

+  @retval EFI_ABORTED           The callback function aborted the Discovery sequence.

+  @retval EFI_TIMEOUT           The Discovery sequence timed out.

+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the PXE discovery

+                                session.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcDiscover (

+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN UINT16                           Type,

+  IN UINT16                           *Layer,

+  IN BOOLEAN                          UseBis,

+  IN EFI_PXE_BASE_CODE_DISCOVER_INFO  *Info   OPTIONAL

+  )

+{

+  PXEBC_PRIVATE_DATA              *Private;

+  EFI_PXE_BASE_CODE_MODE          *Mode;

+  EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;

+  EFI_PXE_BASE_CODE_SRVLIST       *SrvList;

+  PXEBC_BOOT_SVR_ENTRY            *BootSvrEntry;

+  UINT16                          Index;

+  EFI_STATUS                      Status;

+  EFI_PXE_BASE_CODE_IP_FILTER     IpFilter;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private                 = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode                    = Private->PxeBc.Mode;

+  Mode->IcmpErrorReceived = FALSE;

+  BootSvrEntry            = NULL;

+  SrvList                 = NULL;

+  Status                  = EFI_DEVICE_ERROR;

+  Private->Function       = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  //

+  // Station address should be ready before do discover.

+  //

+  if (!Private->IsAddressOk) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Mode->UsingIpv6) {

+

+    //

+    // Stop Udp6Read instance

+    //

+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);

+  } else {

+

+    //

+    // Stop Udp4Read instance

+    //

+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);

+  }

+

+  //

+  // There are 3 methods to get the information for discover.

+  //

+  if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) {

+    //

+    // 1. Take the previous setting as the discover info.

+    //

+    if (!Mode->PxeDiscoverValid ||

+        !Mode->PxeReplyReceived ||

+        (!Mode->PxeBisReplyReceived && UseBis)) {

+      Status = EFI_INVALID_PARAMETER;

+      goto ON_EXIT;

+    }

+

+    Info                         = &DefaultInfo;

+    Info->IpCnt                  = 1;

+    Info->UseUCast               = TRUE;

+    SrvList                      = Info->SrvList;

+    SrvList[0].Type              = Type;

+    SrvList[0].AcceptAnyResponse = FALSE;

+

+    CopyMem (&SrvList->IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));

+

+  } else if (Info == NULL) {

+    //

+    // 2. Extract the discover information from the cached packets if unspecified.

+    //

+    Info   = &DefaultInfo;

+    Status = PxeBcExtractDiscoverInfo (Private, Type, Info, &BootSvrEntry, &SrvList);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+

+  } else {

+    //

+    // 3. Take the pass-in information as the discover info, and validate the server list.

+    //

+    SrvList = Info->SrvList;

+

+    if (!SrvList[0].AcceptAnyResponse) {

+      for (Index = 1; Index < Info->IpCnt; Index++) {

+        if (SrvList[Index].AcceptAnyResponse) {

+          break;

+        }

+      }

+      if (Index != Info->IpCnt) {

+        //

+        // It's invalid if the first server doesn't accecpt any response

+        // and meanwhile any of the rest servers accept any reponse.

+        //

+        Status = EFI_INVALID_PARAMETER;

+        goto ON_EXIT;

+      }

+    }

+  }

+

+  //

+  // Info and BootSvrEntry/SrvList are all ready by now, so execute discover by UniCast/BroadCast/MultiCast.

+  //

+  if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) ||

+      (Info->MustUseList && Info->IpCnt == 0)) {

+    Status = EFI_INVALID_PARAMETER;

+    goto ON_EXIT;

+  }

+

+  Private->IsDoDiscover = TRUE;

+

+  if (Info->UseUCast) {

+    //

+    // Do discover by unicast.

+    //

+    for (Index = 0; Index < Info->IpCnt; Index++) {

+      if (BootSvrEntry == NULL) {

+        CopyMem (&Private->ServerIp, &SrvList[Index].IpAddr, sizeof (EFI_IP_ADDRESS));

+      } else {

+        ASSERT (!Mode->UsingIpv6);

+        ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));

+        CopyMem (&Private->ServerIp, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));

+      }

+

+      Status = PxeBcDiscoverBootServer (

+                 Private,

+                 Type,

+                 Layer,

+                 UseBis,

+                 &SrvList[Index].IpAddr,

+                 0,

+                 NULL

+                 );

+    }

+  } else if (Info->UseMCast) {

+    //

+    // Do discover by multicast.

+    //

+    Status = PxeBcDiscoverBootServer (

+               Private,

+               Type,

+               Layer,

+               UseBis,

+               &Info->ServerMCastIp,

+               0,

+               NULL

+               );

+

+  } else if (Info->UseBCast) {

+    //

+    // Do discover by broadcast, but only valid for IPv4.

+    //

+    ASSERT (!Mode->UsingIpv6);

+    Status = PxeBcDiscoverBootServer (

+               Private,

+               Type,

+               Layer,

+               UseBis,

+               NULL,

+               Info->IpCnt,

+               SrvList

+               );

+  }

+

+  if (!EFI_ERROR (Status)) {

+    //

+    // Parse the cached PXE reply packet, and store it into mode data if valid.

+    //

+    if (Mode->UsingIpv6) {

+      Status = PxeBcParseDhcp6Packet (&Private->PxeReply.Dhcp6);

+      if (!EFI_ERROR (Status)) {

+        CopyMem (

+          &Mode->PxeReply.Dhcpv6,

+          &Private->PxeReply.Dhcp6.Packet.Offer,

+          Private->PxeReply.Dhcp6.Packet.Offer.Length

+          );

+        Mode->PxeReplyReceived = TRUE;

+        Mode->PxeDiscoverValid = TRUE;

+      }

+    } else {

+      Status = PxeBcParseDhcp4Packet (&Private->PxeReply.Dhcp4);

+      if (!EFI_ERROR (Status)) {

+        CopyMem (

+          &Mode->PxeReply.Dhcpv4,

+          &Private->PxeReply.Dhcp4.Packet.Offer,

+          Private->PxeReply.Dhcp4.Packet.Offer.Length

+          );

+        Mode->PxeReplyReceived = TRUE;

+        Mode->PxeDiscoverValid = TRUE;

+      }

+    }

+  }

+

+ON_EXIT:

+

+  //

+  // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP

+  // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.

+  //

+  ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));

+  IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;

+  This->SetIpFilter (This, &IpFilter);

+

+  return Status;

+}

+

+

+/**

+  Used to perform TFTP and MTFTP services.

+

+  This function is used to perform TFTP and MTFTP services. This includes the

+  TFTP operations to get the size of a file, read a directory, read a file, and

+  write a file. It also includes the MTFTP operations to get the size of a file,

+  read a directory, and read a file. The type of operation is specified by Operation.

+  If the callback function that is invoked during the TFTP/MTFTP operation does

+  not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will

+  be returned.

+  For read operations, the return data will be placed in the buffer specified by

+  BufferPtr. If BufferSize is too small to contain the entire downloaded file,

+  then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero,

+  or the size of the requested file. (NOTE: the size of the requested file is only returned

+  if the TFTP server supports TFTP options). If BufferSize is large enough for the

+  read operation, then BufferSize will be set to the size of the downloaded file,

+  and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services

+  should use the get-file-size operations to determine the size of the downloaded

+  file prior to using the read-file operations-especially when downloading large

+  (greater than 64 MB) files-instead of making two calls to the read-file operation.

+  Following this recommendation will save time if the file is larger than expected

+  and the TFTP server does not support TFTP option extensions. Without TFTP option

+  extension support, the client must download the entire file, counting and discarding

+  the received packets, to determine the file size.

+  For write operations, the data to be sent is in the buffer specified by BufferPtr.

+  BufferSize specifies the number of bytes to send. If the write operation completes

+  successfully, then EFI_SUCCESS will be returned.

+  For TFTP "get file size" operations, the size of the requested file or directory

+  is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server

+  does not support options, the file will be downloaded into a bit bucket and the

+  length of the downloaded file will be returned. For MTFTP "get file size" operations,

+  if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED

+  will be returned.

+  This function can take up to 10 seconds to timeout and return control to the caller.

+  If the TFTP sequence does not complete, EFI_TIMEOUT will be returned.

+  If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,

+  then the TFTP sequence is stopped and EFI_ABORTED will be returned.

+

+  @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]      Operation     The type of operation to perform.

+  @param[in, out] BufferPtr     A pointer to the data buffer.

+  @param[in]      Overwrite     Only used on write file operations. TRUE if a file on a remote

+                                server can be overwritten.

+  @param[in, out] BufferSize    For get-file-size operations, *BufferSize returns the size of the

+                                requested file.

+  @param[in]      BlockSize     The requested block size to be used during a TFTP transfer.

+  @param[in]      ServerIp      The TFTP / MTFTP server IP address.

+  @param[in]      Filename      A Null-terminated ASCII string that specifies a directory name

+                                or a file name.

+  @param[in]      Info          Pointer to the MTFTP information.

+  @param[in]      DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation.

+

+  @retval EFI_SUCCESS           The TFTP/MTFTP operation was completed.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.

+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.

+  @retval EFI_BUFFER_TOO_SMALL  The buffer is not large enough to complete the read operation.

+  @retval EFI_ABORTED           The callback function aborted the TFTP/MTFTP operation.

+  @retval EFI_TIMEOUT           The TFTP/MTFTP operation timed out.

+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the MTFTP session.

+  @retval EFI_TFTP_ERROR        A TFTP error packet was received during the MTFTP session.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcMtftp (

+  IN     EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN     EFI_PXE_BASE_CODE_TFTP_OPCODE    Operation,

+  IN OUT VOID                             *BufferPtr    OPTIONAL,

+  IN     BOOLEAN                          Overwrite,

+  IN OUT UINT64                           *BufferSize,

+  IN     UINTN                            *BlockSize    OPTIONAL,

+  IN     EFI_IP_ADDRESS                   *ServerIp,

+  IN     UINT8                            *Filename,

+  IN     EFI_PXE_BASE_CODE_MTFTP_INFO     *Info         OPTIONAL,

+  IN     BOOLEAN                          DontUseBuffer

+  )

+{

+  PXEBC_PRIVATE_DATA              *Private;

+  EFI_PXE_BASE_CODE_MODE          *Mode;

+  EFI_MTFTP4_CONFIG_DATA          Mtftp4Config;

+  EFI_MTFTP6_CONFIG_DATA          Mtftp6Config;

+  VOID                            *Config;

+  EFI_STATUS                      Status;

+  EFI_PXE_BASE_CODE_IP_FILTER     IpFilter;

+

+

+  if ((This == NULL) ||

+      (Filename == NULL) ||

+      (BufferSize == NULL) ||

+      (ServerIp == NULL) ||

+      ((BufferPtr == NULL) && DontUseBuffer) ||

+      ((BlockSize != NULL) && (*BlockSize < PXE_MTFTP_DEFAULT_BLOCK_SIZE)) ||

+      (!NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 0) && !NetIp6IsValidUnicast (&ServerIp->v6))) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Config    = NULL;

+  Status    = EFI_DEVICE_ERROR;

+  Private   = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode      = Private->PxeBc.Mode;

+

+  if (Mode->UsingIpv6) {

+    //

+    // Set configuration data for Mtftp6 instance.

+    //

+    ZeroMem (&Mtftp6Config, sizeof (EFI_MTFTP6_CONFIG_DATA));

+    Config                         = &Mtftp6Config;

+    Mtftp6Config.TimeoutValue      = PXEBC_MTFTP_TIMEOUT;

+    Mtftp6Config.TryCount          = PXEBC_MTFTP_RETRIES;

+    CopyMem (&Mtftp6Config.StationIp, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));

+    CopyMem (&Mtftp6Config.ServerIp, &ServerIp->v6, sizeof (EFI_IPv6_ADDRESS));

+    //

+    // Stop Udp6Read instance

+    //

+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);

+  } else {

+    //

+    // Set configuration data for Mtftp4 instance.

+    //

+    ZeroMem (&Mtftp4Config, sizeof (EFI_MTFTP4_CONFIG_DATA));

+    Config                         = &Mtftp4Config;

+    Mtftp4Config.UseDefaultSetting = FALSE;

+    Mtftp4Config.TimeoutValue      = PXEBC_MTFTP_TIMEOUT;

+    Mtftp4Config.TryCount          = PXEBC_MTFTP_RETRIES;

+    CopyMem (&Mtftp4Config.StationIp, &Private->StationIp.v4, sizeof (EFI_IPv4_ADDRESS));

+    CopyMem (&Mtftp4Config.SubnetMask, &Private->SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));

+    CopyMem (&Mtftp4Config.GatewayIp, &Private->GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));

+    CopyMem (&Mtftp4Config.ServerIp, &ServerIp->v4, sizeof (EFI_IPv4_ADDRESS));

+    //

+    // Stop Udp4Read instance

+    //

+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);

+  }

+

+  Mode->TftpErrorReceived = FALSE;

+  Mode->IcmpErrorReceived = FALSE;

+

+  switch (Operation) {

+

+  case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE:

+    //

+    // Send TFTP request to get file size.

+    //

+    Status = PxeBcTftpGetFileSize (

+               Private,

+               Config,

+               Filename,

+               BlockSize,

+               BufferSize

+               );

+

+    break;

+

+  case EFI_PXE_BASE_CODE_TFTP_READ_FILE:

+    //

+    // Send TFTP request to read file.

+    //

+    Status = PxeBcTftpReadFile (

+               Private,

+               Config,

+               Filename,

+               BlockSize,

+               BufferPtr,

+               BufferSize,

+               DontUseBuffer

+               );

+

+    break;

+

+  case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:

+    //

+    // Send TFTP request to write file.

+    //

+    Status = PxeBcTftpWriteFile (

+               Private,

+               Config,

+               Filename,

+               Overwrite,

+               BlockSize,

+               BufferPtr,

+               BufferSize

+               );

+

+    break;

+

+  case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:

+    //

+    // Send TFTP request to read directory.

+    //

+    Status = PxeBcTftpReadDirectory (

+               Private,

+               Config,

+               Filename,

+               BlockSize,

+               BufferPtr,

+               BufferSize,

+               DontUseBuffer

+               );

+

+    break;

+

+  case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE:

+  case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:

+  case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:

+    Status = EFI_UNSUPPORTED;

+

+    break;

+

+  default:

+    Status = EFI_INVALID_PARAMETER;

+

+    break;

+  }

+

+  if (Status == EFI_ICMP_ERROR) {

+    Mode->IcmpErrorReceived = TRUE;

+  }

+

+  //

+  // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP

+  // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.

+  //

+  ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));

+  IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;

+  This->SetIpFilter (This, &IpFilter);

+

+  return Status;

+}

+

+

+/**

+  Writes a UDP packet to the network interface.

+

+  This function writes a UDP packet specified by the (optional HeaderPtr and)

+  BufferPtr parameters to the network interface. The UDP header is automatically

+  built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp,

+  SrcIp, and SrcPort to build this header. If the packet is successfully built and

+  transmitted through the network interface, then EFI_SUCCESS will be returned.

+  If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will

+  be returned. If an ICMP error occurs during the transmission of the packet, then

+  the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and

+  EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return

+  EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned.

+

+  @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]      OpFlags       The UDP operation flags.

+  @param[in]      DestIp        The destination IP address.

+  @param[in]      DestPort      The destination UDP port number.

+  @param[in]      GatewayIp     The gateway IP address.

+  @param[in]      SrcIp         The source IP address.

+  @param[in, out] SrcPort       The source UDP port number.

+  @param[in]      HeaderSize    An optional field which may be set to the length of a header

+                                at HeaderPtr to be prefixed to the data at BufferPtr.

+  @param[in]  HeaderPtr         If HeaderSize is not NULL, a pointer to a header to be

+                                prefixed to the data at BufferPtr.

+  @param[in]  BufferSize        A pointer to the size of the data at BufferPtr.

+  @param[in]  BufferPtr         A pointer to the data to be written.

+

+  @retval EFI_SUCCESS           The UDP Write operation completed.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.

+  @retval EFI_BAD_BUFFER_SIZE   The buffer is too long to be transmitted.

+  @retval EFI_ABORTED           The callback function aborted the UDP Write operation.

+  @retval EFI_TIMEOUT           The UDP Write operation timed out.

+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the UDP write session.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcUdpWrite (

+  IN     EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN     UINT16                           OpFlags,

+  IN     EFI_IP_ADDRESS                   *DestIp,

+  IN     EFI_PXE_BASE_CODE_UDP_PORT       *DestPort,

+  IN     EFI_IP_ADDRESS                   *GatewayIp  OPTIONAL,

+  IN     EFI_IP_ADDRESS                   *SrcIp      OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT       *SrcPort    OPTIONAL,

+  IN     UINTN                            *HeaderSize OPTIONAL,

+  IN     VOID                             *HeaderPtr  OPTIONAL,

+  IN     UINTN                            *BufferSize,

+  IN     VOID                             *BufferPtr

+  )

+{

+  PXEBC_PRIVATE_DATA        *Private;

+  EFI_PXE_BASE_CODE_MODE    *Mode;

+  EFI_UDP4_SESSION_DATA     Udp4Session;

+  EFI_UDP6_SESSION_DATA     Udp6Session;

+  EFI_STATUS                Status;

+  BOOLEAN                   DoNotFragment;

+

+  if (This == NULL || DestIp == NULL || DestPort == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode    = Private->PxeBc.Mode;

+

+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT) != 0) {

+    DoNotFragment = FALSE;

+  } else {

+    DoNotFragment = TRUE;

+  }

+

+  if (!Mode->UsingIpv6 && GatewayIp != NULL && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 0)) {

+    //

+    // Gateway is provided but it's not a unicast IPv4 address, while it will be ignored for IPv6.

+    //

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (HeaderSize != NULL && (*HeaderSize == 0 || HeaderPtr == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (BufferSize == NULL || (*BufferSize != 0 && BufferPtr == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (!Private->IsAddressOk && SrcIp == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Private->CurSrcPort == 0 ||

+      (SrcPort != NULL && *SrcPort != Private->CurSrcPort)) {

+    //

+    // Reconfigure UDPv4/UDPv6 for UdpWrite if the source port changed.

+    //

+    if (SrcPort != NULL) {

+      Private->CurSrcPort = *SrcPort;

+    }

+  }

+

+  if (Mode->UsingIpv6) {

+    Status = PxeBcConfigUdp6Write (

+               Private->Udp6Write,

+               &Private->StationIp.v6,

+               &Private->CurSrcPort

+               );

+  } else {

+    //

+    // Configure the UDPv4 instance with gateway information from DHCP server as default.

+    //

+    Status = PxeBcConfigUdp4Write (

+               Private->Udp4Write,

+               &Private->StationIp.v4,

+               &Private->SubnetMask.v4,

+               &Private->GatewayIp.v4,

+               &Private->CurSrcPort,

+               DoNotFragment

+               );

+  }

+

+  if (EFI_ERROR (Status)) {

+    Private->CurSrcPort = 0;

+    return EFI_INVALID_PARAMETER;

+  } else if (SrcPort != NULL) {

+    *SrcPort = Private->CurSrcPort;

+  }

+

+  //

+  // Start a timer as timeout event for this blocking API.

+  //

+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);

+

+  if (Mode->UsingIpv6) {

+    //

+    // Construct UDPv6 session data.

+    //

+    ZeroMem (&Udp6Session, sizeof (EFI_UDP6_SESSION_DATA));

+    CopyMem (&Udp6Session.DestinationAddress, DestIp, sizeof (EFI_IPv6_ADDRESS));

+    Udp6Session.DestinationPort = *DestPort;

+    if (SrcIp != NULL) {

+      CopyMem (&Udp6Session.SourceAddress, SrcIp, sizeof (EFI_IPv6_ADDRESS));

+    }

+    if (SrcPort != NULL) {

+      Udp6Session.SourcePort = *SrcPort;

+    }

+

+    Status = PxeBcUdp6Write (

+               Private->Udp6Write,

+               &Udp6Session,

+               Private->UdpTimeOutEvent,

+               HeaderSize,

+               HeaderPtr,

+               BufferSize,

+               BufferPtr

+               );

+  } else {

+    //

+    // Construct UDPv4 session data.

+    //

+    ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA));

+    CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));

+    Udp4Session.DestinationPort = *DestPort;

+    if (SrcIp != NULL) {

+      CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS));

+    }

+    if (SrcPort != NULL) {

+      Udp4Session.SourcePort = *SrcPort;

+    }

+    //

+    // Override the gateway information if user specified.

+    //

+    Status = PxeBcUdp4Write (

+               Private->Udp4Write,

+               &Udp4Session,

+               Private->UdpTimeOutEvent,

+               (EFI_IPv4_ADDRESS *) GatewayIp,

+               HeaderSize,

+               HeaderPtr,

+               BufferSize,

+               BufferPtr

+               );

+  }

+

+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);

+

+

+  //

+  // Reset the UdpWrite instance.

+  //

+  if (Mode->UsingIpv6) {

+    Private->Udp6Write->Configure (Private->Udp6Write, NULL);

+  } else {

+    Private->Udp4Write->Configure (Private->Udp4Write, NULL);

+  }

+

+  return Status;

+}

+

+

+/**

+  Reads a UDP packet from the network interface.

++

+  This function reads a UDP packet from a network interface. The data contents

+  are returned in (the optional HeaderPtr and) BufferPtr, and the size of the

+  buffer received is returned in BufferSize . If the input BufferSize is smaller

+  than the UDP packet received (less optional HeaderSize), it will be set to the

+  required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the

+  contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is

+  successfully received, then EFI_SUCCESS will be returned, and the information

+  from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if

+  they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort,

+  SrcIp, and SrcPort input values, different types of UDP packet receive filtering

+  will be performed. The following tables summarize these receive filter operations.

+

+  @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]      OpFlags       The UDP operation flags.

+  @param[in, out] DestIp        The destination IP address.

+  @param[in, out] DestPort      The destination UDP port number.

+  @param[in, out] SrcIp         The source IP address.

+  @param[in, out] SrcPort       The source UDP port number.

+  @param[in]      HeaderSize    An optional field which may be set to the length of a

+                                header at HeaderPtr to be prefixed to the data at BufferPtr.

+  @param[in]      HeaderPtr     If HeaderSize is not NULL, a pointer to a header to be

+                                prefixed to the data at BufferPtr.

+  @param[in, out] BufferSize    A pointer to the size of the data at BufferPtr.

+  @param[in]      BufferPtr     A pointer to the data to be read.

+

+  @retval EFI_SUCCESS           The UDP Read operation was completed.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.

+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.

+  @retval EFI_BUFFER_TOO_SMALL  The packet is larger than Buffer can hold.

+  @retval EFI_ABORTED           The callback function aborted the UDP Read operation.

+  @retval EFI_TIMEOUT           The UDP Read operation timed out.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcUdpRead (

+  IN     EFI_PXE_BASE_CODE_PROTOCOL   *This,

+  IN     UINT16                       OpFlags,

+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,

+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL,

+  IN     UINTN                        *HeaderSize  OPTIONAL,

+  IN     VOID                         *HeaderPtr   OPTIONAL,

+  IN OUT UINTN                        *BufferSize,

+  IN     VOID                         *BufferPtr

+  )

+{

+  PXEBC_PRIVATE_DATA          *Private;

+  EFI_PXE_BASE_CODE_MODE      *Mode;

+  EFI_UDP4_COMPLETION_TOKEN   Udp4Token;

+  EFI_UDP6_COMPLETION_TOKEN   Udp6Token;

+  EFI_UDP4_RECEIVE_DATA       *Udp4Rx;

+  EFI_UDP6_RECEIVE_DATA       *Udp6Rx;

+  EFI_STATUS                  Status;

+  BOOLEAN                     IsDone;

+  BOOLEAN                     IsMatched;

+  UINTN                       CopiedLen;

+

+  if (This == NULL || DestIp == NULL || DestPort == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private   = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode      = Private->PxeBc.Mode;

+  IsDone    = FALSE;

+  IsMatched = FALSE;

+  Udp4Rx    = NULL;

+  Udp6Rx    = NULL;

+

+  if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0 && DestPort == NULL) ||

+      ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0 && SrcIp == NULL) ||

+      ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0 && SrcPort == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if ((HeaderSize != NULL && *HeaderSize == 0) || (HeaderSize != NULL && HeaderPtr == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if ((BufferSize == NULL) || (BufferPtr == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  ZeroMem (&Udp6Token, sizeof (EFI_UDP6_COMPLETION_TOKEN));

+  ZeroMem (&Udp4Token, sizeof (EFI_UDP4_COMPLETION_TOKEN));

+

+  if (Mode->UsingIpv6) {

+    Status = gBS->CreateEvent (

+                    EVT_NOTIFY_SIGNAL,

+                    TPL_NOTIFY,

+                    PxeBcCommonNotify,

+                    &IsDone,

+                    &Udp6Token.Event

+                    );

+    if (EFI_ERROR (Status)) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  } else {

+    Status = gBS->CreateEvent (

+                    EVT_NOTIFY_SIGNAL,

+                    TPL_NOTIFY,

+                    PxeBcCommonNotify,

+                    &IsDone,

+                    &Udp4Token.Event

+                    );

+    if (EFI_ERROR (Status)) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  }

+

+  //

+  // Start a timer as timeout event for this blocking API.

+  //

+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);

+  Mode->IcmpErrorReceived = FALSE;

+

+  //

+  // Read packet by Udp4Read/Udp6Read until matched or timeout.

+  //

+  while (!IsMatched && !EFI_ERROR (Status)) {

+    if (Mode->UsingIpv6) {

+      Status = PxeBcUdp6Read (

+                 Private->Udp6Read,

+                 &Udp6Token,

+                 Mode,

+                 Private->UdpTimeOutEvent,

+                 OpFlags,

+                 &IsDone,

+                 &IsMatched,

+                 DestIp,

+                 DestPort,

+                 SrcIp,

+                 SrcPort

+                 );

+    } else {

+      Status = PxeBcUdp4Read (

+                 Private->Udp4Read,

+                 &Udp4Token,

+                 Mode,

+                 Private->UdpTimeOutEvent,

+                 OpFlags,

+                 &IsDone,

+                 &IsMatched,

+                 DestIp,

+                 DestPort,

+                 SrcIp,

+                 SrcPort

+                 );

+    }

+  }

+

+  if (Status == EFI_ICMP_ERROR ||

+      Status == EFI_NETWORK_UNREACHABLE ||

+      Status == EFI_HOST_UNREACHABLE ||

+      Status == EFI_PROTOCOL_UNREACHABLE ||

+      Status == EFI_PORT_UNREACHABLE) {

+    //

+    // Get different return status for icmp error from Udp, refers to UEFI spec.

+    //

+    Mode->IcmpErrorReceived = TRUE;

+  }

+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);

+

+  if (IsMatched) {

+    //

+    // Copy the rececived packet to user if matched by filter.

+    //

+    CopiedLen = 0;

+    if (Mode->UsingIpv6) {

+      Udp6Rx = Udp6Token.Packet.RxData;

+      ASSERT (Udp6Rx != NULL);

+      //

+      // Copy the header part of received data.

+      //

+      if (HeaderSize != NULL) {

+        CopiedLen   = MIN (*HeaderSize, Udp6Rx->DataLength);

+        *HeaderSize = CopiedLen;

+        CopyMem (HeaderPtr, Udp6Rx->FragmentTable[0].FragmentBuffer, *HeaderSize);

+      }

+      //

+      // Copy the other part of received data.

+      //

+      if (Udp6Rx->DataLength - CopiedLen > *BufferSize) {

+        Status = EFI_BUFFER_TOO_SMALL;

+      } else {

+        *BufferSize = Udp6Rx->DataLength - CopiedLen;

+        CopyMem (BufferPtr, (UINT8 *) Udp6Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize);

+      }

+      //

+      // Recycle the receiving buffer after copy to user.

+      //

+      gBS->SignalEvent (Udp6Rx->RecycleSignal);

+    } else {

+      Udp4Rx = Udp4Token.Packet.RxData;

+      ASSERT (Udp4Rx != NULL);

+      //

+      // Copy the header part of received data.

+      //

+      if (HeaderSize != NULL) {

+        CopiedLen   = MIN (*HeaderSize, Udp4Rx->DataLength);

+        *HeaderSize = CopiedLen;

+        CopyMem (HeaderPtr, Udp4Rx->FragmentTable[0].FragmentBuffer, *HeaderSize);

+      }

+      //

+      // Copy the other part of received data.

+      //

+      if (Udp4Rx->DataLength - CopiedLen > *BufferSize) {

+        Status = EFI_BUFFER_TOO_SMALL;

+      } else {

+        *BufferSize = Udp4Rx->DataLength - CopiedLen;

+        CopyMem (BufferPtr, (UINT8 *) Udp4Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize);

+      }

+      //

+      // Recycle the receiving buffer after copy to user.

+      //

+      gBS->SignalEvent (Udp4Rx->RecycleSignal);

+    }

+  }

+

+  if (Mode->UsingIpv6) {

+    Private->Udp6Read->Cancel (Private->Udp6Read, &Udp6Token);

+    gBS->CloseEvent (Udp6Token.Event);

+  } else {

+    Private->Udp4Read->Cancel (Private->Udp4Read, &Udp4Token);

+    gBS->CloseEvent (Udp4Token.Event);

+  }

+

+  return Status;

+}

+

+

+/**

+  Updates the IP receive filters of a network device and enables software filtering.

+

+  The NewFilter field is used to modify the network device's current IP receive

+  filter settings and to enable a software filter. This function updates the IpFilter

+  field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter.

+  The software filter is used when the USE_FILTER in OpFlags is set to UdpRead().

+  The current hardware filter remains in effect no matter what the settings of OpFlags.

+  This is so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those

+  packets whose reception is enabled in hardware-physical NIC address (unicast),

+  broadcast address, logical address or addresses (multicast), or all (promiscuous).

+  UdpRead() does not modify the IP filter settings.

+  Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive

+  filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.

+  If an application or driver wishes to preserve the IP receive filter settings,

+  it will have to preserve the IP receive filter settings before these calls, and

+  use SetIpFilter() to restore them after the calls. If incompatible filtering is

+  requested (for example, PROMISCUOUS with anything else), or if the device does not

+  support a requested filter setting and it cannot be accommodated in software

+  (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned.

+  The IPlist field is used to enable IPs other than the StationIP. They may be

+  multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP,

+  then both the StationIP and the IPs from the IPlist will be used.

+

+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]  NewFilter         Pointer to the new set of IP receive filters.

+

+  @retval EFI_SUCCESS           The IP receive filter settings were updated.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcSetIpFilter (

+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN EFI_PXE_BASE_CODE_IP_FILTER      *NewFilter

+  )

+{

+  EFI_STATUS                Status;

+  PXEBC_PRIVATE_DATA        *Private;

+  EFI_PXE_BASE_CODE_MODE    *Mode;

+  EFI_UDP4_CONFIG_DATA      Udp4Cfg;

+  EFI_UDP6_CONFIG_DATA      Udp6Cfg;

+  UINTN                     Index;

+  BOOLEAN                   NeedPromiscuous;

+

+  if (This == NULL || NewFilter == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private         = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode            = Private->PxeBc.Mode;

+  Status          = EFI_SUCCESS;

+  NeedPromiscuous = FALSE;

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  for (Index = 0; Index < NewFilter->IpCnt; Index++) {

+    ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);

+    if (!Mode->UsingIpv6 &&

+        IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) {

+      //

+      // IPv4 broadcast address should not be in IP filter.

+      //

+      return EFI_INVALID_PARAMETER;

+    }

+    if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&

+        (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) ||

+         NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6))) {

+      //

+      // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IPv4/IPv6 address

+      // is in IpList, promiscuous mode is needed.

+      //

+      NeedPromiscuous = TRUE;

+    }

+  }

+

+  //

+  // Clear configuration for UdpRead and leave the original group joined before.

+  //

+  if (Mode->UsingIpv6) {

+    CopyMem(&Udp6Cfg, &Private->Udp6CfgData, sizeof (EFI_UDP6_CONFIG_DATA));

+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);

+    Udp6Cfg.AcceptPromiscuous  = FALSE;

+  } else {

+    CopyMem(&Udp4Cfg, &Private->Udp4CfgData, sizeof (EFI_UDP4_CONFIG_DATA));

+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);

+    Udp4Cfg.AcceptPromiscuous = FALSE;

+    Udp4Cfg.AcceptBroadcast   = FALSE;

+  }

+

+  if (NeedPromiscuous ||

+      (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0 ||

+      (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) {

+    //

+    // Configure UDPv4/UDPv6 as promiscuous mode to receive all packets.

+    //

+    Udp4Cfg.AcceptPromiscuous = TRUE;

+    Udp6Cfg.AcceptPromiscuous = TRUE;

+

+  } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) {

+    //

+    // Configure UDPv4 to receive all broadcast packets.

+    //

+    Udp4Cfg.AcceptBroadcast  = TRUE;

+  }

+

+  //

+  // Configure UDPv4/UDPv6 instance with the new configuration.

+  //

+  if (Mode->UsingIpv6) {

+    Status = Private->Udp6Read->Configure (Private->Udp6Read, &Udp6Cfg);

+  } else {

+    Status = Private->Udp4Read->Configure (Private->Udp4Read, &Udp4Cfg);

+  }

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) {

+

+    for (Index = 0; Index < NewFilter->IpCnt; Index++) {

+      //

+      // Join the multicast group if needed.

+      //

+      if (Mode->UsingIpv6) {

+        if (IP6_IS_MULTICAST (&NewFilter->IpList[Index].v6)) {

+          Status = Private->Udp6Read->Groups (

+                                        Private->Udp6Read,

+                                        TRUE,

+                                        &NewFilter->IpList[Index].v6

+                                        );

+          if (EFI_ERROR (Status)) {

+            goto ON_EXIT;

+          }

+        }

+      } else {

+        if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) {

+          Status = Private->Udp4Read->Groups (

+                                        Private->Udp4Read,

+                                        TRUE,

+                                        &NewFilter->IpList[Index].v4

+                                        );

+          if (EFI_ERROR (Status)) {

+            goto ON_EXIT;

+          }

+        }

+      }

+    }

+  }

+

+  //

+  // Save the new IP filter into mode data.

+  //

+  CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter));

+

+ON_EXIT:

+  return Status;

+}

+

+

+/**

+  Uses the ARP protocol to resolve a MAC address. It is not supported for IPv6.

+

+  This function uses the ARP protocol to resolve a MAC address. The IP address specified

+  by IpAddr is used to resolve a MAC address. If the ARP protocol succeeds in resolving

+  the specified address, then the ArpCacheEntries and ArpCache fields of the mode data

+  are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved

+  MAC address is placed there as well.  If the PXE Base Code protocol is in the

+  stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters

+  a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is

+  returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,

+  then EFI_ABORTED is returned.

+

+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]  IpAddr            Pointer to the IP address that is used to resolve a MAC address.

+  @param[in]  MacAddr           If not NULL, a pointer to the MAC address that was resolved with the

+                                ARP protocol.

+

+  @retval EFI_SUCCESS           The IP or MAC address was resolved.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.

+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.

+  @retval EFI_ICMP_ERROR        An error occur with the ICMP packet message.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcArp (

+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN EFI_IP_ADDRESS                   *IpAddr,

+  IN EFI_MAC_ADDRESS                  *MacAddr OPTIONAL

+  )

+{

+  PXEBC_PRIVATE_DATA      *Private;

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+  EFI_EVENT               ResolvedEvent;

+  EFI_STATUS              Status;

+  EFI_MAC_ADDRESS         TempMac;

+  EFI_MAC_ADDRESS         ZeroMac;

+  BOOLEAN                 IsResolved;

+

+  if (This == NULL || IpAddr == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private       = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode          = Private->PxeBc.Mode;

+  ResolvedEvent = NULL;

+  Status        = EFI_SUCCESS;

+  IsResolved    = FALSE;

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (Mode->UsingIpv6) {

+    return EFI_UNSUPPORTED;

+  }

+

+  //

+  // Station address should be ready before do arp.

+  //

+  if (!Private->IsAddressOk) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Mode->IcmpErrorReceived = FALSE;

+  ZeroMem (&TempMac, sizeof (EFI_MAC_ADDRESS));

+  ZeroMem (&ZeroMac, sizeof (EFI_MAC_ADDRESS));

+

+  if (!Mode->AutoArp) {

+    //

+    // If AutoArp is FALSE, only search in the current Arp cache.

+    //

+    PxeBcArpCacheUpdate (NULL, Private);

+    if (!PxeBcCheckArpCache (Mode, &IpAddr->v4, &TempMac)) {

+      Status = EFI_DEVICE_ERROR;

+      goto ON_EXIT;

+    }

+  } else {

+    Status = gBS->CreateEvent (

+                    EVT_NOTIFY_SIGNAL,

+                    TPL_NOTIFY,

+                    PxeBcCommonNotify,

+                    &IsResolved,

+                    &ResolvedEvent

+                    );

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+

+    //

+    // If AutoArp is TRUE, try to send Arp request on initiative.

+    //

+    Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, ResolvedEvent, &TempMac);

+    if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {

+      goto ON_EXIT;

+    }

+

+    while (!IsResolved) {

+      if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {

+        break;

+      }

+    }

+    if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {

+      Status = EFI_SUCCESS;

+    } else {

+      Status = EFI_TIMEOUT;

+    }

+  }

+

+  //

+  // Copy the Mac address to user if needed.

+  //

+  if (MacAddr != NULL && !EFI_ERROR (Status)) {

+    CopyMem (MacAddr, &TempMac, sizeof (EFI_MAC_ADDRESS));

+  }

+

+ON_EXIT:

+  if (ResolvedEvent != NULL) {

+    gBS->CloseEvent (ResolvedEvent);

+  }

+  return Status;

+}

+

+

+/**

+  Updates the parameters that affect the operation of the PXE Base Code Protocol.

+

+  This function sets parameters that affect the operation of the PXE Base Code Protocol.

+  The parameter specified by NewAutoArp is used to control the generation of ARP

+  protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated

+  as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP

+  Protocol packets will be generated. In this case, the only mappings that are

+  available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure.

+  If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol

+  service, then the service will fail. This function updates the AutoArp field of

+  the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp.

+  The SetParameters() call must be invoked after a Callback Protocol is installed

+  to enable the use of callbacks.

+

+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]  NewAutoArp        If not NULL, a pointer to a value that specifies whether to replace the

+                                current value of AutoARP.

+  @param[in]  NewSendGUID       If not NULL, a pointer to a value that specifies whether to replace the

+                                current value of SendGUID.

+  @param[in]  NewTTL            If not NULL, a pointer to be used in place of the current value of TTL,

+                                the "time to live" field of the IP header.

+  @param[in]  NewToS            If not NULL, a pointer to be used in place of the current value of ToS,

+                                the "type of service" field of the IP header.

+  @param[in]  NewMakeCallback   If not NULL, a pointer to a value that specifies whether to replace the

+                                current value of the MakeCallback field of the Mode structure.

+

+  @retval EFI_SUCCESS           The new parameters values were updated.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcSetParameters (

+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN BOOLEAN                          *NewAutoArp         OPTIONAL,

+  IN BOOLEAN                          *NewSendGUID        OPTIONAL,

+  IN UINT8                            *NewTTL             OPTIONAL,

+  IN UINT8                            *NewToS             OPTIONAL,

+  IN BOOLEAN                          *NewMakeCallback    OPTIONAL

+  )

+{

+  PXEBC_PRIVATE_DATA      *Private;

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+  EFI_GUID                SystemGuid;

+  EFI_STATUS              Status;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode    = Private->PxeBc.Mode;

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (NewMakeCallback != NULL) {

+    if (*NewMakeCallback) {

+      //

+      // Update the previous PxeBcCallback protocol.

+      //

+      Status = gBS->HandleProtocol (

+                      Private->Controller,

+                      &gEfiPxeBaseCodeCallbackProtocolGuid,

+                      (VOID **) &Private->PxeBcCallback

+                      );

+

+      if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) {

+        return EFI_INVALID_PARAMETER;

+      }

+    } else {

+      Private->PxeBcCallback = NULL;

+    }

+    Mode->MakeCallbacks = *NewMakeCallback;

+  }

+

+  if (NewSendGUID != NULL) {

+    if (*NewSendGUID && EFI_ERROR (PxeBcGetSystemGuid (&SystemGuid))) {

+      return EFI_INVALID_PARAMETER;

+    }

+    Mode->SendGUID = *NewSendGUID;

+  }

+

+  if (NewAutoArp != NULL) {

+    Mode->AutoArp = *NewAutoArp;

+  }

+

+  if (NewTTL != NULL) {

+    Mode->TTL = *NewTTL;

+  }

+

+  if (NewToS != NULL) {

+    Mode->ToS = *NewToS;

+  }

+

+  return EFI_SUCCESS;

+}

+

+

+/**

+  Updates the station IP address and/or subnet mask values of a network device.

+

+  This function updates the station IP address and/or subnet mask values of a network

+  device. The NewStationIp field is used to modify the network device's current IP address.

+  If NewStationIP is NULL, then the current IP address will not be modified. Otherwise,

+  this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure

+  with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet

+  mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified.

+  Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE

+  structure with NewSubnetMask.

+

+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]  NewStationIp      Pointer to the new IP address to be used by the network device.

+  @param[in]  NewSubnetMask     Pointer to the new subnet mask to be used by the network device.

+

+  @retval EFI_SUCCESS           The new station IP address and/or subnet mask were updated.

+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcSetStationIP (

+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN EFI_IP_ADDRESS                   *NewStationIp    OPTIONAL,

+  IN EFI_IP_ADDRESS                   *NewSubnetMask   OPTIONAL

+  )

+{

+  EFI_STATUS              Status;

+  PXEBC_PRIVATE_DATA      *Private;

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+  EFI_ARP_CONFIG_DATA     ArpConfigData;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (NewStationIp != NULL &&

+      (!NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0) &&

+       !NetIp6IsValidUnicast (&NewStationIp->v6))) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode    = Private->PxeBc.Mode;

+  Status  = EFI_SUCCESS;

+

+  if (!Mode->UsingIpv6 &&

+      NewSubnetMask != NULL &&

+      !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (Mode->UsingIpv6 && NewStationIp != NULL) {

+    //

+    // Set the IPv6 address by Ip6Config protocol.

+    //

+    Status = PxeBcRegisterIp6Address (Private, &NewStationIp->v6);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+  } else if (!Mode->UsingIpv6 && NewStationIp != NULL) {

+    //

+    // Configure the corresponding ARP with the IPv4 address.

+    //

+    ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA));

+

+    ArpConfigData.SwAddressType   = 0x0800;

+    ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS);

+    ArpConfigData.StationAddress  = &NewStationIp->v4;

+

+    Private->Arp->Configure (Private->Arp, NULL);

+    Private->Arp->Configure (Private->Arp, &ArpConfigData);

+

+    if (NewSubnetMask != NULL) {

+      Mode->RouteTableEntries                = 1;

+      Mode->RouteTable[0].IpAddr.Addr[0]     = NewStationIp->Addr[0] & NewSubnetMask->Addr[0];

+      Mode->RouteTable[0].SubnetMask.Addr[0] = NewSubnetMask->Addr[0];

+      Mode->RouteTable[0].GwAddr.Addr[0]     = 0;

+    }

+

+    Private->IsAddressOk = TRUE;

+  }

+

+  if (NewStationIp != NULL) {

+    CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));

+    CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));

+  }

+

+  if (!Mode->UsingIpv6 && NewSubnetMask != NULL) {

+    CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS));

+    CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS));

+  }

+

+  Status = PxeBcFlushStaionIp (Private, NewStationIp, NewSubnetMask);

+ON_EXIT:

+  return Status;

+}

+

+

+/**

+  Updates the contents of the cached DHCP and Discover packets.

+

+  The pointers to the new packets are used to update the contents of the cached

+  packets in the EFI_PXE_BASE_CODE_MODE structure.

+

+  @param[in]  This                   Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.

+  @param[in]  NewDhcpDiscoverValid   Pointer to a value that will replace the current

+                                     DhcpDiscoverValid field.

+  @param[in]  NewDhcpAckReceived     Pointer to a value that will replace the current

+                                     DhcpAckReceived field.

+  @param[in]  NewProxyOfferReceived  Pointer to a value that will replace the current

+                                     ProxyOfferReceived field.

+  @param[in]  NewPxeDiscoverValid    Pointer to a value that will replace the current

+                                     ProxyOfferReceived field.

+  @param[in]  NewPxeReplyReceived    Pointer to a value that will replace the current

+                                     PxeReplyReceived field.

+  @param[in]  NewPxeBisReplyReceived Pointer to a value that will replace the current

+                                     PxeBisReplyReceived field.

+  @param[in]  NewDhcpDiscover        Pointer to the new cached DHCP Discover packet contents.

+  @param[in]  NewDhcpAck             Pointer to the new cached DHCP Ack packet contents.

+  @param[in]  NewProxyOffer          Pointer to the new cached Proxy Offer packet contents.

+  @param[in]  NewPxeDiscover         Pointer to the new cached PXE Discover packet contents.

+  @param[in]  NewPxeReply            Pointer to the new cached PXE Reply packet contents.

+  @param[in]  NewPxeBisReply         Pointer to the new cached PXE BIS Reply packet contents.

+

+  @retval EFI_SUCCESS            The cached packet contents were updated.

+  @retval EFI_NOT_STARTED        The PXE Base Code Protocol is in the stopped state.

+  @retval EFI_INVALID_PARAMETER  This is NULL or does not point to a valid

+                                 EFI_PXE_BASE_CODE_PROTOCOL structure.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeBcSetPackets (

+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,

+  IN BOOLEAN                          *NewDhcpDiscoverValid      OPTIONAL,

+  IN BOOLEAN                          *NewDhcpAckReceived        OPTIONAL,

+  IN BOOLEAN                          *NewProxyOfferReceived     OPTIONAL,

+  IN BOOLEAN                          *NewPxeDiscoverValid       OPTIONAL,

+  IN BOOLEAN                          *NewPxeReplyReceived       OPTIONAL,

+  IN BOOLEAN                          *NewPxeBisReplyReceived    OPTIONAL,

+  IN EFI_PXE_BASE_CODE_PACKET         *NewDhcpDiscover           OPTIONAL,

+  IN EFI_PXE_BASE_CODE_PACKET         *NewDhcpAck                OPTIONAL,

+  IN EFI_PXE_BASE_CODE_PACKET         *NewProxyOffer             OPTIONAL,

+  IN EFI_PXE_BASE_CODE_PACKET         *NewPxeDiscover            OPTIONAL,

+  IN EFI_PXE_BASE_CODE_PACKET         *NewPxeReply               OPTIONAL,

+  IN EFI_PXE_BASE_CODE_PACKET         *NewPxeBisReply            OPTIONAL

+  )

+{

+  PXEBC_PRIVATE_DATA      *Private;

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+

+  if (This == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);

+  Mode    = Private->PxeBc.Mode;

+

+  if (!Mode->Started) {

+    return EFI_NOT_STARTED;

+  }

+

+  if (NewDhcpDiscoverValid != NULL) {

+    Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid;

+  }

+

+  if (NewDhcpAckReceived != NULL) {

+    Mode->DhcpAckReceived = *NewDhcpAckReceived;

+  }

+

+  if (NewProxyOfferReceived != NULL) {

+    Mode->ProxyOfferReceived = *NewProxyOfferReceived;

+  }

+

+  if (NewPxeDiscoverValid != NULL) {

+    Mode->PxeDiscoverValid = *NewPxeDiscoverValid;

+  }

+

+  if (NewPxeReplyReceived != NULL) {

+    Mode->PxeReplyReceived = *NewPxeReplyReceived;

+  }

+

+  if (NewPxeBisReplyReceived != NULL) {

+    Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived;

+  }

+

+  if (NewDhcpDiscover != NULL) {

+    CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));

+  }

+

+  if (NewDhcpAck != NULL) {

+    CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET));

+  }

+

+  if (NewProxyOffer != NULL) {

+    CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET));

+  }

+

+  if (NewPxeDiscover != NULL) {

+    CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));

+  }

+

+  if (NewPxeReply != NULL) {

+    CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET));

+  }

+

+  if (NewPxeBisReply != NULL) {

+    CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET));

+  }

+

+  return EFI_SUCCESS;

+}

+

+EFI_PXE_BASE_CODE_PROTOCOL  gPxeBcProtocolTemplate = {

+  EFI_PXE_BASE_CODE_PROTOCOL_REVISION,

+  EfiPxeBcStart,

+  EfiPxeBcStop,

+  EfiPxeBcDhcp,

+  EfiPxeBcDiscover,

+  EfiPxeBcMtftp,

+  EfiPxeBcUdpWrite,

+  EfiPxeBcUdpRead,

+  EfiPxeBcSetIpFilter,

+  EfiPxeBcArp,

+  EfiPxeBcSetParameters,

+  EfiPxeBcSetStationIP,

+  EfiPxeBcSetPackets,

+  NULL

+};

+

+

+/**

+  Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has

+  received, or is waiting to receive a packet.

+

+  This function is invoked when the PXE Base Code Protocol is about to transmit, has received,

+  or is waiting to receive a packet. Parameters Function and Received specify the type of event.

+  Parameters PacketLen and Packet specify the packet that generated the event. If these fields

+  are zero and NULL respectively, then this is a status update callback. If the operation specified

+  by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation

+  specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to

+  the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms.

+  The SetParameters() function must be called after a Callback Protocol is installed to enable the

+  use of callbacks.

+

+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance.

+  @param[in]  Function          The PXE Base Code Protocol function that is waiting for an event.

+  @param[in]  Received          TRUE if the callback is being invoked due to a receive event. FALSE if

+                                the callback is being invoked due to a transmit event.

+  @param[in]  PacketLength      The length, in bytes, of Packet. This field will have a value of zero if

+                                this is a wait for receive event.

+  @param[in]  PacketPtr         If Received is TRUE, a pointer to the packet that was just received;

+                                otherwise a pointer to the packet that is about to be transmitted.

+

+  @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE If Function specifies a continue operation.

+  @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT    If Function specifies an abort operation.

+

+**/

+EFI_PXE_BASE_CODE_CALLBACK_STATUS

+EFIAPI

+EfiPxeLoadFileCallback (

+  IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  *This,

+  IN EFI_PXE_BASE_CODE_FUNCTION           Function,

+  IN BOOLEAN                              Received,

+  IN UINT32                               PacketLength,

+  IN EFI_PXE_BASE_CODE_PACKET             *PacketPtr     OPTIONAL

+  )

+{

+  EFI_INPUT_KEY       Key;

+  EFI_STATUS          Status;

+

+  //

+  // Catch Ctrl-C or ESC to abort.

+  //

+  Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);

+

+  if (!EFI_ERROR (Status)) {

+

+    if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) {

+

+      return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;

+    }

+  }

+  //

+  // No print if receive packet

+  //

+  if (Received) {

+    return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;

+  }

+  //

+  // Print only for three functions

+  //

+  switch (Function) {

+

+  case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:

+    //

+    // Print only for open MTFTP packets, not every MTFTP packets

+    //

+    if (PacketLength != 0 && PacketPtr != NULL) {

+      if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {

+        return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;

+      }

+    }

+    break;

+

+  case EFI_PXE_BASE_CODE_FUNCTION_DHCP:

+  case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:

+    break;

+

+  default:

+    return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;

+  }

+

+  if (PacketLength != 0 && PacketPtr != NULL) {

+    //

+    // Print '.' when transmit a packet

+    //

+    AsciiPrint (".");

+  }

+

+  return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;

+}

+

+EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate = {

+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,

+  EfiPxeLoadFileCallback

+};

+

+

+/**

+  Causes the driver to load a specified file.

+

+  @param[in]      This                Protocol instance pointer.

+  @param[in]      FilePath            The device specific path of the file to load.

+  @param[in]      BootPolicy          If TRUE, indicates that the request originates from the

+                                      boot manager is attempting to load FilePath as a boot

+                                      selection. If FALSE, then FilePath must match an exact file

+                                      to be loaded.

+  @param[in, out] BufferSize          On input the size of Buffer in bytes. On output with a return

+                                      code of EFI_SUCCESS, the amount of data transferred to

+                                      Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,

+                                      the size of Buffer required to retrieve the requested file.

+  @param[in]      Buffer              The memory buffer to transfer the file to. IF Buffer is NULL,

+                                      then no the size of the requested file is returned in

+                                      BufferSize.

+

+  @retval EFI_SUCCESS                 The file was loaded.

+  @retval EFI_UNSUPPORTED             The device does not support the provided BootPolicy.

+  @retval EFI_INVALID_PARAMETER       FilePath is not a valid device path, or

+                                      BufferSize is NULL.

+  @retval EFI_NO_MEDIA                No medium was present to load the file.

+  @retval EFI_DEVICE_ERROR            The file was not loaded due to a device error.

+  @retval EFI_NO_RESPONSE             The remote system did not respond.

+  @retval EFI_NOT_FOUND               The file was not found.

+  @retval EFI_ABORTED                 The file load process was manually cancelled.

+

+**/

+EFI_STATUS

+EFIAPI

+EfiPxeLoadFile (

+  IN     EFI_LOAD_FILE_PROTOCOL           *This,

+  IN     EFI_DEVICE_PATH_PROTOCOL         *FilePath,

+  IN     BOOLEAN                          BootPolicy,

+  IN OUT UINTN                            *BufferSize,

+  IN     VOID                             *Buffer       OPTIONAL

+  )

+{

+  PXEBC_PRIVATE_DATA          *Private;

+  PXEBC_VIRTUAL_NIC           *VirtualNic;

+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;

+  BOOLEAN                     UsingIpv6;

+  EFI_STATUS                  Status;

+  BOOLEAN                     MediaPresent;

+

+  VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (This);

+  Private    = VirtualNic->Private;

+  PxeBc      = &Private->PxeBc;

+  UsingIpv6  = FALSE;

+  Status     = EFI_DEVICE_ERROR;

+

+  if (This == NULL || BufferSize == NULL) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  //

+  // Only support BootPolicy

+  //

+  if (!BootPolicy) {

+    return EFI_UNSUPPORTED;

+  }

+

+  //

+  // Check media status before PXE start

+  //

+  MediaPresent = TRUE;

+  NetLibDetectMedia (Private->Controller, &MediaPresent);

+  if (!MediaPresent) {

+    return EFI_NO_MEDIA;

+  }

+

+  //

+  // Check whether the virtual nic is using IPv6 or not.

+  //

+  if (VirtualNic == Private->Ip6Nic) {

+    UsingIpv6 = TRUE;

+  }

+

+  //

+  // Start Pxe Base Code to initialize PXE boot.

+  //

+  Status = PxeBc->Start (PxeBc, UsingIpv6);

+  if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {

+    Status = PxeBcLoadBootFile (Private, BufferSize, Buffer);

+  }

+

+  if (Status != EFI_SUCCESS &&

+      Status != EFI_UNSUPPORTED &&

+      Status != EFI_BUFFER_TOO_SMALL) {

+    //

+    // There are three cases, which needn't stop pxebc here.

+    //   1. success to download file.

+    //   2. success to get file size.

+    //   3. unsupported.

+    //

+    PxeBc->Stop (PxeBc);

+  }

+

+  return Status;

+}

+

+EFI_LOAD_FILE_PROTOCOL  gLoadFileProtocolTemplate = { EfiPxeLoadFile };

+

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h
new file mode 100644
index 0000000..7491cf8
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h
@@ -0,0 +1,207 @@
+/** @file

+  This EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.

+  interfaces declaration.

+

+  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_PXEBC_IMPL_H__

+#define __EFI_PXEBC_IMPL_H__

+

+#include <Uefi.h>

+

+#include <Guid/SmBios.h>

+#include <IndustryStandard/SmBios.h>

+#include <Protocol/NetworkInterfaceIdentifier.h>

+#include <Protocol/Arp.h>

+#include <Protocol/Ip4.h>

+#include <Protocol/Ip6.h>

+#include <Protocol/Ip6Config.h>

+#include <Protocol/Udp4.h>

+#include <Protocol/Udp6.h>

+#include <Protocol/Dhcp4.h>

+#include <Protocol/Dhcp6.h>

+#include <Protocol/Mtftp4.h>

+#include <Protocol/Mtftp6.h>

+#include <Protocol/PxeBaseCode.h>

+#include <Protocol/LoadFile.h>

+#include <Protocol/PxeBaseCodeCallBack.h>

+#include <Protocol/ServiceBinding.h>

+#include <Protocol/DriverBinding.h>

+

+#include <Library/DebugLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/UefiDriverEntryPoint.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/BaseLib.h>

+#include <Library/NetLib.h>

+#include <Library/DpcLib.h>

+#include <Library/DevicePathLib.h>

+#include <Library/PcdLib.h>

+

+typedef struct _PXEBC_PRIVATE_DATA  PXEBC_PRIVATE_DATA;

+typedef struct _PXEBC_VIRTUAL_NIC   PXEBC_VIRTUAL_NIC;

+

+#include "PxeBcDriver.h"

+#include "PxeBcDhcp4.h"

+#include "PxeBcDhcp6.h"

+#include "PxeBcMtftp.h"

+#include "PxeBcBoot.h"

+#include "PxeBcSupport.h"

+

+#define PXEBC_DEFAULT_HOPLIMIT        64

+#define PXEBC_DEFAULT_LIFETIME        50000    // 50 ms, unit is microsecond

+#define PXEBC_UDP_TIMEOUT             30000000 // 3 seconds, unit is 100nanosecond

+#define PXEBC_MTFTP_TIMEOUT           4

+#define PXEBC_MTFTP_RETRIES           6

+#define PXEBC_DHCP_RETRIES            4        // refers to mPxeDhcpTimeout, also by PXE2.1 spec.

+#define PXEBC_MENU_MAX_NUM            24

+#define PXEBC_OFFER_MAX_NUM           16

+

+#define PXEBC_PRIVATE_DATA_SIGNATURE          SIGNATURE_32 ('P', 'X', 'E', 'P')

+#define PXEBC_VIRTUAL_NIC_SIGNATURE           SIGNATURE_32 ('P', 'X', 'E', 'V')

+#define PXEBC_PRIVATE_DATA_FROM_PXEBC(a)      CR (a, PXEBC_PRIVATE_DATA, PxeBc, PXEBC_PRIVATE_DATA_SIGNATURE)

+#define PXEBC_VIRTUAL_NIC_FROM_LOADFILE(a)    CR (a, PXEBC_VIRTUAL_NIC, LoadFile, PXEBC_VIRTUAL_NIC_SIGNATURE)

+

+typedef union {

+  PXEBC_DHCP4_PACKET_CACHE            Dhcp4;

+  PXEBC_DHCP6_PACKET_CACHE            Dhcp6;

+} PXEBC_DHCP_PACKET_CACHE;

+

+struct _PXEBC_VIRTUAL_NIC {

+  UINT32                                    Signature;

+  EFI_HANDLE                                Controller;

+  EFI_LOAD_FILE_PROTOCOL                    LoadFile;

+  EFI_DEVICE_PATH_PROTOCOL                  *DevicePath;

+  PXEBC_PRIVATE_DATA                        *Private;

+};

+

+struct _PXEBC_PRIVATE_DATA {

+  UINT32                                    Signature;

+  EFI_HANDLE                                Controller;

+  EFI_HANDLE                                Image;

+

+  PXEBC_VIRTUAL_NIC                         *Ip4Nic;

+  PXEBC_VIRTUAL_NIC                         *Ip6Nic;

+

+  EFI_HANDLE                                ArpChild;

+  EFI_HANDLE                                Ip4Child;

+  EFI_HANDLE                                Dhcp4Child;

+  EFI_HANDLE                                Mtftp4Child;

+  EFI_HANDLE                                Udp4ReadChild;

+  EFI_HANDLE                                Udp4WriteChild;

+

+  EFI_ARP_PROTOCOL                          *Arp;

+  EFI_IP4_PROTOCOL                          *Ip4;

+  EFI_DHCP4_PROTOCOL                        *Dhcp4;

+  EFI_MTFTP4_PROTOCOL                       *Mtftp4;

+  EFI_UDP4_PROTOCOL                         *Udp4Read;

+  EFI_UDP4_PROTOCOL                         *Udp4Write;

+

+  EFI_HANDLE                                Ip6Child;

+  EFI_HANDLE                                Dhcp6Child;

+  EFI_HANDLE                                Mtftp6Child;

+  EFI_HANDLE                                Udp6ReadChild;

+  EFI_HANDLE                                Udp6WriteChild;

+

+  EFI_IP6_PROTOCOL                          *Ip6;

+  EFI_IP6_CONFIG_PROTOCOL                   *Ip6Cfg;

+  EFI_DHCP6_PROTOCOL                        *Dhcp6;

+  EFI_MTFTP6_PROTOCOL                       *Mtftp6;

+  EFI_UDP6_PROTOCOL                         *Udp6Read;

+  EFI_UDP6_PROTOCOL                         *Udp6Write;

+

+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;

+  EFI_PXE_BASE_CODE_PROTOCOL                PxeBc;

+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL       LoadFileCallback;

+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL       *PxeBcCallback;

+  EFI_DEVICE_PATH_PROTOCOL                  *DevicePath;

+

+  EFI_PXE_BASE_CODE_MODE                    Mode;

+  EFI_PXE_BASE_CODE_FUNCTION                Function;

+  UINT32                                    Ip6Policy;

+

+  EFI_UDP4_CONFIG_DATA                      Udp4CfgData;

+  EFI_UDP6_CONFIG_DATA                      Udp6CfgData;

+  EFI_IP4_CONFIG_DATA                       Ip4CfgData;

+  EFI_IP6_CONFIG_DATA                       Ip6CfgData;

+

+  EFI_EVENT                                 UdpTimeOutEvent;

+  EFI_EVENT                                 ArpUpdateEvent;

+  EFI_IP4_COMPLETION_TOKEN                  IcmpToken;

+  EFI_IP6_COMPLETION_TOKEN                  Icmp6Token;

+

+  BOOLEAN                                   IsAddressOk;

+  BOOLEAN                                   IsOfferSorted;

+  BOOLEAN                                   IsProxyRecved;

+  BOOLEAN                                   IsDoDiscover;

+

+  EFI_IP_ADDRESS                            StationIp;

+  EFI_IP_ADDRESS                            SubnetMask;

+  EFI_IP_ADDRESS                            GatewayIp;

+  EFI_IP_ADDRESS                            ServerIp;

+  UINT16                                    CurSrcPort;

+

+  UINT32                                    Ip4MaxPacketSize;

+  UINT32                                    Ip6MaxPacketSize;

+  UINT8                                     *BootFileName;

+  UINTN                                     BootFileSize;

+  UINTN                                     BlockSize;

+

+  PXEBC_DHCP_PACKET_CACHE                   ProxyOffer;

+  PXEBC_DHCP_PACKET_CACHE                   DhcpAck;

+  PXEBC_DHCP_PACKET_CACHE                   PxeReply;

+  EFI_DHCP6_PACKET                          *Dhcp6Request;

+  EFI_DHCP4_PACKET                          SeedPacket;

+

+  //

+  // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer.

+  //

+  // It supposed that

+  //

+  //   OfferNum:    8

+  //   OfferBuffer: [ProxyBinl, ProxyBinl, DhcpOnly, ProxyPxe10, DhcpOnly, DhcpPxe10, DhcpBinl, ProxyBinl]

+  //   (OfferBuffer is 0-based.)

+  //

+  // And assume that (DhcpPxe10 is the first priority actually.)

+  //

+  //   SelectIndex:     2

+  //   SelectProxyType: PXEBC_OFFER_TYPE_PROXY_BINL

+  //   (SelectIndex is 1-based, and 0 means no one is selected.)

+  //

+  // So it should be

+  //

+  //                 DhcpOnly  DhcpPxe10  DhcpWfm11a  DhcpBinl  ProxyPxe10  ProxyWfm11a  ProxyBinl  Bootp

+  //   OfferCount:  [    2(n),      1(n),       0(n),     1(n),       1(1),        0(1),      3(n),  1(1)]

+  //

+  //   OfferIndex: {[       2,         5,          0,        6,          3,           0,        *0,     0]

+  //                [       4,         0,          0,        0,          0,           0,         1,     0]

+  //                [       0,         0,          0,        0,          0,           0,         7,     0]

+  //                ...                                                                                  ]}

+  //   (OfferIndex is 0-based.)

+  //

+  //

+  UINT32                                    SelectIndex;

+  UINT32                                    SelectProxyType;

+  PXEBC_DHCP_PACKET_CACHE                   OfferBuffer[PXEBC_OFFER_MAX_NUM];

+  UINT32                                    OfferNum;

+  UINT32                                    OfferCount[PxeOfferTypeMax];

+  UINT32                                    OfferIndex[PxeOfferTypeMax][PXEBC_OFFER_MAX_NUM];

+};

+

+extern EFI_PXE_BASE_CODE_PROTOCOL           gPxeBcProtocolTemplate;

+extern EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  gPxeBcCallBackTemplate;

+extern EFI_LOAD_FILE_PROTOCOL               gLoadFileProtocolTemplate;

+

+#endif

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c
new file mode 100644
index 0000000..8adba66
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c
@@ -0,0 +1,1105 @@
+/** @file

+  Functions implementation related with Mtftp for UefiPxeBc Driver.

+

+  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "PxeBcImpl.h"

+

+CHAR8 *mMtftpOptions[PXE_MTFTP_OPTION_MAXIMUM_INDEX] = {

+  "blksize",

+  "timeout",

+  "tsize",

+  "multicast"

+};

+

+

+/**

+  This is a callback function when packets are received or transmitted in Mtftp driver.

+

+  A callback function that is provided by the caller to intercept

+  the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP6_OPCODE_DATA8 packets processed in the

+  EFI_MTFTP6_PROTOCOL.ReadFile() function, and alternatively to intercept

+  EFI_MTFTP6_OPCODE_OACK or EFI_MTFTP6_OPCODE_ERROR packets during a call to

+  EFI_MTFTP6_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().

+

+  @param[in]  This           Pointer to EFI_MTFTP6_PROTOCOL.

+  @param[in]  Token          Pointer to EFI_MTFTP6_TOKEN.

+  @param[in]  PacketLen      Length of EFI_MTFTP6_PACKET.

+  @param[in]  Packet         Pointer to EFI_MTFTP6_PACKET to be checked.

+

+  @retval EFI_SUCCESS    The current operation succeeded.

+  @retval EFI_ABORTED    Abort the current transfer process.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcMtftp6CheckPacket (

+  IN EFI_MTFTP6_PROTOCOL              *This,

+  IN EFI_MTFTP6_TOKEN                 *Token,

+  IN UINT16                           PacketLen,

+  IN EFI_MTFTP6_PACKET                *Packet

+  )

+{

+  PXEBC_PRIVATE_DATA                  *Private;

+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;

+  EFI_STATUS                          Status;

+

+  Private   = (PXEBC_PRIVATE_DATA *) Token->Context;

+  Callback  = Private->PxeBcCallback;

+  Status    = EFI_SUCCESS;

+

+  if (Packet->OpCode == EFI_MTFTP6_OPCODE_ERROR) {

+    //

+    // Store the tftp error message into mode data and set the received flag.

+    //

+    Private->Mode.TftpErrorReceived   = TRUE;

+    Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;

+    AsciiStrnCpy (

+      Private->Mode.TftpError.ErrorString,

+      (CHAR8 *) Packet->Error.ErrorMessage,

+      PXE_MTFTP_ERROR_STRING_LENGTH

+      );

+  }

+

+  if (Callback != NULL) {

+    //

+    // Callback to user if has when received any tftp packet.

+    //

+    Status = Callback->Callback (

+                        Callback,

+                        Private->Function,

+                        TRUE,

+                        PacketLen,

+                        (EFI_PXE_BASE_CODE_PACKET *) Packet

+                        );

+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {

+      //

+      // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.

+      //

+      Status = EFI_ABORTED;

+    } else {

+      //

+      // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.

+      //

+      Status = EFI_SUCCESS;

+    }

+  }

+

+  return Status;

+}

+

+

+/**

+  This function is to get the size of a file using Tftp.

+

+  @param[in]      Private        Pointer to PxeBc private data.

+  @param[in]      Config         Pointer to EFI_MTFTP6_CONFIG_DATA.

+  @param[in]      Filename       Pointer to boot file name.

+  @param[in]      BlockSize      Pointer to required block size.

+  @param[in, out] BufferSize     Pointer to buffer size.

+

+  @retval EFI_SUCCESS        Sucessfully obtained the size of file.

+  @retval EFI_NOT_FOUND      Parse the tftp ptions failed.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Has not obtained the size of the file.

+

+**/

+EFI_STATUS

+PxeBcMtftp6GetFileSize (

+  IN     PXEBC_PRIVATE_DATA           *Private,

+  IN     EFI_MTFTP6_CONFIG_DATA       *Config,

+  IN     UINT8                        *Filename,

+  IN     UINTN                        *BlockSize,

+  IN OUT UINT64                       *BufferSize

+  )

+{

+  EFI_MTFTP6_PROTOCOL                 *Mtftp6;

+  EFI_MTFTP6_OPTION                   ReqOpt[2];

+  EFI_MTFTP6_PACKET                   *Packet;

+  EFI_MTFTP6_OPTION                   *Option;

+  UINT32                              PktLen;

+  UINT8                               OptBuf[128];

+  UINT32                              OptCnt;

+  EFI_STATUS                          Status;

+

+  *BufferSize               = 0;

+  Status                    = EFI_DEVICE_ERROR;

+  Mtftp6                    = Private->Mtftp6;

+  Packet                    = NULL;

+  Option                    = NULL;

+  PktLen                    = 0;

+  OptCnt                    = 1;

+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;

+

+  Status = Mtftp6->Configure (Mtftp6, Config);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Build the required options for get info.

+  //

+  ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];

+  PxeBcUintnToAscDec (0, OptBuf);

+  ReqOpt[0].ValueStr  = OptBuf;

+

+  if (BlockSize != NULL) {

+    ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];

+    ReqOpt[1].ValueStr  = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);

+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr);

+    OptCnt++;

+  }

+

+  Status = Mtftp6->GetInfo (

+                     Mtftp6,

+                     FALSE,

+                     Filename,

+                     NULL,

+                     (UINT8) OptCnt,

+                     ReqOpt,

+                     &PktLen,

+                     &Packet

+                     );

+  if (EFI_ERROR (Status)) {

+    if (Status == EFI_TFTP_ERROR) {

+      //

+      // Store the tftp error message into mode data and set the received flag.

+      //

+      Private->Mode.TftpErrorReceived   = TRUE;

+      Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;

+      AsciiStrnCpy (

+        Private->Mode.TftpError.ErrorString,

+        (CHAR8 *) Packet->Error.ErrorMessage,

+        PXE_MTFTP_ERROR_STRING_LENGTH

+        );

+    }

+    goto ON_ERROR;

+  }

+

+  //

+  // Parse the options in the reply packet.

+  //

+  OptCnt = 0;

+  Status = Mtftp6->ParseOptions (

+                     Mtftp6,

+                     PktLen,

+                     Packet,

+                     (UINT32 *) &OptCnt,

+                     &Option

+                     );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Parse out the value of "tsize" option.

+  //

+  Status = EFI_NOT_FOUND;

+  while (OptCnt != 0) {

+    if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {

+      *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));

+      Status      = EFI_SUCCESS;

+    }

+    OptCnt--;

+  }

+  FreePool (Option);

+

+ON_ERROR:

+  if (Packet != NULL) {

+    FreePool (Packet);

+  }

+  Mtftp6->Configure (Mtftp6, NULL);

+

+  return Status;

+}

+

+

+/**

+  This function is to get data of a file using Tftp.

+

+  @param[in]      Private        Pointer to PxeBc private data.

+  @param[in]      Config         Pointer to EFI_MTFTP6_CONFIG_DATA.

+  @param[in]      Filename       Pointer to boot file name.

+  @param[in]      BlockSize      Pointer to required block size.

+  @param[in]      BufferPtr      Pointer to buffer.

+  @param[in, out] BufferSize     Pointer to buffer size.

+  @param[in]      DontUseBuffer  Indicates whether with a receive buffer.

+

+  @retval EFI_SUCCESS        Successfully read the data from the special file.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Read data from file failed.

+

+**/

+EFI_STATUS

+PxeBcMtftp6ReadFile (

+  IN    PXEBC_PRIVATE_DATA            *Private,

+  IN     EFI_MTFTP6_CONFIG_DATA       *Config,

+  IN     UINT8                        *Filename,

+  IN     UINTN                        *BlockSize,

+  IN     UINT8                        *BufferPtr,

+  IN OUT UINT64                       *BufferSize,

+  IN     BOOLEAN                      DontUseBuffer

+  )

+{

+  EFI_MTFTP6_PROTOCOL                 *Mtftp6;

+  EFI_MTFTP6_TOKEN                    Token;

+  EFI_MTFTP6_OPTION                   ReqOpt[1];

+  UINT32                              OptCnt;

+  UINT8                               OptBuf[128];

+  EFI_STATUS                          Status;

+

+  Status                    = EFI_DEVICE_ERROR;

+  Mtftp6                    = Private->Mtftp6;

+  OptCnt                    = 0;

+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;

+

+  Status = Mtftp6->Configure (Mtftp6, Config);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (BlockSize != NULL) {

+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];

+    ReqOpt[0].ValueStr  = OptBuf;

+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);

+    OptCnt++;

+  }

+

+  Token.Event         = NULL;

+  Token.OverrideData  = NULL;

+  Token.Filename      = Filename;

+  Token.ModeStr       = NULL;

+  Token.OptionCount   = OptCnt;

+  Token.OptionList    = ReqOpt;

+  Token.Context       = Private;

+

+  if (DontUseBuffer) {

+    Token.BufferSize  = 0;

+    Token.Buffer      = NULL;

+  } else {

+    Token.BufferSize  = *BufferSize;

+    Token.Buffer      = BufferPtr;

+  }

+

+  Token.CheckPacket     = PxeBcMtftp6CheckPacket;

+  Token.TimeoutCallback = NULL;

+  Token.PacketNeeded    = NULL;

+

+  Status = Mtftp6->ReadFile (Mtftp6, &Token);

+  //

+  // Get the real size of received buffer.

+  //

+  *BufferSize = Token.BufferSize;

+

+  Mtftp6->Configure (Mtftp6, NULL);

+

+  return Status;

+}

+

+

+/**

+  This function is used to write the data of a file using Tftp.

+

+  @param[in]       Private        Pointer to PxeBc private data.

+  @param[in]       Config         Pointer to EFI_MTFTP6_CONFIG_DATA.

+  @param[in]       Filename       Pointer to boot file name.

+  @param[in]       Overwrite      Indicate whether with overwrite attribute.

+  @param[in]       BlockSize      Pointer to required block size.

+  @param[in]       BufferPtr      Pointer to buffer.

+  @param[in, out]  BufferSize     Pointer to buffer size.

+

+  @retval EFI_SUCCESS        Successfully wrote the data into a special file.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval other              Write data into file failed.

+

+**/

+EFI_STATUS

+PxeBcMtftp6WriteFile (

+  IN     PXEBC_PRIVATE_DATA           *Private,

+  IN     EFI_MTFTP6_CONFIG_DATA       *Config,

+  IN     UINT8                        *Filename,

+  IN     BOOLEAN                      Overwrite,

+  IN     UINTN                        *BlockSize,

+  IN     UINT8                        *BufferPtr,

+  IN OUT UINT64                       *BufferSize

+  )

+{

+  EFI_MTFTP6_PROTOCOL                 *Mtftp6;

+  EFI_MTFTP6_TOKEN                    Token;

+  EFI_MTFTP6_OPTION                   ReqOpt[1];

+  UINT32                              OptCnt;

+  UINT8                               OptBuf[128];

+  EFI_STATUS                          Status;

+

+  Status                    = EFI_DEVICE_ERROR;

+  Mtftp6                    = Private->Mtftp6;

+  OptCnt                    = 0;

+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;

+

+  Status = Mtftp6->Configure (Mtftp6, Config);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (BlockSize != NULL) {

+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];

+    ReqOpt[0].ValueStr  = OptBuf;

+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);

+    OptCnt++;

+  }

+

+  Token.Event           = NULL;

+  Token.OverrideData    = NULL;

+  Token.Filename        = Filename;

+  Token.ModeStr         = NULL;

+  Token.OptionCount     = OptCnt;

+  Token.OptionList      = ReqOpt;

+  Token.BufferSize      = *BufferSize;

+  Token.Buffer          = BufferPtr;

+  Token.CheckPacket     = PxeBcMtftp6CheckPacket;

+  Token.TimeoutCallback = NULL;

+  Token.PacketNeeded    = NULL;

+

+  Status = Mtftp6->WriteFile (Mtftp6, &Token);

+  //

+  // Get the real size of transmitted buffer.

+  //

+  *BufferSize = Token.BufferSize;

+

+  Mtftp6->Configure (Mtftp6, NULL);

+

+  return Status;

+}

+

+

+/**

+  This function is to read the data (file) from a directory using Tftp.

+

+  @param[in]       Private        Pointer to PxeBc private data.

+  @param[in]       Config         Pointer to EFI_MTFTP6_CONFIG_DATA.

+  @param[in]       Filename       Pointer to boot file name.

+  @param[in]       BlockSize      Pointer to required block size.

+  @param[in]       BufferPtr      Pointer to buffer.

+  @param[in, out]  BufferSize     Pointer to buffer size.

+  @param[in]       DontUseBuffer  Indicates whether to use a receive buffer.

+

+  @retval EFI_SUCCESS        Successfully obtained the data from the file included in directory.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Operation failed.

+

+**/

+EFI_STATUS

+PxeBcMtftp6ReadDirectory (

+  IN     PXEBC_PRIVATE_DATA            *Private,

+  IN     EFI_MTFTP6_CONFIG_DATA        *Config,

+  IN     UINT8                         *Filename,

+  IN     UINTN                         *BlockSize,

+  IN     UINT8                         *BufferPtr,

+  IN OUT UINT64                        *BufferSize,

+  IN     BOOLEAN                       DontUseBuffer

+  )

+{

+  EFI_MTFTP6_PROTOCOL                  *Mtftp6;

+  EFI_MTFTP6_TOKEN                     Token;

+  EFI_MTFTP6_OPTION                    ReqOpt[1];

+  UINT32                               OptCnt;

+  UINT8                                OptBuf[128];

+  EFI_STATUS                           Status;

+

+  Status                    = EFI_DEVICE_ERROR;

+  Mtftp6                    = Private->Mtftp6;

+  OptCnt                    = 0;

+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;

+

+  Status = Mtftp6->Configure (Mtftp6, Config);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (BlockSize != NULL) {

+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];

+    ReqOpt[0].ValueStr  = OptBuf;

+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);

+    OptCnt++;

+  }

+

+  Token.Event         = NULL;

+  Token.OverrideData  = NULL;

+  Token.Filename      = Filename;

+  Token.ModeStr       = NULL;

+  Token.OptionCount   = OptCnt;

+  Token.OptionList    = ReqOpt;

+  Token.Context       = Private;

+

+  if (DontUseBuffer) {

+    Token.BufferSize  = 0;

+    Token.Buffer      = NULL;

+  } else {

+    Token.BufferSize  = *BufferSize;

+    Token.Buffer      = BufferPtr;

+  }

+

+  Token.CheckPacket     = PxeBcMtftp6CheckPacket;

+  Token.TimeoutCallback = NULL;

+  Token.PacketNeeded    = NULL;

+

+  Status = Mtftp6->ReadDirectory (Mtftp6, &Token);

+  //

+  // Get the real size of received buffer.

+  //

+  *BufferSize = Token.BufferSize;

+

+  Mtftp6->Configure (Mtftp6, NULL);

+

+  return Status;

+}

+

+

+/**

+  This is a callback function when packets are received or transmitted in Mtftp driver.

+

+  A callback function that is provided by the caller to intercept

+  the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the

+  EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept

+  EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to

+  EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().

+

+  @param[in]  This           Pointer to EFI_MTFTP4_PROTOCOL.

+  @param[in]  Token          Pointer to EFI_MTFTP4_TOKEN.

+  @param[in]  PacketLen      Length of EFI_MTFTP4_PACKET.

+  @param[in]  Packet         Pointer to EFI_MTFTP4_PACKET to be checked.

+

+  @retval EFI_SUCCESS    The current operation succeeeded.

+  @retval EFI_ABORTED    Abort the current transfer process.

+

+**/

+EFI_STATUS

+EFIAPI

+PxeBcMtftp4CheckPacket (

+  IN EFI_MTFTP4_PROTOCOL        *This,

+  IN EFI_MTFTP4_TOKEN           *Token,

+  IN UINT16                     PacketLen,

+  IN EFI_MTFTP4_PACKET          *Packet

+  )

+{

+  PXEBC_PRIVATE_DATA                  *Private;

+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;

+  EFI_STATUS                          Status;

+

+  Private   = (PXEBC_PRIVATE_DATA *) Token->Context;

+  Callback  = Private->PxeBcCallback;

+  Status    = EFI_SUCCESS;

+

+  if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) {

+    //

+    // Store the tftp error message into mode data and set the received flag.

+    //

+    Private->Mode.TftpErrorReceived   = TRUE;

+    Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;

+    AsciiStrnCpy (

+      Private->Mode.TftpError.ErrorString,

+      (CHAR8 *) Packet->Error.ErrorMessage,

+      PXE_MTFTP_ERROR_STRING_LENGTH

+      );

+  }

+

+  if (Callback != NULL) {

+    //

+    // Callback to user if has when received any tftp packet.

+    //

+    Status = Callback->Callback (

+                        Callback,

+                        Private->Function,

+                        TRUE,

+                        PacketLen,

+                        (EFI_PXE_BASE_CODE_PACKET *) Packet

+                        );

+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {

+      //

+      // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.

+      //

+      Status = EFI_ABORTED;

+    } else {

+      //

+      // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.

+      //

+      Status = EFI_SUCCESS;

+    }

+  }

+

+  return Status;

+}

+

+

+/**

+  This function is to get size of a file using Tftp.

+

+  @param[in]      Private        Pointer to PxeBc private data.

+  @param[in]      Config         Pointer to EFI_MTFTP4_CONFIG_DATA.

+  @param[in]      Filename       Pointer to boot file name.

+  @param[in]      BlockSize      Pointer to required block size.

+  @param[in, out] BufferSize     Pointer to buffer size.

+

+  @retval EFI_SUCCESS        Successfully obtained the size of file.

+  @retval EFI_NOT_FOUND      Parse the tftp options failed.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Did not obtain the size of the file.

+

+**/

+EFI_STATUS

+PxeBcMtftp4GetFileSize (

+  IN     PXEBC_PRIVATE_DATA         *Private,

+  IN     EFI_MTFTP4_CONFIG_DATA     *Config,

+  IN     UINT8                      *Filename,

+  IN     UINTN                      *BlockSize,

+  IN OUT UINT64                     *BufferSize

+  )

+{

+  EFI_MTFTP4_PROTOCOL *Mtftp4;

+  EFI_MTFTP4_OPTION   ReqOpt[2];

+  EFI_MTFTP4_PACKET   *Packet;

+  EFI_MTFTP4_OPTION   *Option;

+  UINT32              PktLen;

+  UINT8               OptBuf[128];

+  UINT32              OptCnt;

+  EFI_STATUS          Status;

+

+  *BufferSize               = 0;

+  Status                    = EFI_DEVICE_ERROR;

+  Mtftp4                    = Private->Mtftp4;

+  Packet                    = NULL;

+  Option                    = NULL;

+  PktLen                    = 0;

+  OptCnt                    = 1;

+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;

+

+  Status = Mtftp4->Configure (Mtftp4, Config);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Build the required options for get info.

+  //

+  ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];

+  PxeBcUintnToAscDec (0, OptBuf);

+  ReqOpt[0].ValueStr  = OptBuf;

+

+  if (BlockSize != NULL) {

+    ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];

+    ReqOpt[1].ValueStr  = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);

+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr);

+    OptCnt++;

+  }

+

+  Status = Mtftp4->GetInfo (

+                     Mtftp4,

+                     FALSE,

+                     Filename,

+                     NULL,

+                     (UINT8) OptCnt,

+                     ReqOpt,

+                     &PktLen,

+                     &Packet

+                     );

+  if (EFI_ERROR (Status)) {

+    if (Status == EFI_TFTP_ERROR) {

+      //

+      // Store the tftp error message into mode data and set the received flag.

+      //

+      Private->Mode.TftpErrorReceived   = TRUE;

+      Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;

+      AsciiStrnCpy (

+        Private->Mode.TftpError.ErrorString,

+        (CHAR8 *) Packet->Error.ErrorMessage,

+        PXE_MTFTP_ERROR_STRING_LENGTH

+        );

+    }

+    goto ON_ERROR;

+  }

+

+  //

+  // Parse the options in the reply packet.

+  //

+  OptCnt = 0;

+  Status = Mtftp4->ParseOptions (

+                     Mtftp4,

+                     PktLen,

+                     Packet,

+                     (UINT32 *) &OptCnt,

+                     &Option

+                     );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  //

+  // Parse out the value of "tsize" option.

+  //

+  Status = EFI_NOT_FOUND;

+  while (OptCnt != 0) {

+    if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {

+      *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));

+      Status      = EFI_SUCCESS;

+    }

+    OptCnt--;

+  }

+  FreePool (Option);

+

+ON_ERROR:

+  if (Packet != NULL) {

+    FreePool (Packet);

+  }

+  Mtftp4->Configure (Mtftp4, NULL);

+

+  return Status;

+}

+

+

+/**

+  This function is to read the data of a file using Tftp.

+

+  @param[in]      Private        Pointer to PxeBc private data.

+  @param[in]      Config         Pointer to EFI_MTFTP4_CONFIG_DATA.

+  @param[in]      Filename       Pointer to boot file name.

+  @param[in]      BlockSize      Pointer to required block size.

+  @param[in]      BufferPtr      Pointer to buffer.

+  @param[in, out] BufferSize     Pointer to buffer size.

+  @param[in]      DontUseBuffer  Indicates whether to use a receive buffer.

+

+  @retval EFI_SUCCESS        Successfully read the data from the special file.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Read data from file failed.

+

+**/

+EFI_STATUS

+PxeBcMtftp4ReadFile (

+  IN     PXEBC_PRIVATE_DATA         *Private,

+  IN     EFI_MTFTP4_CONFIG_DATA     *Config,

+  IN     UINT8                      *Filename,

+  IN     UINTN                      *BlockSize,

+  IN     UINT8                      *BufferPtr,

+  IN OUT UINT64                     *BufferSize,

+  IN     BOOLEAN                    DontUseBuffer

+  )

+{

+  EFI_MTFTP4_PROTOCOL *Mtftp4;

+  EFI_MTFTP4_TOKEN    Token;

+  EFI_MTFTP4_OPTION   ReqOpt[1];

+  UINT32              OptCnt;

+  UINT8               OptBuf[128];

+  EFI_STATUS          Status;

+

+  Status                    = EFI_DEVICE_ERROR;

+  Mtftp4                    = Private->Mtftp4;

+  OptCnt                    = 0;

+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;

+

+  Status = Mtftp4->Configure (Mtftp4, Config);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (BlockSize != NULL) {

+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];

+    ReqOpt[0].ValueStr  = OptBuf;

+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);

+    OptCnt++;

+  }

+

+  Token.Event         = NULL;

+  Token.OverrideData  = NULL;

+  Token.Filename      = Filename;

+  Token.ModeStr       = NULL;

+  Token.OptionCount   = OptCnt;

+  Token.OptionList    = ReqOpt;

+  Token.Context       = Private;

+

+  if (DontUseBuffer) {

+    Token.BufferSize  = 0;

+    Token.Buffer      = NULL;

+  } else {

+    Token.BufferSize  = *BufferSize;

+    Token.Buffer      = BufferPtr;

+  }

+

+  Token.CheckPacket     = PxeBcMtftp4CheckPacket;

+  Token.TimeoutCallback = NULL;

+  Token.PacketNeeded    = NULL;

+

+  Status = Mtftp4->ReadFile (Mtftp4, &Token);

+  //

+  // Get the real size of received buffer.

+  //

+  *BufferSize = Token.BufferSize;

+

+  Mtftp4->Configure (Mtftp4, NULL);

+

+  return Status;

+}

+

+

+/**

+  This function is to write the data of a file using Tftp.

+

+  @param[in]       Private        Pointer to PxeBc private data.

+  @param[in]       Config         Pointer to EFI_MTFTP4_CONFIG_DATA.

+  @param[in]       Filename       Pointer to boot file name.

+  @param[in]       Overwrite      Indicates whether to use the overwrite attribute.

+  @param[in]       BlockSize      Pointer to required block size.

+  @param[in]       BufferPtr      Pointer to buffer.

+  @param[in, out]  BufferSize     Pointer to buffer size.

+

+  @retval EFI_SUCCESS        Successfully write the data  into the special file.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval other              Write data into file failed.

+

+**/

+EFI_STATUS

+PxeBcMtftp4WriteFile (

+  IN     PXEBC_PRIVATE_DATA         *Private,

+  IN     EFI_MTFTP4_CONFIG_DATA     *Config,

+  IN     UINT8                      *Filename,

+  IN     BOOLEAN                    Overwrite,

+  IN     UINTN                      *BlockSize,

+  IN     UINT8                      *BufferPtr,

+  IN OUT UINT64                     *BufferSize

+  )

+{

+  EFI_MTFTP4_PROTOCOL *Mtftp4;

+  EFI_MTFTP4_TOKEN    Token;

+  EFI_MTFTP4_OPTION   ReqOpt[1];

+  UINT32              OptCnt;

+  UINT8               OptBuf[128];

+  EFI_STATUS          Status;

+

+  Status                    = EFI_DEVICE_ERROR;

+  Mtftp4                    = Private->Mtftp4;

+  OptCnt                    = 0;

+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;

+

+  Status  = Mtftp4->Configure (Mtftp4, Config);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (BlockSize != NULL) {

+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];

+    ReqOpt[0].ValueStr  = OptBuf;

+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);

+    OptCnt++;

+  }

+

+  Token.Event           = NULL;

+  Token.OverrideData    = NULL;

+  Token.Filename        = Filename;

+  Token.ModeStr         = NULL;

+  Token.OptionCount     = OptCnt;

+  Token.OptionList      = ReqOpt;

+  Token.BufferSize      = *BufferSize;

+  Token.Buffer          = BufferPtr;

+  Token.CheckPacket     = PxeBcMtftp4CheckPacket;

+  Token.TimeoutCallback = NULL;

+  Token.PacketNeeded    = NULL;

+

+  Status = Mtftp4->WriteFile (Mtftp4, &Token);

+  //

+  // Get the real size of transmitted buffer.

+  //

+  *BufferSize = Token.BufferSize;

+

+  Mtftp4->Configure (Mtftp4, NULL);

+

+  return Status;

+}

+

+

+/**

+  This function is to get data (file) from a directory using Tftp.

+

+  @param[in]       Private        Pointer to PxeBc private data.

+  @param[in]       Config         Pointer to EFI_MTFTP4_CONFIG_DATA.

+  @param[in]       Filename       Pointer to boot file name.

+  @param[in]       BlockSize      Pointer to required block size.

+  @param[in]       BufferPtr      Pointer to buffer.

+  @param[in, out]  BufferSize     Pointer to buffer size.

+  @param[in]       DontUseBuffer  Indicates whether to use a receive buffer.

+

+  @retval EFI_SUCCES         Successfully obtained the data from the file included in the directory.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Operation failed.

+

+**/

+EFI_STATUS

+PxeBcMtftp4ReadDirectory (

+  IN     PXEBC_PRIVATE_DATA            *Private,

+  IN     EFI_MTFTP4_CONFIG_DATA        *Config,

+  IN     UINT8                         *Filename,

+  IN     UINTN                         *BlockSize,

+  IN     UINT8                         *BufferPtr,

+  IN OUT UINT64                        *BufferSize,

+  IN     BOOLEAN                       DontUseBuffer

+  )

+{

+  EFI_MTFTP4_PROTOCOL *Mtftp4;

+  EFI_MTFTP4_TOKEN    Token;

+  EFI_MTFTP4_OPTION   ReqOpt[1];

+  UINT32              OptCnt;

+  UINT8               OptBuf[128];

+  EFI_STATUS          Status;

+

+  Status                    = EFI_DEVICE_ERROR;

+  Mtftp4                    = Private->Mtftp4;

+  OptCnt                    = 0;

+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;

+

+  Status = Mtftp4->Configure (Mtftp4, Config);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (BlockSize != NULL) {

+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];

+    ReqOpt[0].ValueStr  = OptBuf;

+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);

+    OptCnt++;

+  }

+

+  Token.Event         = NULL;

+  Token.OverrideData  = NULL;

+  Token.Filename      = Filename;

+  Token.ModeStr       = NULL;

+  Token.OptionCount   = OptCnt;

+  Token.OptionList    = ReqOpt;

+  Token.Context       = Private;

+

+  if (DontUseBuffer) {

+    Token.BufferSize  = 0;

+    Token.Buffer      = NULL;

+  } else {

+    Token.BufferSize  = *BufferSize;

+    Token.Buffer      = BufferPtr;

+  }

+

+  Token.CheckPacket     = PxeBcMtftp4CheckPacket;

+  Token.TimeoutCallback = NULL;

+  Token.PacketNeeded    = NULL;

+

+  Status = Mtftp4->ReadDirectory (Mtftp4, &Token);

+  //

+  // Get the real size of received buffer.

+  //

+  *BufferSize = Token.BufferSize;

+

+  Mtftp4->Configure (Mtftp4, NULL);

+

+  return Status;

+}

+

+

+/**

+  This function is wrapper to get the file size using TFTP.

+

+  @param[in]      Private        Pointer to PxeBc private data.

+  @param[in]      Config         Pointer to configure data.

+  @param[in]      Filename       Pointer to boot file name.

+  @param[in]      BlockSize      Pointer to required block size.

+  @param[in, out] BufferSize     Pointer to buffer size.

+

+  @retval EFI_SUCCESS        Successfully obtained the size of file.

+  @retval EFI_NOT_FOUND      Parse the tftp options failed.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Did not obtain the size of the file.

+

+**/

+EFI_STATUS

+PxeBcTftpGetFileSize (

+  IN     PXEBC_PRIVATE_DATA         *Private,

+  IN     VOID                       *Config,

+  IN     UINT8                      *Filename,

+  IN     UINTN                      *BlockSize,

+  IN OUT UINT64                     *BufferSize

+  )

+{

+  if (Private->PxeBc.Mode->UsingIpv6) {

+    return PxeBcMtftp6GetFileSize (

+             Private,

+             (EFI_MTFTP6_CONFIG_DATA *) Config,

+             Filename,

+             BlockSize,

+             BufferSize

+             );

+  } else {

+    return PxeBcMtftp4GetFileSize (

+             Private,

+             (EFI_MTFTP4_CONFIG_DATA *) Config,

+             Filename,

+             BlockSize,

+             BufferSize

+             );

+  }

+}

+

+

+/**

+  This function is a wrapper to get file using TFTP.

+

+  @param[in]      Private        Pointer to PxeBc private data.

+  @param[in]      Config         Pointer to config data.

+  @param[in]      Filename       Pointer to boot file name.

+  @param[in]      BlockSize      Pointer to required block size.

+  @param[in]      BufferPtr      Pointer to buffer.

+  @param[in, out] BufferSize     Pointer to buffer size.

+  @param[in]      DontUseBuffer  Indicates whether to use a receive buffer.

+

+  @retval EFI_SUCCESS        Sucessfully read the data from the special file.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Read data from file failed.

+

+**/

+EFI_STATUS

+PxeBcTftpReadFile (

+  IN     PXEBC_PRIVATE_DATA         *Private,

+  IN     VOID                       *Config,

+  IN     UINT8                      *Filename,

+  IN     UINTN                      *BlockSize,

+  IN     UINT8                      *BufferPtr,

+  IN OUT UINT64                     *BufferSize,

+  IN     BOOLEAN                    DontUseBuffer

+  )

+{

+  if (Private->PxeBc.Mode->UsingIpv6) {

+    return PxeBcMtftp6ReadFile (

+             Private,

+             (EFI_MTFTP6_CONFIG_DATA *) Config,

+             Filename,

+             BlockSize,

+             BufferPtr,

+             BufferSize,

+             DontUseBuffer

+             );

+  } else {

+    return PxeBcMtftp4ReadFile (

+             Private,

+             (EFI_MTFTP4_CONFIG_DATA *) Config,

+             Filename,

+             BlockSize,

+             BufferPtr,

+             BufferSize,

+             DontUseBuffer

+             );

+  }

+}

+

+

+/**

+  This function is a wrapper to write file using TFTP.

+

+  @param[in]       Private        Pointer to PxeBc private data.

+  @param[in]       Config         Pointer to config data.

+  @param[in]       Filename       Pointer to boot file name.

+  @param[in]       Overwrite      Indicate whether with overwrite attribute.

+  @param[in]       BlockSize      Pointer to required block size.

+  @param[in]       BufferPtr      Pointer to buffer.

+  @param[in, out]  BufferSize     Pointer to buffer size.

+

+  @retval EFI_SUCCESS        Successfully wrote the data into a special file.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval other              Write data into file failed.

+

+**/

+EFI_STATUS

+PxeBcTftpWriteFile (

+  IN     PXEBC_PRIVATE_DATA         *Private,

+  IN     VOID                       *Config,

+  IN     UINT8                      *Filename,

+  IN     BOOLEAN                    Overwrite,

+  IN     UINTN                      *BlockSize,

+  IN     UINT8                      *BufferPtr,

+  IN OUT UINT64                     *BufferSize

+  )

+{

+  if (Private->PxeBc.Mode->UsingIpv6) {

+    return PxeBcMtftp6WriteFile (

+             Private,

+             (EFI_MTFTP6_CONFIG_DATA *) Config,

+             Filename,

+             Overwrite,

+             BlockSize,

+             BufferPtr,

+             BufferSize

+             );

+  } else {

+    return PxeBcMtftp4WriteFile (

+             Private,

+             (EFI_MTFTP4_CONFIG_DATA *) Config,

+             Filename,

+             Overwrite,

+             BlockSize,

+             BufferPtr,

+             BufferSize

+             );

+  }

+}

+

+

+/**

+  This function is a wrapper to get the data (file) from a directory using TFTP.

+

+  @param[in]       Private        Pointer to PxeBc private data.

+  @param[in]       Config         Pointer to config data.

+  @param[in]       Filename       Pointer to boot file name.

+  @param[in]       BlockSize      Pointer to required block size.

+  @param[in]       BufferPtr      Pointer to buffer.

+  @param[in, out]  BufferSize     Pointer to buffer size.

+  @param[in]       DontUseBuffer  Indicatse whether to use a receive buffer.

+

+  @retval EFI_SUCCES         Successfully obtained the data from the file included in the directory.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Operation failed.

+

+**/

+EFI_STATUS

+PxeBcTftpReadDirectory (

+  IN     PXEBC_PRIVATE_DATA            *Private,

+  IN     VOID                          *Config,

+  IN     UINT8                         *Filename,

+  IN     UINTN                         *BlockSize,

+  IN     UINT8                         *BufferPtr,

+  IN OUT UINT64                        *BufferSize,

+  IN     BOOLEAN                       DontUseBuffer

+  )

+{

+  if (Private->PxeBc.Mode->UsingIpv6) {

+    return PxeBcMtftp6ReadDirectory (

+             Private,

+             (EFI_MTFTP6_CONFIG_DATA *) Config,

+             Filename,

+             BlockSize,

+             BufferPtr,

+             BufferSize,

+             DontUseBuffer

+             );

+  } else {

+    return PxeBcMtftp4ReadDirectory (

+             Private,

+             (EFI_MTFTP4_CONFIG_DATA *) Config,

+             Filename,

+             BlockSize,

+             BufferPtr,

+             BufferSize,

+             DontUseBuffer

+             );

+  }

+}

+

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h
new file mode 100644
index 0000000..1064195
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h
@@ -0,0 +1,136 @@
+/** @file

+  Functions declaration related with Mtftp for UefiPxeBc Driver.

+

+  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_PXEBC_MTFTP_H__

+#define __EFI_PXEBC_MTFTP_H__

+

+#define PXE_MTFTP_OPTION_BLKSIZE_INDEX     0

+#define PXE_MTFTP_OPTION_TIMEOUT_INDEX     1

+#define PXE_MTFTP_OPTION_TSIZE_INDEX       2

+#define PXE_MTFTP_OPTION_MULTICAST_INDEX   3

+#define PXE_MTFTP_OPTION_MAXIMUM_INDEX     4

+

+#define PXE_MTFTP_ERROR_STRING_LENGTH      127   // refer to definition of struct EFI_PXE_BASE_CODE_TFTP_ERROR.

+#define PXE_MTFTP_DEFAULT_BLOCK_SIZE       512   // refer to rfc-1350.

+

+

+/**

+  This function is wrapper to get the file size using TFTP.

+

+  @param[in]      Private        Pointer to PxeBc private data.

+  @param[in]      Config         Pointer to configure data.

+  @param[in]      Filename       Pointer to boot file name.

+  @param[in]      BlockSize      Pointer to required block size.

+  @param[in, out] BufferSize     Pointer to buffer size.

+

+  @retval EFI_SUCCESS        Successfully obtained the size of file.

+  @retval EFI_NOT_FOUND      Parse the tftp ptions failed.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Did not obtain the size of the file.

+

+**/

+EFI_STATUS

+PxeBcTftpGetFileSize (

+  IN     PXEBC_PRIVATE_DATA         *Private,

+  IN     VOID                       *Config,

+  IN     UINT8                      *Filename,

+  IN     UINTN                      *BlockSize,

+  IN OUT UINT64                     *BufferSize

+  );

+

+

+/**

+  This function is a wrapper to get a file using TFTP.

+

+  @param[in]      Private        Pointer to PxeBc private data.

+  @param[in]      Config         Pointer to config data.

+  @param[in]      Filename       Pointer to boot file name.

+  @param[in]      BlockSize      Pointer to required block size.

+  @param[in]      BufferPtr      Pointer to buffer.

+  @param[in, out] BufferSize     Pointer to buffer size.

+  @param[in]      DontUseBuffer  Indicates whether to use a receive buffer.

+

+  @retval EFI_SUCCESS        Successfully read the data from the special file.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Read data from file failed.

+

+**/

+EFI_STATUS

+PxeBcTftpReadFile (

+  IN     PXEBC_PRIVATE_DATA         *Private,

+  IN     VOID                       *Config,

+  IN     UINT8                      *Filename,

+  IN     UINTN                      *BlockSize,

+  IN     UINT8                      *BufferPtr,

+  IN OUT UINT64                     *BufferSize,

+  IN     BOOLEAN                    DontUseBuffer

+  );

+

+

+/**

+  This function is a wrapper to put file with TFTP.

+

+  @param[in]       Private        Pointer to PxeBc private data.

+  @param[in]       Config         Pointer to config data.

+  @param[in]       Filename       Pointer to boot file name.

+  @param[in]       Overwrite      Indicates whether to use an overwrite attribute.

+  @param[in]       BlockSize      Pointer to required block size.

+  @param[in]       BufferPtr      Pointer to buffer.

+  @param[in, out]  BufferSize     Pointer to buffer size.

+

+  @retval EFI_SUCCESS        Successfully wrote the data into the special file.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval other              Write data into file failed.

+

+**/

+EFI_STATUS

+PxeBcTftpWriteFile (

+  IN     PXEBC_PRIVATE_DATA         *Private,

+  IN     VOID                       *Config,

+  IN     UINT8                      *Filename,

+  IN     BOOLEAN                    Overwrite,

+  IN     UINTN                      *BlockSize,

+  IN     UINT8                      *BufferPtr,

+  IN OUT UINT64                     *BufferSize

+  );

+

+

+/**

+  This function is a wrapper to get the data (file) from a directory using TFTP.

+

+  @param[in]       Private        Pointer to PxeBc private data.

+  @param[in]       Config         Pointer to config data.

+  @param[in]       Filename       Pointer to boot file name.

+  @param[in]       BlockSize      Pointer to required block size.

+  @param[in]       BufferPtr      Pointer to buffer.

+  @param[in, out]  BufferSize     Pointer to buffer size.

+  @param[in]       DontUseBuffer  Indicates whether with a receive buffer.

+

+  @retval EFI_SUCCES         Successfully obtained the data from the file included in directory.

+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.

+  @retval Others             Operation failed.

+

+**/

+EFI_STATUS

+PxeBcTftpReadDirectory (

+  IN     PXEBC_PRIVATE_DATA            *Private,

+  IN     VOID                          *Config,

+  IN     UINT8                         *Filename,

+  IN     UINTN                         *BlockSize,

+  IN     UINT8                         *BufferPtr,

+  IN OUT UINT64                        *BufferSize,

+  IN     BOOLEAN                       DontUseBuffer

+  );

+#endif

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
new file mode 100644
index 0000000..7ad070c
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
@@ -0,0 +1,1588 @@
+/** @file

+  Support functions implementation for UefiPxeBc Driver.

+

+  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#include "PxeBcImpl.h"

+

+

+

+/**

+  This function returns SMBIOS string given the string number.

+

+  @param[in]  Smbios              The pointer to the SMBIOS structure

+  @param[in]  StringNumber        String number to return. 0 is used to skip all

+                                  strings and point to the next SMBIOS structure.

+

+  @return String                  The pointer to the next SMBIOS structure if

+                                  StringNumber == 0.

+

+**/

+CHAR8 *

+PxeBcGetSmbiosString (

+  IN  SMBIOS_STRUCTURE_POINTER  *Smbios,

+  IN  UINT16                    StringNumber

+  )

+{

+  UINT16                        Index;

+  CHAR8                         *String;

+

+  //

+  // Skip over formatted section.

+  //

+  String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);

+

+  //

+  // Look through unformated section.

+  //

+  for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) {

+    if (StringNumber == Index) {

+      return String;

+    }

+

+    //

+    // Skip zero string.

+    //

+    while (*String != 0) {

+      String++;

+    }

+    String++;

+

+    if (*String == 0) {

+      //

+      // If double NULL then we are done.

+      //  Return pointer to next structure in Smbios.

+      //  if you pass in a 0 you will always get here

+      //

+      Smbios->Raw = (UINT8 *)++String;

+      return NULL;

+    }

+  }

+

+  return NULL;

+}

+

+

+/**

+  This function obtains the system guid and the serial number from the smbios table.

+

+  @param[out]  SystemGuid     The pointer of the returned system guid.

+

+  @retval EFI_SUCCESS         Successfully obtained the system guid.

+  @retval EFI_NOT_FOUND       Did not find the SMBIOS table.

+

+**/

+EFI_STATUS

+PxeBcGetSystemGuid (

+  OUT EFI_GUID              *SystemGuid

+  )

+{

+  EFI_STATUS                Status;

+  SMBIOS_TABLE_ENTRY_POINT  *SmbiosTable;

+  SMBIOS_STRUCTURE_POINTER  Smbios;

+  SMBIOS_STRUCTURE_POINTER  SmbiosEnd;

+  UINT16                    Index;

+

+  SmbiosTable = NULL;

+  Status      = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);

+

+  if (EFI_ERROR (Status) || SmbiosTable == NULL) {

+    return EFI_NOT_FOUND;

+  }

+

+  Smbios.Hdr    = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress;

+  SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength);

+

+  for (Index = 0; Index < SmbiosTable->TableLength; Index++) {

+    if (Smbios.Hdr->Type == 1) {

+      if (Smbios.Hdr->Length < 0x19) {

+        //

+        // Older version did not support Guid and Serial number

+        //

+        continue;

+      }

+      //

+      // SMBIOS tables are byte packed so we need to do a byte copy to

+      // prevend alignment faults on Itanium-based platform.

+      //

+      CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));

+      PxeBcGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber);

+

+      return EFI_SUCCESS;

+    }

+    //

+    // Make Smbios point to the next record

+    //

+    PxeBcGetSmbiosString (&Smbios, 0);

+

+    if (Smbios.Raw >= SmbiosEnd.Raw) {

+      //

+      // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e.

+      // given this we must double check against the length of the structure.

+      //

+      return EFI_SUCCESS;

+    }

+  }

+

+  return EFI_NOT_FOUND;

+}

+

+

+/**

+  Flush the previous configration using the new station Ip address.

+

+  @param[in]   Private        The pointer to the PxeBc private data.

+  @param[in]   StationIp      The pointer to the station Ip address.

+  @param[in]   SubnetMask     The pointer to the subnet mask address for v4.

+

+  @retval EFI_SUCCESS         Successfully flushed the previous configuration.

+  @retval Others              Failed to flush using the new station Ip.

+

+**/

+EFI_STATUS

+PxeBcFlushStaionIp (

+  PXEBC_PRIVATE_DATA       *Private,

+  EFI_IP_ADDRESS           *StationIp,

+  EFI_IP_ADDRESS           *SubnetMask     OPTIONAL

+  )

+{

+  EFI_PXE_BASE_CODE_MODE   *Mode;

+  EFI_STATUS               Status;

+

+  ASSERT (StationIp != NULL);

+

+  Mode   = Private->PxeBc.Mode;

+  Status = EFI_SUCCESS;

+

+  if (Mode->UsingIpv6) {

+

+    CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));

+    CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));

+

+    //

+    // Reconfigure the Ip6 instance to capture background ICMP6 packets with new station Ip address.

+    //

+    Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);

+    Private->Ip6->Configure (Private->Ip6, NULL);

+

+    Status = Private->Ip6->Configure (Private->Ip6, &Private->Ip6CfgData);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+

+    Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+

+  } else {

+    ASSERT (SubnetMask != NULL);

+    CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));

+    CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));

+    CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));

+    CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));

+

+    //

+    // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address.

+    //

+    Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);

+    Private->Ip4->Configure (Private->Ip4, NULL);

+

+    Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+

+    Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+

+  }

+

+ON_EXIT:

+  return Status;

+}

+

+

+/**

+  Notify the callback function when an event is triggered.

+

+  @param[in]  Event           The triggered event.

+  @param[in]  Context         The opaque parameter to the function.

+

+**/

+VOID

+EFIAPI

+PxeBcCommonNotify (

+  IN EFI_EVENT           Event,

+  IN VOID                *Context

+  )

+{

+  *((BOOLEAN *) Context) = TRUE;

+}

+

+

+/**

+  Do arp resolution from arp cache in PxeBcMode.

+

+  @param  Mode           The pointer to EFI_PXE_BASE_CODE_MODE.

+  @param  Ip4Addr        The Ip4 address for resolution.

+  @param  MacAddress     The resoluted MAC address if the resolution is successful.

+                         The value is undefined if the resolution fails.

+

+  @retval TRUE           Found an matched entry.

+  @retval FALSE          Did not find a matched entry.

+

+**/

+BOOLEAN

+PxeBcCheckArpCache (

+  IN  EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN  EFI_IPv4_ADDRESS          *Ip4Addr,

+  OUT EFI_MAC_ADDRESS           *MacAddress

+  )

+{

+  UINT32       Index;

+

+  ASSERT (!Mode->UsingIpv6);

+

+  //

+  // Check whether the current Arp cache in mode data contains this information or not.

+  //

+  for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {

+    if (EFI_IP4_EQUAL (&Mode->ArpCache[Index].IpAddr.v4, Ip4Addr)) {

+      CopyMem (

+        MacAddress,

+        &Mode->ArpCache[Index].MacAddr,

+        sizeof (EFI_MAC_ADDRESS)

+        );

+      return TRUE;

+    }

+  }

+

+  return FALSE;

+}

+

+

+/**

+  Update the arp cache periodically.

+

+  @param  Event              The pointer to EFI_PXE_BC_PROTOCOL.

+  @param  Context            Context of the timer event.

+

+**/

+VOID

+EFIAPI

+PxeBcArpCacheUpdate (

+  IN EFI_EVENT            Event,

+  IN VOID                 *Context

+  )

+{

+  PXEBC_PRIVATE_DATA      *Private;

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+  EFI_ARP_FIND_DATA       *ArpEntry;

+  UINT32                  EntryLength;

+  UINT32                  EntryCount;

+  UINT32                  Index;

+  EFI_STATUS              Status;

+

+  Private = (PXEBC_PRIVATE_DATA *) Context;

+  Mode    = Private->PxeBc.Mode;

+

+  ASSERT (!Mode->UsingIpv6);

+

+  //

+  // Get the current Arp cache from Arp driver.

+  //

+  Status = Private->Arp->Find (

+                           Private->Arp,

+                           TRUE,

+                           NULL,

+                           &EntryLength,

+                           &EntryCount,

+                           &ArpEntry,

+                           TRUE

+                           );

+  if (EFI_ERROR (Status)) {

+    return;

+  }

+

+  //

+  // Update the Arp cache in mode data.

+  //

+  Mode->ArpCacheEntries = MIN (EntryCount, EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES);

+

+  for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {

+    CopyMem (

+      &Mode->ArpCache[Index].IpAddr,

+      ArpEntry + 1,

+      ArpEntry->SwAddressLength

+      );

+    CopyMem (

+      &Mode->ArpCache[Index].MacAddr,

+      (UINT8 *) (ArpEntry + 1) + ArpEntry->SwAddressLength,

+      ArpEntry->HwAddressLength

+      );

+    ArpEntry = (EFI_ARP_FIND_DATA *) ((UINT8 *) ArpEntry + EntryLength);

+  }

+}

+

+

+/**

+  Notify function to handle the received ICMP message in DPC.

+

+  @param  Context               The PXEBC private data.

+

+**/

+VOID

+EFIAPI

+PxeBcIcmpErrorDpcHandle (

+  IN VOID                      *Context

+  )

+{

+  EFI_STATUS                   Status;

+  EFI_IP4_RECEIVE_DATA         *RxData;

+  EFI_IP4_PROTOCOL             *Ip4;

+  PXEBC_PRIVATE_DATA           *Private;

+  EFI_PXE_BASE_CODE_MODE       *Mode;

+  UINT8                        Type;

+  UINTN                        Index;

+  UINT32                       CopiedLen;

+  UINT8                        *IcmpError;

+

+  Private = (PXEBC_PRIVATE_DATA *) Context;

+  Mode    = &Private->Mode;

+  Status  = Private->IcmpToken.Status;

+  RxData  = Private->IcmpToken.Packet.RxData;

+  Ip4     = Private->Ip4;

+

+  ASSERT (!Mode->UsingIpv6);

+

+  if (Status == EFI_ABORTED) {

+    //

+    // It's triggered by user cancellation.

+    //

+    return;

+  }

+

+  if (RxData == NULL) {

+    goto ON_EXIT;

+  }

+

+  if (Status != EFI_ICMP_ERROR) {

+    //

+    // The return status should be recognized as EFI_ICMP_ERROR.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  if (EFI_IP4 (RxData->Header->SourceAddress) != 0 &&

+      !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) {

+    //

+    // The source address of the received packet should be a valid unicast address.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) {

+    //

+    // The destination address of the received packet should be equal to the host address.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) {

+    //

+    // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);

+

+  if (Type != ICMP_DEST_UNREACHABLE &&

+      Type != ICMP_SOURCE_QUENCH &&

+      Type != ICMP_REDIRECT &&

+      Type != ICMP_TIME_EXCEEDED &&

+      Type != ICMP_PARAMETER_PROBLEM) {

+    //

+    // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  //

+  // Copy the right ICMP error message into mode data.

+  //

+  CopiedLen = 0;

+  IcmpError = (UINT8 *) &Mode->IcmpError;

+

+  for (Index = 0; Index < RxData->FragmentCount; Index++) {

+    CopiedLen += RxData->FragmentTable[Index].FragmentLength;

+    if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {

+      CopyMem (

+        IcmpError,

+        RxData->FragmentTable[Index].FragmentBuffer,

+        RxData->FragmentTable[Index].FragmentLength

+        );

+    } else {

+      CopyMem (

+        IcmpError,

+        RxData->FragmentTable[Index].FragmentBuffer,

+        CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)

+        );

+    }

+    IcmpError += CopiedLen;

+  }

+

+ON_EXIT:

+  Private->IcmpToken.Status = EFI_NOT_READY;

+  Ip4->Receive (Ip4, &Private->IcmpToken);

+}

+

+

+/**

+  Callback function to update the latest ICMP6 error message.

+

+  @param  Event                 The event signalled.

+  @param  Context               The context passed in using the event notifier.

+

+**/

+VOID

+EFIAPI

+PxeBcIcmpErrorUpdate (

+  IN EFI_EVENT            Event,

+  IN VOID                 *Context

+  )

+{

+  QueueDpc (TPL_CALLBACK, PxeBcIcmpErrorDpcHandle, Context);

+}

+

+

+/**

+  Notify function to handle the received ICMP6 message in DPC.

+

+  @param  Context               The PXEBC private data.

+

+**/

+VOID

+EFIAPI

+PxeBcIcmp6ErrorDpcHandle (

+  IN VOID                 *Context

+  )

+{

+  PXEBC_PRIVATE_DATA      *Private;

+  EFI_IP6_RECEIVE_DATA    *RxData;

+  EFI_IP6_PROTOCOL        *Ip6;

+  EFI_PXE_BASE_CODE_MODE  *Mode;

+  EFI_STATUS              Status;

+  UINTN                   Index;

+  UINT8                   Type;

+  UINT32                  CopiedLen;

+  UINT8                   *Icmp6Error;

+

+  Private = (PXEBC_PRIVATE_DATA *) Context;

+  Mode    = &Private->Mode;

+  Status  = Private->Icmp6Token.Status;

+  RxData  = Private->Icmp6Token.Packet.RxData;

+  Ip6     = Private->Ip6;

+

+  ASSERT (Mode->UsingIpv6);

+

+  if (Status == EFI_ABORTED) {

+    //

+    // It's triggered by user cancellation.

+    //

+    return;

+  }

+

+  if (RxData == NULL) {

+    goto ON_EXIT;

+  }

+

+  if (Status != EFI_ICMP_ERROR) {

+    //

+    // The return status should be recognized as EFI_ICMP_ERROR.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) {

+    //

+    // The source address of the received packet should be a valid unicast address.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) &&

+      !EFI_IP6_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v6)) {

+    //

+    // The destination address of the received packet should be equal to the host address.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  if (RxData->Header->NextHeader != IP6_ICMP) {

+    //

+    // The nextheader in the header of the receveid packet should be IP6_ICMP.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);

+

+  if (Type != ICMP_V6_DEST_UNREACHABLE &&

+      Type != ICMP_V6_PACKET_TOO_BIG &&

+      Type != ICMP_V6_PACKET_TOO_BIG &&

+      Type != ICMP_V6_PARAMETER_PROBLEM) {

+    //

+    // The type of the receveid packet should be an ICMP6 error message.

+    //

+    gBS->SignalEvent (RxData->RecycleSignal);

+    goto ON_EXIT;

+  }

+

+  //

+  // Copy the right ICMP6 error message into mode data.

+  //

+  CopiedLen  = 0;

+  Icmp6Error = (UINT8 *) &Mode->IcmpError;

+

+  for (Index = 0; Index < RxData->FragmentCount; Index++) {

+    CopiedLen += RxData->FragmentTable[Index].FragmentLength;

+    if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {

+      CopyMem (

+        Icmp6Error,

+        RxData->FragmentTable[Index].FragmentBuffer,

+        RxData->FragmentTable[Index].FragmentLength

+        );

+    } else {

+      CopyMem (

+        Icmp6Error,

+        RxData->FragmentTable[Index].FragmentBuffer,

+        CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)

+        );

+    }

+    Icmp6Error += CopiedLen;

+  }

+

+ON_EXIT:

+  Private->Icmp6Token.Status = EFI_NOT_READY;

+  Ip6->Receive (Ip6, &Private->Icmp6Token);

+}

+

+

+/**

+  Callback function to update the latest ICMP6 error message.

+

+  @param  Event                 The event signalled.

+  @param  Context               The context passed in using the event notifier.

+

+**/

+VOID

+EFIAPI

+PxeBcIcmp6ErrorUpdate (

+  IN EFI_EVENT               Event,

+  IN VOID                    *Context

+  )

+{

+  QueueDpc (TPL_CALLBACK, PxeBcIcmp6ErrorDpcHandle, Context);

+}

+

+

+/**

+  This function is to configure a UDPv4 instance for UdpWrite.

+

+  @param[in]       Udp4                 The pointer to EFI_UDP4_PROTOCOL.

+  @param[in]       StationIp            The pointer to the station address.

+  @param[in]       SubnetMask           The pointer to the subnet mask.

+  @param[in]       Gateway              The pointer to the gateway address.

+  @param[in, out]  SrcPort              The pointer to the source port.

+  @param[in]       DoNotFragment        If TRUE, fragment is not enabled.

+                                        Otherwise, fragment is enabled.

+

+  @retval          EFI_SUCCESS          Successfully configured this instance.

+  @retval          Others               Failed to configure this instance.

+

+**/

+EFI_STATUS

+PxeBcConfigUdp4Write (

+  IN     EFI_UDP4_PROTOCOL  *Udp4,

+  IN     EFI_IPv4_ADDRESS   *StationIp,

+  IN     EFI_IPv4_ADDRESS   *SubnetMask,

+  IN     EFI_IPv4_ADDRESS   *Gateway,

+  IN OUT UINT16             *SrcPort,

+  IN     BOOLEAN            DoNotFragment

+  )

+{

+  EFI_UDP4_CONFIG_DATA  Udp4CfgData;

+  EFI_STATUS            Status;

+

+  ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData));

+

+  Udp4CfgData.TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;

+  Udp4CfgData.ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;

+  Udp4CfgData.TypeOfService      = DEFAULT_ToS;

+  Udp4CfgData.TimeToLive         = DEFAULT_TTL;

+  Udp4CfgData.AllowDuplicatePort = TRUE;

+  Udp4CfgData.DoNotFragment      = DoNotFragment;

+

+  CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp));

+  CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask));

+

+  Udp4CfgData.StationPort = *SrcPort;

+

+  //

+  // Reset the UDPv4 instance.

+  //

+  Udp4->Configure (Udp4, NULL);

+

+  Status = Udp4->Configure (Udp4, &Udp4CfgData);

+  if (!EFI_ERROR (Status) && !EFI_IP4_EQUAL (Gateway, &mZeroIp4Addr)) {

+    //

+    // The basic configuration is OK, need to add the default route entry

+    //

+    Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway);

+    if (EFI_ERROR (Status)) {

+      Udp4->Configure (Udp4, NULL);

+    }

+  }

+

+  if (!EFI_ERROR (Status) && *SrcPort == 0) {

+    Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL);

+    *SrcPort = Udp4CfgData.StationPort;

+  }

+

+  return Status;

+}

+

+

+/**

+  This function is to configure a UDPv6 instance for UdpWrite.

+

+  @param[in]       Udp6                 The pointer to EFI_UDP6_PROTOCOL.

+  @param[in]       StationIp            The pointer to the station address.

+  @param[in, out]  SrcPort              The pointer to the source port.

+

+  @retval          EFI_SUCCESS          Successfully configured this instance.

+  @retval          Others               Failed to configure this instance.

+

+**/

+EFI_STATUS

+PxeBcConfigUdp6Write (

+  IN     EFI_UDP6_PROTOCOL  *Udp6,

+  IN     EFI_IPv6_ADDRESS   *StationIp,

+  IN OUT UINT16             *SrcPort

+  )

+{

+  EFI_UDP6_CONFIG_DATA  CfgData;

+  EFI_STATUS            Status;

+

+  ZeroMem (&CfgData, sizeof (EFI_UDP6_CONFIG_DATA));

+

+  CfgData.ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;

+  CfgData.TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;

+  CfgData.HopLimit           = PXEBC_DEFAULT_HOPLIMIT;

+  CfgData.AllowDuplicatePort = TRUE;

+  CfgData.StationPort        = *SrcPort;

+

+  CopyMem (&CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));

+

+  //

+  // Reset the UDPv6 instance.

+  //

+  Udp6->Configure (Udp6, NULL);

+

+  Status = Udp6->Configure (Udp6, &CfgData);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (!EFI_ERROR (Status) && *SrcPort == 0) {

+    Udp6->GetModeData (Udp6, &CfgData, NULL, NULL, NULL);

+    *SrcPort = CfgData.StationPort;

+  }

+

+  return Status;

+}

+

+

+/**

+  This function is to configure a UDPv4 instance for UdpWrite.

+

+  @param[in]       Udp4                 The pointer to EFI_UDP4_PROTOCOL.

+  @param[in]       Session              The pointer to the UDP4 session data.

+  @param[in]       TimeoutEvent         The event for timeout.

+  @param[in]       Gateway              The pointer to the gateway address.

+  @param[in]       HeaderSize           An optional field which may be set to the length of a header

+                                        at HeaderPtr to be prefixed to the data at BufferPtr.

+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be

+                                        prefixed to the data at BufferPtr.

+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.

+  @param[in]       BufferPtr            A pointer to the data to be written.

+

+  @retval          EFI_SUCCESS          Successfully send out data using Udp4Write.

+  @retval          Others               Failed to send out data.

+

+**/

+EFI_STATUS

+PxeBcUdp4Write (

+  IN EFI_UDP4_PROTOCOL       *Udp4,

+  IN EFI_UDP4_SESSION_DATA   *Session,

+  IN EFI_EVENT               TimeoutEvent,

+  IN EFI_IPv4_ADDRESS        *Gateway      OPTIONAL,

+  IN UINTN                   *HeaderSize   OPTIONAL,

+  IN VOID                    *HeaderPtr    OPTIONAL,

+  IN UINTN                   *BufferSize,

+  IN VOID                    *BufferPtr

+  )

+{

+  EFI_UDP4_COMPLETION_TOKEN Token;

+  EFI_UDP4_TRANSMIT_DATA    *TxData;

+  UINT32                    TxLength;

+  UINT32                    FragCount;

+  UINT32                    DataLength;

+  BOOLEAN                   IsDone;

+  EFI_STATUS                Status;

+

+  //

+  // Arrange one fragment buffer for data, and another fragment buffer for header if has.

+  //

+  FragCount = (HeaderSize != NULL) ? 2 : 1;

+  TxLength  = sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA);

+  TxData    = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (TxLength);

+  if (TxData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  TxData->FragmentCount                               = FragCount;

+  TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;

+  TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;

+  DataLength                                          = (UINT32) *BufferSize;

+

+  if (HeaderSize != NULL) {

+    TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;

+    TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;

+    DataLength                             += (UINT32) *HeaderSize;

+  }

+

+  if (Gateway != NULL) {

+    TxData->GatewayAddress  = Gateway;

+  }

+

+  TxData->UdpSessionData  = Session;

+  TxData->DataLength      = DataLength;

+  Token.Packet.TxData     = TxData;

+  Token.Status            = EFI_NOT_READY;

+  IsDone                  = FALSE;

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  PxeBcCommonNotify,

+                  &IsDone,

+                  &Token.Event

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  Status = Udp4->Transmit (Udp4, &Token);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.

+  //

+  while (!IsDone &&

+         Token.Status == EFI_NOT_READY &&

+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {

+    Udp4->Poll (Udp4);

+  }

+

+  Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;

+

+ON_EXIT:

+  if (Token.Event != NULL) {

+    gBS->CloseEvent (Token.Event);

+  }

+  FreePool (TxData);

+

+  return Status;

+}

+

+

+/**

+  This function is to configure a UDPv4 instance for UdpWrite.

+

+  @param[in]       Udp6                 The pointer to EFI_UDP6_PROTOCOL.

+  @param[in]       Session              The pointer to the UDP6 session data.

+  @param[in]       TimeoutEvent         The event for timeout.

+  @param[in]       HeaderSize           An optional field which may be set to the length of a header

+                                        at HeaderPtr to be prefixed to the data at BufferPtr.

+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be

+                                        prefixed to the data at BufferPtr.

+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.

+  @param[in]       BufferPtr            A pointer to the data to be written.

+

+  @retval          EFI_SUCCESS          Successfully sent out data using Udp6Write.

+  @retval          Others               Failed to send out data.

+

+**/

+EFI_STATUS

+PxeBcUdp6Write (

+  IN EFI_UDP6_PROTOCOL       *Udp6,

+  IN EFI_UDP6_SESSION_DATA   *Session,

+  IN EFI_EVENT               TimeoutEvent,

+  IN UINTN                   *HeaderSize   OPTIONAL,

+  IN VOID                    *HeaderPtr    OPTIONAL,

+  IN UINTN                   *BufferSize,

+  IN VOID                    *BufferPtr

+  )

+{

+  EFI_UDP6_COMPLETION_TOKEN Token;

+  EFI_UDP6_TRANSMIT_DATA    *TxData;

+  UINT32                    TxLength;

+  UINT32                    FragCount;

+  UINT32                    DataLength;

+  BOOLEAN                   IsDone;

+  EFI_STATUS                Status;

+

+  //

+  // Arrange one fragment buffer for data, and another fragment buffer for header if has.

+  //

+  FragCount = (HeaderSize != NULL) ? 2 : 1;

+  TxLength  = sizeof (EFI_UDP6_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA);

+  TxData    = (EFI_UDP6_TRANSMIT_DATA *) AllocateZeroPool (TxLength);

+  if (TxData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  TxData->FragmentCount                               = FragCount;

+  TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;

+  TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;

+  DataLength                                          = (UINT32) *BufferSize;

+

+  if (HeaderSize != NULL) {

+    TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;

+    TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;

+    DataLength                             += (UINT32) *HeaderSize;

+  }

+

+  TxData->UdpSessionData  = Session;

+  TxData->DataLength      = DataLength;

+  Token.Packet.TxData     = TxData;

+  Token.Status            = EFI_NOT_READY;

+  IsDone                  = FALSE;

+

+  Status = gBS->CreateEvent (

+                  EVT_NOTIFY_SIGNAL,

+                  TPL_NOTIFY,

+                  PxeBcCommonNotify,

+                  &IsDone,

+                  &Token.Event

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  Status = Udp6->Transmit (Udp6, &Token);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  //

+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.

+  //

+  while (!IsDone &&

+         Token.Status == EFI_NOT_READY &&

+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {

+    Udp6->Poll (Udp6);

+  }

+

+  Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;

+

+ON_EXIT:

+  if (Token.Event != NULL) {

+    gBS->CloseEvent (Token.Event);

+  }

+  FreePool (TxData);

+

+  return Status;

+}

+

+

+/**

+  Check the received packet using the Ip filter.

+

+  @param[in]  Mode                The pointer to the mode data of PxeBc.

+  @param[in]  Session             The pointer to the current UDPv4 session.

+  @param[in]  OpFlags             Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Passed the Ip filter successfully.

+  @retval     FALSE               Failed to pass the Ip filter.

+

+**/

+BOOLEAN

+PxeBcCheckByIpFilter (

+  IN EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN VOID                      *Session,

+  IN UINT16                    OpFlags

+  )

+{

+  EFI_IP_ADDRESS               DestinationIp;

+  UINTN                        Index;

+

+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) == 0) {

+    return TRUE;

+  }

+

+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) {

+    return TRUE;

+  }

+

+  //

+  // Convert the destination address in session data to host order.

+  //

+  if (Mode->UsingIpv6) {

+    CopyMem (

+      &DestinationIp,

+      &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress,

+      sizeof (EFI_IPv6_ADDRESS)

+      );

+    NTOHLLL (&DestinationIp.v6);

+  } else {

+    ZeroMem (&DestinationIp, sizeof (EFI_IP_ADDRESS));

+    CopyMem (

+      &DestinationIp,

+      &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress,

+      sizeof (EFI_IPv4_ADDRESS)

+      );

+    EFI_NTOHL (DestinationIp);

+  }

+

+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0 &&

+      (IP4_IS_MULTICAST (DestinationIp.Addr[0]) ||

+       IP6_IS_MULTICAST (&DestinationIp))) {

+    return TRUE;

+  }

+

+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0 &&

+      IP4_IS_LOCAL_BROADCAST (DestinationIp.Addr[0])) {

+    ASSERT (!Mode->UsingIpv6);

+    return TRUE;

+  }

+

+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&

+      (EFI_IP4_EQUAL (&Mode->StationIp.v4, &DestinationIp) ||

+       EFI_IP6_EQUAL (&Mode->StationIp.v6, &DestinationIp))) {

+    //

+    // Matched if the dest address is equal to the station address.

+    //

+    return TRUE;

+  }

+

+  for (Index = 0; Index < Mode->IpFilter.IpCnt; Index++) {

+    ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);

+    if (EFI_IP4_EQUAL (&Mode->IpFilter.IpList[Index].v4, &DestinationIp) ||

+        EFI_IP6_EQUAL (&Mode->IpFilter.IpList[Index].v6, &DestinationIp)) {

+      //

+      // Matched if the dest address is equal to any of address in the filter list.

+      //

+      return TRUE;

+    }

+  }

+

+  return FALSE;

+}

+

+

+/**

+  Filter the received packet using the destination Ip.

+

+  @param[in]       Mode           The pointer to the mode data of PxeBc.

+  @param[in]       Session        The pointer to the current UDPv4 session.

+  @param[in, out]  DestIp         The pointer to the destination Ip address.

+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Passed the IPv4 filter successfully.

+  @retval     FALSE               Failed to pass the IPv4 filter.

+

+**/

+BOOLEAN

+PxeBcCheckByDestIp (

+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN     VOID                      *Session,

+  IN OUT EFI_IP_ADDRESS            *DestIp,

+  IN     UINT16                    OpFlags

+  )

+{

+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) {

+    //

+    // Copy the destination address from the received packet if accept any.

+    //

+    if (DestIp != NULL) {

+      if (Mode->UsingIpv6) {

+        CopyMem (

+          DestIp,

+          &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress,

+          sizeof (EFI_IPv6_ADDRESS)

+          );

+      } else {

+        ZeroMem (DestIp, sizeof (EFI_IP_ADDRESS));

+        CopyMem (

+          DestIp,

+          &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress,

+          sizeof (EFI_IPv4_ADDRESS)

+          );

+      }

+

+    }

+    return TRUE;

+  } else if (DestIp != NULL &&

+             (EFI_IP4_EQUAL (DestIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||

+              EFI_IP6_EQUAL (DestIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress))) {

+    //

+    // The destination address in the received packet is matched if present.

+    //

+    return TRUE;

+  } else if (EFI_IP4_EQUAL (&Mode->StationIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||

+             EFI_IP6_EQUAL (&Mode->StationIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress)) {

+    //

+    // The destination address in the received packet is equal to the host address.

+    //

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+

+/**

+  Check the received packet using the destination port.

+

+  @param[in]       PxeBcMode      The pointer to the mode data of PxeBc.

+  @param[in]       Session        The pointer to the current UDPv4 session.

+  @param[in, out]  DestPort       The pointer to the destination port.

+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Passed the IPv4 filter successfully.

+  @retval     FALSE               Failed to pass the IPv4 filter.

+

+**/

+BOOLEAN

+PxeBcCheckByDestPort (

+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN     VOID                      *Session,

+  IN OUT UINT16                    *DestPort,

+  IN     UINT16                    OpFlags

+  )

+{

+  UINT16       Port;

+

+  if (Mode->UsingIpv6) {

+    Port = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort;

+  } else {

+    Port = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort;

+  }

+

+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) {

+    //

+    // Return the destination port in the received packet if accept any.

+    //

+    if (DestPort != NULL) {

+      *DestPort = Port;

+    }

+    return TRUE;

+  } else if (DestPort != NULL && *DestPort == Port) {

+    //

+    // The destination port in the received packet is matched if present.

+    //

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+

+/**

+  Filter the received packet using the source Ip.

+

+  @param[in]       Mode           The pointer to the mode data of PxeBc.

+  @param[in]       Session        The pointer to the current UDPv4 session.

+  @param[in, out]  SrcIp          The pointer to the source Ip address.

+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Passed the IPv4 filter successfully.

+  @retval     FALSE               Failed to pass the IPv4 filter.

+

+**/

+BOOLEAN

+PxeBcFilterBySrcIp (

+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN     VOID                      *Session,

+  IN OUT EFI_IP_ADDRESS            *SrcIp,

+  IN     UINT16                    OpFlags

+  )

+{

+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) {

+    //

+    // Copy the source address from the received packet if accept any.

+    //

+    if (SrcIp != NULL) {

+      if (Mode->UsingIpv6) {

+        CopyMem (

+          SrcIp,

+          &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress,

+          sizeof (EFI_IPv6_ADDRESS)

+          );

+      } else {

+        ZeroMem (SrcIp, sizeof (EFI_IP_ADDRESS));

+        CopyMem (

+          SrcIp,

+          &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress,

+          sizeof (EFI_IPv4_ADDRESS)

+          );

+      }

+

+    }

+    return TRUE;

+  } else if (SrcIp != NULL &&

+             (EFI_IP4_EQUAL (SrcIp, &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress) ||

+              EFI_IP6_EQUAL (SrcIp, &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress))) {

+    //

+    // The source address in the received packet is matched if present.

+    //

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+

+/**

+  Filter the received packet using the source port.

+

+  @param[in]       Mode           The pointer to the mode data of PxeBc.

+  @param[in]       Session        The pointer to the current UDPv4 session.

+  @param[in, out]  SrcPort        The pointer to the source port.

+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Passed the IPv4 filter successfully.

+  @retval     FALSE               Failed to pass the IPv4 filter.

+

+**/

+BOOLEAN

+PxeBcFilterBySrcPort (

+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN     VOID                      *Session,

+  IN OUT UINT16                    *SrcPort,

+  IN     UINT16                    OpFlags

+  )

+{

+  UINT16       Port;

+

+  if (Mode->UsingIpv6) {

+    Port = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort;

+  } else {

+    Port = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort;

+  }

+

+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) {

+    //

+    // Return the source port in the received packet if accept any.

+    //

+    if (SrcPort != NULL) {

+      *SrcPort = Port;

+    }

+    return TRUE;

+  } else if (SrcPort != NULL && *SrcPort == Port) {

+    //

+    // The source port in the received packet is matched if present.

+    //

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+

+/**

+  This function is to receive packet using Udp4Read.

+

+  @param[in]       Udp4                 The pointer to EFI_UDP4_PROTOCOL.

+  @param[in]       Token                The pointer to EFI_UDP4_COMPLETION_TOKEN.

+  @param[in]       Mode                 The pointer to EFI_PXE_BASE_CODE_MODE.

+  @param[in]       TimeoutEvent         The event for timeout.

+  @param[in]       OpFlags              The UDP operation flags.

+  @param[in]       IsDone               The pointer to the IsDone flag.

+  @param[out]      IsMatched            The pointer to the IsMatched flag.

+  @param[in, out]  DestIp               The pointer to the destination address.

+  @param[in, out]  DestPort             The pointer to the destination port.

+  @param[in, out]  SrcIp                The pointer to the source address.

+  @param[in, out]  SrcPort              The pointer to the source port.

+

+  @retval          EFI_SUCCESS          Successfully read the data using Udp4.

+  @retval          Others               Failed to send out data.

+

+**/

+EFI_STATUS

+PxeBcUdp4Read (

+  IN     EFI_UDP4_PROTOCOL            *Udp4,

+  IN     EFI_UDP4_COMPLETION_TOKEN    *Token,

+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,

+  IN     EFI_EVENT                    TimeoutEvent,

+  IN     UINT16                       OpFlags,

+  IN     BOOLEAN                      *IsDone,

+     OUT BOOLEAN                      *IsMatched,

+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,

+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL

+  )

+{

+  EFI_UDP4_RECEIVE_DATA     *RxData;

+  EFI_UDP4_SESSION_DATA     *Session;

+  EFI_STATUS                Status;

+

+  Token->Status = EFI_NOT_READY;

+  *IsDone       = FALSE;

+

+  Status = Udp4->Receive (Udp4, Token);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.

+  //

+  while (!(*IsDone) &&

+         Token->Status == EFI_NOT_READY &&

+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {

+    //

+    // Poll the token utill reply/ICMPv6 error message received or timeout.

+    //

+    Udp4->Poll (Udp4);

+    if (Token->Status == EFI_ICMP_ERROR ||

+        Token->Status == EFI_NETWORK_UNREACHABLE ||

+        Token->Status == EFI_HOST_UNREACHABLE ||

+        Token->Status == EFI_PROTOCOL_UNREACHABLE ||

+        Token->Status == EFI_PORT_UNREACHABLE) {

+      break;

+    }

+  }

+

+  Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;

+

+  if (!EFI_ERROR (Status)) {

+    //

+    // check whether this packet matches the filters

+    //

+    RxData    = Token->Packet.RxData;

+    Session   = &RxData->UdpSession;

+

+    *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);

+

+    if (*IsMatched) {

+      *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);

+    }

+

+    if (*IsMatched) {

+      *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);

+    }

+

+    if (*IsMatched) {

+      *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);

+    }

+

+    if (*IsMatched) {

+      *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);

+    }

+

+    if (!(*IsMatched)) {

+      //

+      // Recycle the receiving buffer if not matched.

+      //

+      gBS->SignalEvent (RxData->RecycleSignal);

+    }

+  }

+

+  return Status;

+}

+

+

+/**

+  This function is to receive packets using Udp6Read.

+

+  @param[in]       Udp6                 The pointer to EFI_UDP6_PROTOCOL.

+  @param[in]       Token                The pointer to EFI_UDP6_COMPLETION_TOKEN.

+  @param[in]       Mode                 The pointer to EFI_PXE_BASE_CODE_MODE.

+  @param[in]       TimeoutEvent         The event for timeout.

+  @param[in]       OpFlags              The UDP operation flags.

+  @param[in]       IsDone               The pointer to the IsDone flag.

+  @param[out]      IsMatched            The pointer to the IsMatched flag.

+  @param[in, out]  DestIp               The pointer to the destination address.

+  @param[in, out]  DestPort             The pointer to the destination port.

+  @param[in, out]  SrcIp                The pointer to the source address.

+  @param[in, out]  SrcPort              The pointer to the source port.

+

+  @retval          EFI_SUCCESS          Successfully read data using Udp6.

+  @retval          Others               Failed to send out data.

+

+**/

+EFI_STATUS

+PxeBcUdp6Read (

+  IN     EFI_UDP6_PROTOCOL            *Udp6,

+  IN     EFI_UDP6_COMPLETION_TOKEN    *Token,

+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,

+  IN     EFI_EVENT                    TimeoutEvent,

+  IN     UINT16                       OpFlags,

+  IN     BOOLEAN                      *IsDone,

+     OUT BOOLEAN                      *IsMatched,

+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,

+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL

+  )

+{

+  EFI_UDP6_RECEIVE_DATA     *RxData;

+  EFI_UDP6_SESSION_DATA     *Session;

+  EFI_STATUS                Status;

+

+  Token->Status = EFI_NOT_READY;

+  *IsDone       = FALSE;

+

+  Status = Udp6->Receive (Udp6, Token);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  //

+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.

+  //

+  while (!(*IsDone) &&

+         Token->Status == EFI_NOT_READY &&

+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {

+    //

+    // Poll the token utill reply/ICMPv6 error message received or timeout.

+    //

+    Udp6->Poll (Udp6);

+    if (Token->Status == EFI_ICMP_ERROR ||

+        Token->Status == EFI_NETWORK_UNREACHABLE ||

+        Token->Status == EFI_HOST_UNREACHABLE ||

+        Token->Status == EFI_PROTOCOL_UNREACHABLE ||

+        Token->Status == EFI_PORT_UNREACHABLE) {

+      break;

+    }

+  }

+

+  Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;

+

+  if (!EFI_ERROR (Status)) {

+    //

+    // check whether this packet matches the filters

+    //

+    RxData    = Token->Packet.RxData;

+    Session   = &RxData->UdpSession;

+

+    *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);

+

+    if (*IsMatched) {

+      *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);

+    }

+

+    if (*IsMatched) {

+      *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);

+    }

+

+    if (*IsMatched) {

+      *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);

+    }

+

+    if (*IsMatched) {

+      *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);

+    }

+

+    if (!(*IsMatched)) {

+      //

+      // Recycle the receiving buffer if not matched.

+      //

+      gBS->SignalEvent (RxData->RecycleSignal);

+    }

+  }

+

+  return Status;

+}

+

+

+/**

+  This function is to display the IPv4 address.

+

+  @param[in]  Ip        The pointer to the IPv4 address.

+

+**/

+VOID

+PxeBcShowIp4Addr (

+  IN EFI_IPv4_ADDRESS   *Ip

+  )

+{

+  UINTN                 Index;

+

+  for (Index = 0; Index < 4; Index++) {

+    AsciiPrint ("%d", Ip->Addr[Index]);

+    if (Index < 3) {

+      AsciiPrint (".");

+    }

+  }

+}

+

+

+/**

+  This function is to display the IPv6 address.

+

+  @param[in]  Ip        The pointer to the IPv6 address.

+

+**/

+VOID

+PxeBcShowIp6Addr (

+  IN EFI_IPv6_ADDRESS   *Ip

+  )

+{

+  UINTN                 Index;

+

+  for (Index = 0; Index < 16; Index++) {

+

+    if (Ip->Addr[Index] != 0) {

+      AsciiPrint ("%x", Ip->Addr[Index]);

+    }

+    Index++;

+    if (Index > 15) {

+      return;

+    }

+    if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) {

+      AsciiPrint ("0");

+    }

+    AsciiPrint ("%x", Ip->Addr[Index]);

+    if (Index < 15) {

+      AsciiPrint (":");

+    }

+  }

+}

+

+

+/**

+  This function is to convert UINTN to ASCII string with the required formatting.

+

+  @param[in]  Number         Numeric value to be converted.

+  @param[in]  Buffer         The pointer to the buffer for ASCII string.

+  @param[in]  Length         The length of the required format.

+

+**/

+VOID

+PxeBcUintnToAscDecWithFormat (

+  IN UINTN                       Number,

+  IN UINT8                       *Buffer,

+  IN INTN                        Length

+  )

+{

+  UINTN                          Remainder;

+

+  while (Length > 0) {

+    Length--;

+    Remainder      = Number % 10;

+    Number        /= 10;

+    Buffer[Length] = (UINT8) ('0' + Remainder);

+  }

+}

+

+

+/**

+  This function is to convert a UINTN to a ASCII string, and return the

+  actual length of the buffer.

+

+  @param[in]  Number         Numeric value to be converted.

+  @param[in]  Buffer         The pointer to the buffer for ASCII string.

+

+  @return     Length         The actual length of the ASCII string.

+

+**/

+UINTN

+PxeBcUintnToAscDec (

+  IN UINTN               Number,

+  IN UINT8               *Buffer

+  )

+{

+  UINTN           Index;

+  UINTN           Length;

+  CHAR8           TempStr[64];

+

+  Index           = 63;

+  TempStr[Index]  = 0;

+

+  do {

+    Index--;

+    TempStr[Index] = (CHAR8) ('0' + (Number % 10));

+    Number         = (UINTN) (Number / 10);

+  } while (Number != 0);

+

+  AsciiStrCpy ((CHAR8 *) Buffer, &TempStr[Index]);

+

+  Length = AsciiStrLen ((CHAR8 *) Buffer);

+

+  return Length;

+}

+

+

+/**

+  This function is to convert unicode hex number to a UINT8.

+

+  @param[out]  Digit                   The converted UINT8 for output.

+  @param[in]   Char                    The unicode hex number to be converted.

+

+  @retval      EFI_SUCCESS             Successfully converted the unicode hex.

+  @retval      EFI_INVALID_PARAMETER   Failed to convert the unicode hex.

+

+**/

+EFI_STATUS

+PxeBcUniHexToUint8 (

+  OUT UINT8                *Digit,

+  IN  CHAR16               Char

+  )

+{

+  if ((Char >= L'0') && (Char <= L'9')) {

+    *Digit = (UINT8) (Char - L'0');

+    return EFI_SUCCESS;

+  }

+

+  if ((Char >= L'A') && (Char <= L'F')) {

+    *Digit = (UINT8) (Char - L'A' + 0x0A);

+    return EFI_SUCCESS;

+  }

+

+  if ((Char >= L'a') && (Char <= L'f')) {

+    *Digit = (UINT8) (Char - L'a' + 0x0A);

+    return EFI_SUCCESS;

+  }

+

+  return EFI_INVALID_PARAMETER;

+}

diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h
new file mode 100644
index 0000000..0d78205
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h
@@ -0,0 +1,491 @@
+/** @file

+  Support functions declaration for UefiPxeBc Driver.

+

+  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>

+

+  This program and the accompanying materials

+  are licensed and made available under the terms and conditions of the BSD License

+  which accompanies this distribution.  The full text of the license may be found at

+  http://opensource.org/licenses/bsd-license.php.

+

+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+

+**/

+

+#ifndef __EFI_PXEBC_SUPPORT_H__

+#define __EFI_PXEBC_SUPPORT_H__

+

+

+#define ICMP_DEST_UNREACHABLE      3

+#define ICMP_SOURCE_QUENCH         4

+#define ICMP_REDIRECT              5

+#define ICMP_ECHO_REQUEST          8

+#define ICMP_TIME_EXCEEDED         11

+#define ICMP_PARAMETER_PROBLEM     12

+

+

+/**

+  This function obtain the system guid and serial number from the smbios table.

+

+  @param[out]  SystemGuid     The pointer of returned system guid.

+

+  @retval EFI_SUCCESS         Successfully obtained the system guid.

+  @retval EFI_NOT_FOUND       Did not find the SMBIOS table.

+

+**/

+EFI_STATUS

+PxeBcGetSystemGuid (

+  OUT EFI_GUID              *SystemGuid

+  );

+

+

+/**

+  Flush the previous configration using the new station Ip address.

+

+  @param[in]   Private        Pointer to PxeBc private data.

+  @param[in]   StationIp      Pointer to the station Ip address.

+  @param[in]   SubnetMask     Pointer to the subnet mask address for v4.

+

+  @retval EFI_SUCCESS         Successfully flushed the previous config.

+  @retval Others              Failed to flush using the new station Ip.

+

+**/

+EFI_STATUS

+PxeBcFlushStaionIp (

+  PXEBC_PRIVATE_DATA       *Private,

+  EFI_IP_ADDRESS           *StationIp,

+  EFI_IP_ADDRESS           *SubnetMask     OPTIONAL

+  );

+

+

+/**

+  Notify callback function when an event is triggered.

+

+  @param[in]  Event           The triggered event.

+  @param[in]  Context         The opaque parameter to the function.

+

+**/

+VOID

+EFIAPI

+PxeBcCommonNotify (

+  IN EFI_EVENT           Event,

+  IN VOID                *Context

+  );

+

+

+/**

+  Perform arp resolution from the arp cache in PxeBcMode.

+

+  @param  Mode           Pointer to EFI_PXE_BASE_CODE_MODE.

+  @param  Ip4Addr        The Ip4 address for resolution.

+  @param  MacAddress     The resoluted MAC address if the resolution is successful.

+                         The value is undefined if resolution fails.

+

+  @retval TRUE           Found a matched entry.

+  @retval FALSE          Did not find a matched entry.

+

+**/

+BOOLEAN

+PxeBcCheckArpCache (

+  IN  EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN  EFI_IPv4_ADDRESS          *Ip4Addr,

+  OUT EFI_MAC_ADDRESS           *MacAddress

+  );

+

+

+/**

+  Update arp cache periodically.

+

+  @param  Event              Pointer to EFI_PXE_BC_PROTOCOL.

+  @param  Context            Context of the timer event.

+

+**/

+VOID

+EFIAPI

+PxeBcArpCacheUpdate (

+  IN EFI_EVENT    Event,

+  IN VOID         *Context

+  );

+

+

+/**

+  xxx

+

+  @param  Event                 The event signaled.

+  @param  Context               The context passed in by the event notifier.

+

+**/

+VOID

+EFIAPI

+PxeBcIcmpErrorUpdate (

+  IN EFI_EVENT             Event,

+  IN VOID                  *Context

+  );

+

+

+/**

+  xxx

+

+  @param  Event                 The event signaled.

+  @param  Context               The context passed in by the event notifier.

+

+**/

+VOID

+EFIAPI

+PxeBcIcmp6ErrorUpdate (

+  IN EFI_EVENT             Event,

+  IN VOID                  *Context

+  );

+

+

+/**

+  This function is to configure a UDPv4 instance for UdpWrite.

+

+  @param[in]       Udp4                 Pointer to EFI_UDP4_PROTOCOL.

+  @param[in]       StationIp            Pointer to the station address.

+  @param[in]       SubnetMask           Pointer to the subnet mask.

+  @param[in]       Gateway              Pointer to the gateway address.

+  @param[in, out]  SrcPort              Pointer to the source port.

+  @param[in]       DoNotFragment        The flag of DoNotFragment bit in the IPv4

+                                        packet.

+

+  @retval          EFI_SUCCESS          Successfully configured this instance.

+  @retval          Others               Failed to configure this instance.

+

+**/

+EFI_STATUS

+PxeBcConfigUdp4Write (

+  IN     EFI_UDP4_PROTOCOL  *Udp4,

+  IN     EFI_IPv4_ADDRESS   *StationIp,

+  IN     EFI_IPv4_ADDRESS   *SubnetMask,

+  IN     EFI_IPv4_ADDRESS   *Gateway,

+  IN OUT UINT16             *SrcPort,

+  IN     BOOLEAN            DoNotFragment

+  );

+

+

+/**

+  This function is to configure a UDPv6 instance for UdpWrite.

+

+  @param[in]       Udp6                 Pointer to EFI_UDP6_PROTOCOL.

+  @param[in]       StationIp            Pointer to the station address.

+  @param[in, out]  SrcPort              Pointer to the source port.

+

+  @retval          EFI_SUCCESS          Successfuly configured this instance.

+  @retval          Others               Failed to configure this instance.

+

+**/

+EFI_STATUS

+PxeBcConfigUdp6Write (

+  IN     EFI_UDP6_PROTOCOL  *Udp6,

+  IN     EFI_IPv6_ADDRESS   *StationIp,

+  IN OUT UINT16             *SrcPort

+  );

+

+/**

+  This function is to configure a UDPv4 instance for UdpWrite.

+

+  @param[in]       Udp4                 Pointer to EFI_UDP4_PROTOCOL.

+  @param[in]       Session              Pointer to the UDP4 session data.

+  @param[in]       TimeoutEvent         The event for timeout.

+  @param[in]       Gateway              Pointer to the gateway address.

+  @param[in]       HeaderSize           An optional field which may be set to the length of a header

+                                        at HeaderPtr to be prefixed to the data at BufferPtr.

+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be

+                                        prefixed to the data at BufferPtr.

+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.

+  @param[in]       BufferPtr            A pointer to the data to be written.

+

+  @retval          EFI_SUCCESS          Successfully sent out data with Udp4Write.

+  @retval          Others               Failed to send out data.

+

+**/

+EFI_STATUS

+PxeBcUdp4Write (

+  IN EFI_UDP4_PROTOCOL       *Udp4,

+  IN EFI_UDP4_SESSION_DATA   *Session,

+  IN EFI_EVENT               TimeoutEvent,

+  IN EFI_IPv4_ADDRESS        *Gateway      OPTIONAL,

+  IN UINTN                   *HeaderSize   OPTIONAL,

+  IN VOID                    *HeaderPtr    OPTIONAL,

+  IN UINTN                   *BufferSize,

+  IN VOID                    *BufferPtr

+  );

+

+

+/**

+  This function is to configure a UDPv6 instance for UdpWrite.

+

+  @param[in]       Udp6                 Pointer to EFI_UDP6_PROTOCOL.

+  @param[in]       Session              Pointer to the UDP6 session data.

+  @param[in]       TimeoutEvent         The event for timeout.

+  @param[in]       HeaderSize           An optional field which may be set to the length of a header

+                                        at HeaderPtr to be prefixed to the data at BufferPtr.

+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be

+                                        prefixed to the data at BufferPtr.

+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.

+  @param[in]       BufferPtr            A pointer to the data to be written.

+

+  @retval          EFI_SUCCESS          Successfully to send out data with Udp6Write.

+  @retval          Others               Failed to send out data.

+

+**/

+EFI_STATUS

+PxeBcUdp6Write (

+  IN EFI_UDP6_PROTOCOL       *Udp6,

+  IN EFI_UDP6_SESSION_DATA   *Session,

+  IN EFI_EVENT               TimeoutEvent,

+  IN UINTN                   *HeaderSize   OPTIONAL,

+  IN VOID                    *HeaderPtr    OPTIONAL,

+  IN UINTN                   *BufferSize,

+  IN VOID                    *BufferPtr

+  );

+

+

+/**

+  Check the received packet with the Ip filter.

+

+  @param[in]  Mode                Pointer to mode data of PxeBc.

+  @param[in]  Session             Pointer to the current UDPv4 session.

+  @param[in]  OpFlags             Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Succesfully passed the Ip filter.

+  @retval     FALSE               Failed to pass the Ip filter.

+

+**/

+BOOLEAN

+PxeBcCheckByIpFilter (

+  IN EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN VOID                      *Session,

+  IN UINT16                    OpFlags

+  );

+

+

+/**

+  Filter the received packet with the destination Ip.

+

+  @param[in]       Mode           Pointer to mode data of PxeBc.

+  @param[in]       Session        Pointer to the current UDPv4 session.

+  @param[in, out]  DestIp         Pointer to the dest Ip address.

+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Succesfully passed the IPv4 filter.

+  @retval     FALSE               Failed to pass the IPv4 filter.

+

+**/

+BOOLEAN

+PxeBcCheckByDestIp (

+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN     VOID                      *Session,

+  IN OUT EFI_IP_ADDRESS            *DestIp,

+  IN     UINT16                    OpFlags

+  );

+

+

+/**

+  Check the received packet with the destination port.

+

+  @param[in]       PxeBcMode      Pointer to mode data of PxeBc.

+  @param[in]       Session        Pointer to the current UDPv4 session.

+  @param[in, out]  DestPort       Pointer to the destination port.

+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Succesfully passed the IPv4 filter.

+  @retval     FALSE               Failed to pass the IPv4 filter.

+

+**/

+BOOLEAN

+PxeBcCheckByDestPort (

+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN     VOID                      *Session,

+  IN OUT UINT16                    *DestPort,

+  IN     UINT16                    OpFlags

+  );

+

+

+/**

+  Filter the received packet with the source Ip.

+

+  @param[in]       Mode           Pointer to mode data of PxeBc.

+  @param[in]       Session        Pointer to the current UDPv4 session.

+  @param[in, out]  SrcIp          Pointer to the source Ip address.

+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Succesfully passed the IPv4 filter.

+  @retval     FALSE               Failed to pass the IPv4 filter.

+

+**/

+BOOLEAN

+PxeBcFilterBySrcIp (

+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN     VOID                      *Session,

+  IN OUT EFI_IP_ADDRESS            *SrcIp,

+  IN     UINT16                    OpFlags

+  );

+

+

+/**

+  Filter the received packet with the source port.

+

+  @param[in]       Mode           Pointer to mode data of PxeBc.

+  @param[in]       Session        Pointer to the current UDPv4 session.

+  @param[in, out]  SrcPort        Pointer to the source port.

+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.

+

+  @retval     TRUE                Succesfully passed the IPv4 filter.

+  @retval     FALSE               Failed to pass the IPv4 filter.

+

+**/

+BOOLEAN

+PxeBcFilterBySrcPort (

+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,

+  IN     VOID                      *Session,

+  IN OUT UINT16                    *SrcPort,

+  IN     UINT16                    OpFlags

+  );

+

+

+/**

+  This function is to receive packet with Udp4Read.

+

+  @param[in]       Udp4                 Pointer to EFI_UDP4_PROTOCOL.

+  @param[in]       Token                Pointer to EFI_UDP4_COMPLETION_TOKEN.

+  @param[in]       Mode                 Pointer to EFI_PXE_BASE_CODE_MODE.

+  @param[in]       TimeoutEvent         The event for timeout.

+  @param[in]       OpFlags              The UDP operation flags.

+  @param[in]       IsDone               Pointer to IsDone flag.

+  @param[out]      IsMatched            Pointer to IsMatched flag.

+  @param[in, out]  DestIp               Pointer to destination address.

+  @param[in, out]  DestPort             Pointer to destination port.

+  @param[in, out]  SrcIp                Pointer to source address.

+  @param[in, out]  SrcPort              Pointer to source port.

+

+  @retval          EFI_SUCCESS          Successfully read data with Udp4.

+  @retval          Others               Failed to send out data.

+

+**/

+EFI_STATUS

+PxeBcUdp4Read (

+  IN     EFI_UDP4_PROTOCOL            *Udp4,

+  IN     EFI_UDP4_COMPLETION_TOKEN    *Token,

+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,

+  IN     EFI_EVENT                    TimeoutEvent,

+  IN     UINT16                       OpFlags,

+  IN     BOOLEAN                      *IsDone,

+     OUT BOOLEAN                      *IsMatched,

+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,

+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL

+  );

+

+

+/**

+  This function is to receive packet with Udp6Read.

+

+  @param[in]       Udp6                 Pointer to EFI_UDP6_PROTOCOL.

+  @param[in]       Token                Pointer to EFI_UDP6_COMPLETION_TOKEN.

+  @param[in]       Mode                 Pointer to EFI_PXE_BASE_CODE_MODE.

+  @param[in]       TimeoutEvent         The event for timeout.

+  @param[in]       OpFlags              The UDP operation flags.

+  @param[in]       IsDone               Pointer to IsDone flag.

+  @param[out]      IsMatched            Pointer to IsMatched flag.

+  @param[in, out]  DestIp               Pointer to destination address.

+  @param[in, out]  DestPort             Pointer to destination port.

+  @param[in, out]  SrcIp                Pointer to source address.

+  @param[in, out]  SrcPort              Pointer to source port.

+

+  @retval          EFI_SUCCESS          Successfully read data with Udp6.

+  @retval          Others               Failed to send out data.

+

+**/

+EFI_STATUS

+PxeBcUdp6Read (

+  IN     EFI_UDP6_PROTOCOL            *Udp6,

+  IN     EFI_UDP6_COMPLETION_TOKEN    *Token,

+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,

+  IN     EFI_EVENT                    TimeoutEvent,

+  IN     UINT16                       OpFlags,

+  IN     BOOLEAN                      *IsDone,

+     OUT BOOLEAN                      *IsMatched,

+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,

+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,

+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL

+  );

+

+

+/**

+  This function is to display the IPv4 address.

+

+  @param[in]  Ip        Pointer to the IPv4 address.

+

+**/

+VOID

+PxeBcShowIp4Addr (

+  IN EFI_IPv4_ADDRESS   *Ip

+  );

+

+

+/**

+  This function is to display the IPv6 address.

+

+  @param[in]  Ip        Pointer to the IPv6 address.

+

+**/

+VOID

+PxeBcShowIp6Addr (

+  IN EFI_IPv6_ADDRESS   *Ip

+  );

+

+

+/**

+  This function is to convert UINTN to ASCII string with required format.

+

+  @param[in]  Number         Numeric value to be converted.

+  @param[in]  Buffer         Pointer to the buffer for ASCII string.

+  @param[in]  Length         Length of the required format.

+

+**/

+VOID

+PxeBcUintnToAscDecWithFormat (

+  IN UINTN                       Number,

+  IN UINT8                       *Buffer,

+  IN INTN                        Length

+  );

+

+

+/**

+  This function is to convert a UINTN to a ASCII string, and return the

+  actual length of the buffer.

+

+  @param[in]  Number         Numeric value to be converted.

+  @param[in]  Buffer         Pointer to the buffer for ASCII string.

+

+  @return     Length         The actual length of the ASCII string.

+

+**/

+UINTN

+PxeBcUintnToAscDec (

+  IN UINTN               Number,

+  IN UINT8               *Buffer

+  );

+

+/**

+  This function is to convert unicode hex number to a UINT8.

+

+  @param[out]  Digit                   The converted UINT8 for output.

+  @param[in]   Char                    The unicode hex number to be converted.

+

+  @retval      EFI_SUCCESS             Successfully converted the unicode hex.

+  @retval      EFI_INVALID_PARAMETER   Failed to convert the unicode hex.

+

+**/

+EFI_STATUS

+PxeBcUniHexToUint8 (

+  OUT UINT8                *Digit,

+  IN  CHAR16               Char

+  );

+

+#endif

diff --git a/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf b/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
new file mode 100644
index 0000000..1e690b8
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
@@ -0,0 +1,98 @@
+## @file

+#  Component name for module PxeBc

+#

+#  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>

+#

+#  This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php.

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+##

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = UefiPxeBcDxe

+  FILE_GUID                      = B95E9FDA-26DE-48d2-8807-1F9107AC5E3A

+  MODULE_TYPE                    = UEFI_DRIVER

+  VERSION_STRING                 = 1.0

+  ENTRY_POINT                    = PxeBcDriverEntryPoint

+  UNLOAD_IMAGE                   = NetLibDefaultUnload

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF

+#

+

+[Sources]

+  ComponentName.c

+  PxeBcDriver.c

+  PxeBcDriver.h

+  PxeBcImpl.c

+  PxeBcImpl.h

+  PxeBcBoot.c

+  PxeBcBoot.h

+  PxeBcDhcp6.c

+  PxeBcDhcp6.h

+  PxeBcDhcp4.c

+  PxeBcDhcp4.h

+  PxeBcMtftp.c

+  PxeBcMtftp.h

+  PxeBcSupport.c

+  PxeBcSupport.h

+

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+

+

+[LibraryClasses]

+  BaseLib

+  UefiLib

+  UefiBootServicesTableLib

+  UefiDriverEntryPoint

+  BaseMemoryLib

+  MemoryAllocationLib

+  DebugLib

+  NetLib

+  DpcLib

+  DevicePathLib

+  PcdLib

+

+

+[Guids]

+  gEfiSmbiosTableGuid

+

+

+[Protocols]

+  gEfiDevicePathProtocolGuid

+  gEfiNetworkInterfaceIdentifierProtocolGuid_31

+  gEfiArpServiceBindingProtocolGuid

+  gEfiArpProtocolGuid

+  gEfiIp4ServiceBindingProtocolGuid

+  gEfiIp4ProtocolGuid

+  gEfiIp6ServiceBindingProtocolGuid

+  gEfiIp6ProtocolGuid

+  gEfiIp6ConfigProtocolGuid

+  gEfiUdp4ServiceBindingProtocolGuid

+  gEfiUdp4ProtocolGuid

+  gEfiMtftp4ServiceBindingProtocolGuid

+  gEfiMtftp4ProtocolGuid

+  gEfiDhcp4ServiceBindingProtocolGuid

+  gEfiDhcp4ProtocolGuid

+  gEfiUdp6ServiceBindingProtocolGuid

+  gEfiUdp6ProtocolGuid

+  gEfiMtftp6ServiceBindingProtocolGuid

+  gEfiMtftp6ProtocolGuid

+  gEfiDhcp6ServiceBindingProtocolGuid

+  gEfiDhcp6ProtocolGuid

+  gEfiPxeBaseCodeCallbackProtocolGuid

+  gEfiPxeBaseCodeProtocolGuid

+  gEfiLoadFileProtocolGuid

+

+[Pcd]

+  gEfiMdeModulePkgTokenSpaceGuid.PcdTftpBlockSize     ## CONSUMES