]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/HttpBootDxe/HttpBootDhcp6.c
NetworkPkg:Enable Http Boot over Ipv6 stack
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootDhcp6.c
diff --git a/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c b/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c
new file mode 100644 (file)
index 0000000..e5cf894
--- /dev/null
@@ -0,0 +1,984 @@
+/** @file\r
+  Functions implementation related with DHCPv6 for HTTP boot driver.\r
+\r
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials are licensed and made available under \r
+the terms and conditions of the BSD License that accompanies this distribution.  \r
+The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php.                                          \r
+    \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "HttpBootDxe.h"\r
+\r
+/**\r
+  Build the options buffer for the DHCPv6 request packet.\r
+\r
+  @param[in]  Private             The pointer to HTTP BOOT driver private data.\r
+  @param[out] OptList             The pointer to the option pointer array.\r
+  @param[in]  Buffer              The pointer to the buffer to contain the option list.\r
+\r
+  @return     Index               The count of the built-in options.\r
+\r
+**/\r
+UINT32\r
+HttpBootBuildDhcp6Options (\r
+  IN  HTTP_BOOT_PRIVATE_DATA       *Private,\r
+  OUT EFI_DHCP6_PACKET_OPTION      **OptList,\r
+  IN  UINT8                        *Buffer\r
+  )\r
+{\r
+  HTTP_BOOT_DHCP6_OPTION_ENTRY     OptEnt;\r
+  UINT16                           Value;\r
+  UINT32                           Index;\r
+\r
+  Index      = 0;\r
+  OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
+\r
+  //\r
+  // Append client option request option\r
+  //\r
+  OptList[Index]->OpCode     = HTONS (HTTP_BOOT_DHCP6_OPT_ORO);\r
+  OptList[Index]->OpLen      = HTONS (8);\r
+  OptEnt.Oro                 = (HTTP_BOOT_DHCP6_OPTION_ORO *) OptList[Index]->Data;\r
+  OptEnt.Oro->OpCode[0]      = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL);\r
+  OptEnt.Oro->OpCode[1]      = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM);\r
+  OptEnt.Oro->OpCode[2]      = HTONS(HTTP_BOOT_DHCP6_OPT_DNS_SERVERS);\r
+  OptEnt.Oro->OpCode[3]      = HTONS(HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS);\r
+  Index++;\r
+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
+\r
+  //\r
+  // Append client network device interface option\r
+  //\r
+  OptList[Index]->OpCode     = HTONS (HTTP_BOOT_DHCP6_OPT_UNDI);\r
+  OptList[Index]->OpLen      = HTONS ((UINT16)3);\r
+  OptEnt.Undi                = (HTTP_BOOT_DHCP6_OPTION_UNDI *) OptList[Index]->Data;\r
+\r
+  if (Private->Nii != NULL) {\r
+    OptEnt.Undi->Type        = Private->Nii->Type;\r
+    OptEnt.Undi->MajorVer    = Private->Nii->MajorVer;\r
+    OptEnt.Undi->MinorVer    = Private->Nii->MinorVer;\r
+  } else {\r
+    OptEnt.Undi->Type        = DEFAULT_UNDI_TYPE;\r
+    OptEnt.Undi->MajorVer    = DEFAULT_UNDI_MAJOR;\r
+    OptEnt.Undi->MinorVer    = DEFAULT_UNDI_MINOR;\r
+  }\r
+\r
+  Index++;\r
+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
+\r
+  //\r
+  // Append client system architecture option\r
+  //\r
+  OptList[Index]->OpCode     = HTONS (HTTP_BOOT_DHCP6_OPT_ARCH);\r
+  OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_ARCH));\r
+  OptEnt.Arch                = (HTTP_BOOT_DHCP6_OPTION_ARCH *) OptList[Index]->Data;\r
+  Value                      = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE);\r
+  CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));\r
+  Index++;\r
+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
+\r
+  //\r
+  // Append vendor class identify option.\r
+  //\r
+  OptList[Index]->OpCode       = HTONS (HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS);\r
+  OptList[Index]->OpLen        = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS));\r
+  OptEnt.VendorClass           = (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;\r
+  OptEnt.VendorClass->Vendor   = HTONL (HTTP_BOOT_DHCP6_ENTERPRISE_NUM);\r
+  OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (HTTP_BOOT_CLASS_ID));\r
+  CopyMem (\r
+    &OptEnt.VendorClass->ClassId,\r
+    DEFAULT_CLASS_ID_DATA,\r
+    sizeof (HTTP_BOOT_CLASS_ID)\r
+    );\r
+  HttpBootUintnToAscDecWithFormat (\r
+    EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE,\r
+    OptEnt.VendorClass->ClassId.ArchitectureType,\r
+    sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)\r
+    );\r
+\r
+  if (Private->Nii != NULL) {\r
+    CopyMem (\r
+      OptEnt.VendorClass->ClassId.InterfaceName,\r
+      Private->Nii->StringId,\r
+      sizeof (OptEnt.VendorClass->ClassId.InterfaceName)\r
+      );\r
+    HttpBootUintnToAscDecWithFormat (\r
+      Private->Nii->MajorVer,\r
+      OptEnt.VendorClass->ClassId.UndiMajor,\r
+      sizeof (OptEnt.VendorClass->ClassId.UndiMajor)\r
+      );\r
+    HttpBootUintnToAscDecWithFormat (\r
+      Private->Nii->MinorVer,\r
+      OptEnt.VendorClass->ClassId.UndiMinor,\r
+      sizeof (OptEnt.VendorClass->ClassId.UndiMinor)\r
+      );\r
+  }\r
+\r
+  Index++;\r
+\r
+  return Index;\r
+}\r
+\r
+/**\r
+  Parse out a DHCPv6 option by OptTag, and find the position in buffer.\r
+\r
+  @param[in]  Buffer        The pointer to the option buffer.\r
+  @param[in]  Length        Length of the option buffer.\r
+  @param[in]  OptTag        The required option tag.\r
+\r
+  @retval     NULL          Failed to parse the required option.\r
+  @retval     Others        The postion of the required option in buffer.\r
+\r
+**/\r
+EFI_DHCP6_PACKET_OPTION *\r
+HttpBootParseDhcp6Options (\r
+  IN UINT8                       *Buffer,\r
+  IN UINT32                      Length,\r
+  IN UINT16                      OptTag\r
+  )\r
+{\r
+  EFI_DHCP6_PACKET_OPTION        *Option;\r
+  UINT32                         Offset;\r
+\r
+  Option  = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
+  Offset  = 0;\r
+\r
+  //\r
+  // OpLen and OpCode here are both stored in network order.\r
+  //\r
+  while (Offset < Length) {\r
+\r
+    if (NTOHS (Option->OpCode) == OptTag) {\r
+\r
+      return Option;\r
+    }\r
+\r
+    Offset += (NTOHS(Option->OpLen) + 4);\r
+    Option  = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);\r
+  }\r
+\r
+  return NULL;\r
+\r
+}\r
+\r
+/**\r
+  Parse the cached DHCPv6 packet, including all the options.\r
+\r
+  @param[in]  Cache6           The pointer to a cached DHCPv6 packet.\r
+\r
+  @retval     EFI_SUCCESS      Parsed the DHCPv6 packet successfully.\r
+  @retval     EFI_DEVICE_ERROR Failed to parse and invalid the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootParseDhcp6Packet (\r
+  IN  HTTP_BOOT_DHCP6_PACKET_CACHE    *Cache6\r
+  )\r
+{\r
+  EFI_DHCP6_PACKET               *Offer;\r
+  EFI_DHCP6_PACKET_OPTION        **Options;\r
+  EFI_DHCP6_PACKET_OPTION        *Option;\r
+  HTTP_BOOT_OFFER_TYPE           OfferType;\r
+  EFI_IPv6_ADDRESS               IpAddr;\r
+  BOOLEAN                        IsProxyOffer;\r
+  BOOLEAN                        IsHttpOffer;\r
+  BOOLEAN                        IsDnsOffer;\r
+  BOOLEAN                        IpExpressedUri;\r
+  EFI_STATUS                     Status;\r
+  UINT32                         Offset;\r
+  UINT32                         Length;\r
+  \r
+  IsDnsOffer     = FALSE;\r
+  IpExpressedUri = FALSE;\r
+  IsProxyOffer   = TRUE;\r
+  IsHttpOffer    = FALSE;\r
+  Offer        = &Cache6->Packet.Offer;\r
+  Options      = Cache6->OptList;\r
+  \r
+  ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));\r
+\r
+  Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);\r
+  Offset  = 0;\r
+  Length  = GET_DHCP6_OPTION_SIZE (Offer);\r
+\r
+  //\r
+  // OpLen and OpCode here are both stored in network order, since they are from original packet.\r
+  //\r
+  while (Offset < Length) {\r
+\r
+    if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_IA_NA) {\r
+      Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option;\r
+    } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL) {\r
+      //\r
+      // The server sends this option to inform the client about an URL to a boot file.\r
+      //\r
+      Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] = Option;\r
+    } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM) {\r
+      Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option;\r
+    } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS) {\r
+      Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option;\r
+    } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_DNS_SERVERS) {\r
+      Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER] = Option;\r
+    }\r
+\r
+    Offset += (NTOHS (Option->OpLen) + 4);\r
+    Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);\r
+  }\r
+  //\r
+  // The offer with assigned client address is NOT a proxy offer.\r
+  // An ia_na option, embeded with valid ia_addr option and a status_code of success.\r
+  //\r
+  Option = Options[HTTP_BOOT_DHCP6_IDX_IA_NA];\r
+  if (Option != NULL) {\r
+    Option = HttpBootParseDhcp6Options (\r
+               Option->Data + 12,\r
+               NTOHS (Option->OpLen),\r
+               HTTP_BOOT_DHCP6_OPT_STATUS_CODE\r
+               );\r
+    if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {\r
+      IsProxyOffer = FALSE;\r
+    }\r
+  }\r
+\r
+  //\r
+  // The offer with "HTTPClient" is a Http offer.\r
+  //\r
+  Option = Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS];\r
+\r
+  if (Option != NULL &&\r
+      NTOHS(Option->OpLen) >= 10 &&\r
+      CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 10) == 0) {\r
+      IsHttpOffer = TRUE;\r
+  }\r
+\r
+  //\r
+  // The offer with Domain Server is a DNS offer.\r
+  //\r
+  Option = Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];\r
+  if (Option != NULL) {\r
+    IsDnsOffer = TRUE;\r
+  }\r
+\r
+  //\r
+  // Http offer must have a boot URI.\r
+  //\r
+  if (IsHttpOffer && Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
\r
+  //\r
+  // Try to retrieve the IP of HTTP server from URI. \r
+  //\r
+  if (IsHttpOffer) {\r
+    Status = HttpParseUrl (\r
+               (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
+               (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
+               FALSE,\r
+               &Cache6->UriParser\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    Status = HttpUrlGetIp6 (\r
+               (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
+               Cache6->UriParser,\r
+               &IpAddr\r
+               );\r
+    IpExpressedUri = !EFI_ERROR (Status);\r
+  }\r
+\r
+  //\r
+  // Determine offer type of the DHCPv6 packet.\r
+  //\r
+  if (IsHttpOffer) {\r
+    if (IpExpressedUri) {\r
+      OfferType = IsProxyOffer ? HttpOfferTypeProxyIpUri : HttpOfferTypeDhcpIpUri;\r
+    } else {\r
+      if (!IsProxyOffer) {\r
+        OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri;\r
+      } else {\r
+        OfferType = HttpOfferTypeProxyNameUri;\r
+      }\r
+    }\r
+\r
+  } else {\r
+    if (!IsProxyOffer) {\r
+      OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly;\r
+    } else {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+  \r
+  Cache6->OfferType = OfferType;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Cache the DHCPv6 packet.\r
+\r
+  @param[in]  Dst          The pointer to the cache buffer for DHCPv6 packet.\r
+  @param[in]  Src          The pointer to the DHCPv6 packet to be cached.\r
+\r
+**/\r
+VOID\r
+HttpBootCacheDhcp6Packet (\r
+  IN EFI_DHCP6_PACKET          *Dst,\r
+  IN EFI_DHCP6_PACKET          *Src\r
+  )\r
+{\r
+  ASSERT (Dst->Size >= Src->Length);\r
+\r
+  CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);\r
+  Dst->Length = Src->Length;\r
+}\r
+\r
+/**\r
+  Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.\r
+\r
+  @param[in]  Private               The pointer to HTTP_BOOT_PRIVATE_DATA.\r
+  @param[in]  RcvdOffer             The pointer to the received offer packet.\r
+\r
+**/\r
+VOID\r
+HttpBootCacheDhcp6Offer (\r
+  IN HTTP_BOOT_PRIVATE_DATA  *Private,\r
+  IN EFI_DHCP6_PACKET        *RcvdOffer\r
+  )\r
+{\r
+  HTTP_BOOT_DHCP6_PACKET_CACHE   *Cache6;\r
+  EFI_DHCP6_PACKET               *Offer;\r
+  HTTP_BOOT_OFFER_TYPE           OfferType;\r
+\r
+  Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;\r
+  Offer  = &Cache6->Packet.Offer;\r
+\r
+  //\r
+  // Cache the content of DHCPv6 packet firstly.\r
+  //\r
+  HttpBootCacheDhcp6Packet(Offer, RcvdOffer);\r
+\r
+  //\r
+  // Validate the DHCPv6 packet, and parse the options and offer type.\r
+  //\r
+  if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.\r
+  //\r
+  OfferType = Cache6->OfferType;\r
+  ASSERT (OfferType < HttpOfferTypeMax);\r
+  ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM);\r
+  Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
+  Private->OfferCount[OfferType]++;\r
+  Private->OfferNum++;  \r
+}\r
+\r
+/**\r
+  EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver\r
+  to intercept events that occurred in the configuration process.\r
+\r
+  @param[in]  This              The pointer to the EFI DHCPv6 Protocol.\r
+  @param[in]  Context           The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().\r
+  @param[in]  CurrentState      The current operational state of the EFI DHCPv Protocol driver.\r
+  @param[in]  Dhcp6Event        The event that occurs in the current state, which usually means a\r
+                                state transition.\r
+  @param[in]  Packet            The DHCPv6 packet that is going to be sent or was already received.\r
+  @param[out] NewPacket         The packet that is used to replace the Packet above.\r
+\r
+  @retval EFI_SUCCESS           Told the EFI DHCPv6 Protocol driver to continue the DHCP process.\r
+  @retval EFI_NOT_READY         Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol\r
+                                driver will continue to wait for more packets.\r
+  @retval EFI_ABORTED           Told the EFI DHCPv6 Protocol driver to abort the current process.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpBootDhcp6CallBack (\r
+  IN  EFI_DHCP6_PROTOCOL           *This,\r
+  IN  VOID                         *Context,\r
+  IN  EFI_DHCP6_STATE              CurrentState,\r
+  IN  EFI_DHCP6_EVENT              Dhcp6Event,\r
+  IN  EFI_DHCP6_PACKET             *Packet,\r
+  OUT EFI_DHCP6_PACKET             **NewPacket     OPTIONAL\r
+  )\r
+{\r
+   HTTP_BOOT_PRIVATE_DATA          *Private;\r
+   EFI_DHCP6_PACKET                *SelectAd;\r
+   EFI_STATUS                      Status;\r
+   if ((Dhcp6Event != Dhcp6RcvdAdvertise) && (Dhcp6Event != Dhcp6SelectAdvertise)) {\r
+     return EFI_SUCCESS;\r
+   }\r
+\r
+   ASSERT (Packet != NULL);\r
+   \r
+   Private     = (HTTP_BOOT_PRIVATE_DATA *) Context;\r
+   Status = EFI_SUCCESS;\r
+   switch (Dhcp6Event) {\r
+    \r
+   case Dhcp6RcvdAdvertise:\r
+     Status = EFI_NOT_READY;\r
+     if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {\r
+       //\r
+       // Cache the dhcp offers to OfferBuffer[] for select later, and record\r
+       // the OfferIndex and OfferCount.\r
+       //\r
+       HttpBootCacheDhcp6Offer (Private, Packet);\r
+     }\r
+     break;\r
+\r
+   case Dhcp6SelectAdvertise:\r
+     //\r
+     // Select offer by the default policy or by order, and record the SelectIndex\r
+     // and SelectProxyType.\r
+     //\r
+     HttpBootSelectDhcpOffer (Private);\r
+\r
+     if (Private->SelectIndex == 0) {\r
+       Status = EFI_ABORTED;\r
+     } else {\r
+       ASSERT (NewPacket != NULL);\r
+       SelectAd   = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;\r
+       *NewPacket = AllocateZeroPool (SelectAd->Size);\r
+       ASSERT (*NewPacket != NULL);\r
+       CopyMem (*NewPacket, SelectAd, SelectAd->Size);\r
+     }\r
+     break;\r
+     \r
+   default:\r
+     break;\r
+  }\r
+\r
+  return Status;   \r
+}\r
+\r
+/**\r
+  Check whether IP driver could route the message which will be sent to ServerIp address.\r
+  \r
+  This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid\r
+  route is found in IP6 route table, the address will be filed in GatewayAddr and return.\r
+\r
+  @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.\r
+  @param[in]  TimeOutInSecond     Timeout value in seconds.\r
+  @param[out] GatewayAddr         Pointer to store the gateway IP address.\r
+\r
+  @retval     EFI_SUCCESS         Found a valid gateway address successfully.\r
+  @retval     EFI_TIMEOUT         The operation is time out.\r
+  @retval     Other               Unexpect error happened.\r
+  \r
+**/\r
+EFI_STATUS\r
+HttpBootCheckRouteTable (\r
+  IN  HTTP_BOOT_PRIVATE_DATA        *Private,\r
+  IN  UINTN                         TimeOutInSecond,\r
+  OUT EFI_IPv6_ADDRESS              *GatewayAddr\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  EFI_IP6_PROTOCOL                 *Ip6;\r
+  EFI_IP6_MODE_DATA                Ip6ModeData;\r
+  UINTN                            Index;\r
+  EFI_EVENT                        TimeOutEvt;\r
+  UINTN                            RetryCount;\r
+  BOOLEAN                          GatewayIsFound;\r
+\r
+  ASSERT (GatewayAddr != NULL);\r
+  ASSERT (Private != NULL);\r
+\r
+  Ip6            = Private->Ip6;\r
+  GatewayIsFound = FALSE;\r
+  RetryCount     = 0;\r
+  TimeOutEvt     = NULL;\r
+  Status         = EFI_SUCCESS;\r
+  ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  while (TRUE) {\r
+    Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+    \r
+    //\r
+    // Find out the gateway address which can route the message which send to ServerIp.\r
+    //\r
+    for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {\r
+      if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {\r
+        IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);\r
+        GatewayIsFound = TRUE;\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (Ip6ModeData.AddressList != NULL) {\r
+      FreePool (Ip6ModeData.AddressList);\r
+    }\r
+    if (Ip6ModeData.GroupTable != NULL) {\r
+      FreePool (Ip6ModeData.GroupTable);\r
+    }\r
+    if (Ip6ModeData.RouteTable != NULL) {\r
+      FreePool (Ip6ModeData.RouteTable);\r
+    }\r
+    if (Ip6ModeData.NeighborCache != NULL) {\r
+      FreePool (Ip6ModeData.NeighborCache);\r
+    }\r
+    if (Ip6ModeData.PrefixTable != NULL) {\r
+      FreePool (Ip6ModeData.PrefixTable);\r
+    }\r
+    if (Ip6ModeData.IcmpTypeList != NULL) {\r
+      FreePool (Ip6ModeData.IcmpTypeList);\r
+    }\r
+    \r
+    if (GatewayIsFound || RetryCount == TimeOutInSecond) {\r
+      break;\r
+    }\r
+    \r
+    RetryCount++;\r
+    \r
+    //\r
+    // Delay 1 second then recheck it again.\r
+    //\r
+    if (TimeOutEvt == NULL) {\r
+      Status = gBS->CreateEvent (\r
+                      EVT_TIMER,\r
+                      TPL_CALLBACK,\r
+                      NULL,\r
+                      NULL,\r
+                      &TimeOutEvt\r
+                      );\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+    }\r
+\r
+    Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+    while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
+      Ip6->Poll (Ip6);\r
+    }\r
+  }\r
+  \r
+ON_EXIT:\r
+  if (TimeOutEvt != NULL) {\r
+    gBS->CloseEvent (TimeOutEvt);\r
+  }\r
+  \r
+  if (GatewayIsFound) {\r
+    Status = EFI_SUCCESS;\r
+  } else if (RetryCount == TimeOutInSecond) {\r
+    Status = EFI_TIMEOUT;\r
+  }\r
+\r
+  return Status; \r
+}\r
+\r
+/**\r
+  Set the IP6 policy to Automatic.\r
+\r
+  @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.\r
+\r
+  @retval     EFI_SUCCESS         Switch the IP policy succesfully.\r
+  @retval     Others              Unexpect error happened.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootSetIp6Policy (\r
+  IN HTTP_BOOT_PRIVATE_DATA        *Private\r
+  )\r
+{\r
+  EFI_IP6_CONFIG_POLICY            Policy;\r
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Config;\r
+  EFI_STATUS                       Status;\r
+  UINTN                            DataSize;\r
+\r
+  Ip6Config       = Private->Ip6Config;\r
+  DataSize        = sizeof (EFI_IP6_CONFIG_POLICY);\r
+  \r
+  //\r
+  // Get and store the current policy of IP6 driver.\r
+  //\r
+  Status = Ip6Config->GetData (\r
+                        Ip6Config,\r
+                        Ip6ConfigDataTypePolicy,\r
+                        &DataSize,\r
+                        &Policy\r
+                        );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Policy == Ip6ConfigPolicyManual) {\r
+    Policy = Ip6ConfigPolicyAutomatic;\r
+    Status = Ip6Config->SetData (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypePolicy,\r
+                          sizeof(EFI_IP6_CONFIG_POLICY),\r
+                          &Policy\r
+                          );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function will register the default DNS addresses to the network device.\r
+  \r
+  @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.\r
+  @param[in]  DataLength          Size of the buffer pointed to by DnsServerData in bytes.\r
+  @param[in]  DnsServerData       Point a list of DNS server address in an array\r
+                                  of EFI_IPv6_ADDRESS instances.\r
+\r
+  @retval     EFI_SUCCESS         The DNS configuration has been configured successfully.\r
+  @retval     Others              Failed to configure the address.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootSetIp6Dns (\r
+  IN HTTP_BOOT_PRIVATE_DATA         *Private,\r
+  IN UINTN                          DataLength,\r
+  IN VOID                           *DnsServerData\r
+  )\r
+{\r
+  EFI_IP6_CONFIG_PROTOCOL        *Ip6Config;\r
+  \r
+  ASSERT (Private->UsingIpv6);\r
+\r
+  Ip6Config = Private->Ip6Config;\r
+  \r
+  return Ip6Config->SetData (\r
+                      Ip6Config,\r
+                      Ip6ConfigDataTypeDnsServer,\r
+                      DataLength,\r
+                      DnsServerData\r
+                      );\r
+}\r
+\r
+/**\r
+  This function will register the IPv6 gateway address to the network device.\r
+  \r
+  @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.\r
+\r
+  @retval     EFI_SUCCESS         The new IP configuration has been configured successfully.\r
+  @retval     Others              Failed to configure the address.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootSetIp6Gateway (\r
+  IN HTTP_BOOT_PRIVATE_DATA         *Private\r
+  )\r
+{\r
+  EFI_IP6_CONFIG_PROTOCOL           *Ip6Config;\r
+  EFI_STATUS                        Status;\r
+\r
+  ASSERT (Private->UsingIpv6);\r
+  Ip6Config = Private->Ip6Config;\r
\r
+  //\r
+  // Set the default gateway address. \r
+  //\r
+  if (!Private->NoGateway && !NetIp6IsUnspecifiedAddr (&Private->GatewayIp.v6)) {\r
+    Status = Ip6Config->SetData (\r
+                          Ip6Config,\r
+                          Ip6ConfigDataTypeGateway,\r
+                          sizeof (EFI_IPv6_ADDRESS),\r
+                          &Private->GatewayIp.v6\r
+                          );\r
+    if (EFI_ERROR(Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This function will register the station IP address.\r
+  \r
+  @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.\r
+\r
+  @retval     EFI_SUCCESS         The new IP address has been configured successfully.\r
+  @retval     Others              Failed to configure the address.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootSetIp6Address (\r
+  IN HTTP_BOOT_PRIVATE_DATA         *Private\r
+)\r
+{\r
+  EFI_STATUS                         Status;\r
+  EFI_IP6_PROTOCOL                   *Ip6;\r
+  EFI_IP6_CONFIG_PROTOCOL            *Ip6Cfg;\r
+  EFI_IP6_CONFIG_POLICY              Policy;\r
+  EFI_IP6_CONFIG_MANUAL_ADDRESS      CfgAddr;\r
+  EFI_IPv6_ADDRESS                   *Ip6Addr;\r
+  EFI_IPv6_ADDRESS                   GatewayAddr;\r
+  EFI_IP6_CONFIG_DATA                Ip6CfgData;\r
+  EFI_EVENT                          MappedEvt; \r
+  UINTN                              DataSize;\r
+  BOOLEAN                            IsAddressOk;\r
+  UINTN                              Index;\r
+\r
+  ASSERT (Private->UsingIpv6);\r
+  \r
+  MappedEvt   = NULL;\r
+  IsAddressOk = FALSE;\r
+  Ip6Addr     = NULL;\r
+  Ip6Cfg      = Private->Ip6Config;\r
+  Ip6         = Private->Ip6;\r
+  \r
+  ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
+  CopyMem (&CfgAddr, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+  ZeroMem (&Ip6CfgData, sizeof (EFI_IP6_CONFIG_DATA));\r
+  \r
+  Ip6CfgData.AcceptIcmpErrors    = TRUE;\r
+  Ip6CfgData.DefaultProtocol     = IP6_ICMP;\r
+  Ip6CfgData.HopLimit            = HTTP_BOOT_DEFAULT_HOPLIMIT;\r
+  Ip6CfgData.ReceiveTimeout      = HTTP_BOOT_DEFAULT_LIFETIME;\r
+  Ip6CfgData.TransmitTimeout     = HTTP_BOOT_DEFAULT_LIFETIME;\r
+    \r
+  Status = Ip6->Configure (Ip6, &Ip6CfgData);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Retrieve the gateway address from IP6 route table.\r
+  //\r
+  Status = HttpBootCheckRouteTable (Private, HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);\r
+  if (EFI_ERROR (Status)) {\r
+    Private->NoGateway = TRUE;\r
+  } else {\r
+    IP6_COPY_ADDRESS (&Private->GatewayIp.v6, &GatewayAddr);\r
+  }\r
+\r
+  //\r
+  // Set the new address by Ip6ConfigProtocol manually.\r
+  //\r
+  Policy = Ip6ConfigPolicyManual;\r
+  Status = Ip6Cfg->SetData (\r
+                     Ip6Cfg,\r
+                     Ip6ConfigDataTypePolicy,\r
+                     sizeof(EFI_IP6_CONFIG_POLICY),\r
+                     &Policy\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  \r
+  //\r
+  // Create a notify event to set address flag when DAD if IP6 driver succeeded.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  HttpBootCommonNotify,\r
+                  &IsAddressOk,\r
+                  &MappedEvt\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  \r
+  //\r
+  // Set static host ip6 address. This is a asynchronous process.\r
+  //\r
+  Status = Ip6Cfg->RegisterDataNotify (\r
+                     Ip6Cfg,\r
+                     Ip6ConfigDataTypeManualAddress,\r
+                     MappedEvt\r
+                     );\r
+  if (EFI_ERROR(Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Ip6Cfg->SetData (\r
+                     Ip6Cfg,\r
+                     Ip6ConfigDataTypeManualAddress,\r
+                     sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS),\r
+                     &CfgAddr\r
+                     ); \r
+  if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {\r
+    goto ON_EXIT;\r
+  } else if (Status == EFI_NOT_READY) {\r
+    //\r
+    // Poll the network until the asynchronous process is finished.\r
+    //\r
+    while (!IsAddressOk) {\r
+      Ip6->Poll (Ip6);\r
+    }\r
+    //\r
+    // Check whether the Ip6 Address setting is successed.\r
+    //\r
+    DataSize = 0;\r
+    Status = Ip6Cfg->GetData (\r
+                       Ip6Cfg,\r
+                       Ip6ConfigDataTypeManualAddress,\r
+                       &DataSize,\r
+                       NULL\r
+                       );\r
+    if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto ON_EXIT;\r
+    }\r
+    \r
+    Ip6Addr = AllocatePool (DataSize);\r
+    if (Ip6Addr == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    Status = Ip6Cfg->GetData (\r
+                       Ip6Cfg,\r
+                       Ip6ConfigDataTypeManualAddress,\r
+                       &DataSize,\r
+                       (VOID *) Ip6Addr\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index ++) {\r
+      if (CompareMem (Ip6Addr + Index, &CfgAddr, sizeof (EFI_IPv6_ADDRESS)) == 0) {\r
+        break;\r
+      }\r
+    }\r
+    if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {\r
+      Status = EFI_ABORTED;\r
+      goto ON_EXIT;\r
+    } \r
+  }\r
+    \r
+ON_EXIT:\r
+  if (MappedEvt != NULL) {\r
+    Ip6Cfg->UnregisterDataNotify (\r
+              Ip6Cfg,\r
+              Ip6ConfigDataTypeManualAddress,\r
+              MappedEvt\r
+              );\r
+    gBS->CloseEvent (MappedEvt);\r
+  }\r
+\r
+  if (Ip6Addr != NULL) {\r
+    FreePool (Ip6Addr);\r
+  }\r
+  \r
+  return Status;    \r
+}\r
+\r
+/**\r
+  Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information.\r
+\r
+  @param[in]  Private           Pointer to HTTP_BOOT private data.\r
+\r
+  @retval EFI_SUCCESS           The S.A.R.R process successfully finished.\r
+  @retval Others                Failed to finish the S.A.R.R process.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootDhcp6Sarr (\r
+  IN HTTP_BOOT_PRIVATE_DATA         *Private\r
+  )\r
+{\r
+  EFI_DHCP6_PROTOCOL               *Dhcp6;\r
+  EFI_DHCP6_CONFIG_DATA            Config;\r
+  EFI_DHCP6_MODE_DATA              Mode;\r
+  EFI_DHCP6_RETRANSMISSION         *Retransmit;\r
+  EFI_DHCP6_PACKET_OPTION          *OptList[HTTP_BOOT_DHCP6_OPTION_MAX_NUM];\r
+  UINT32                           OptCount;\r
+  UINT8                            Buffer[HTTP_BOOT_DHCP6_OPTION_MAX_SIZE];\r
+  EFI_STATUS                       Status;\r
+\r
+  Dhcp6 = Private->Dhcp6;\r
+  ASSERT (Dhcp6 != NULL);\r
+\r
+  //\r
+  // Build options list for the request packet.\r
+  //\r
+  OptCount = HttpBootBuildDhcp6Options (Private, OptList, Buffer);\r
+  ASSERT (OptCount >0);\r
+  \r
+  Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));\r
+  if (Retransmit == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  \r
+  ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));\r
+  ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));\r
+  \r
+  Config.OptionCount           = OptCount;\r
+  Config.OptionList            = OptList;\r
+  Config.Dhcp6Callback         = HttpBootDhcp6CallBack;\r
+  Config.CallbackContext       = Private;\r
+  Config.IaInfoEvent           = NULL;\r
+  Config.RapidCommit           = FALSE;\r
+  Config.ReconfigureAccept     = FALSE;\r
+  Config.IaDescriptor.IaId     = NET_RANDOM (NetRandomInitSeed ());\r
+  Config.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;\r
+  Config.SolicitRetransmission = Retransmit;\r
+  Retransmit->Irt              = 4;\r
+  Retransmit->Mrc              = 4;\r
+  Retransmit->Mrt              = 32;\r
+  Retransmit->Mrd              = 60;\r
+  \r
+  //\r
+  // Configure the DHCPv6 instance for HTTP boot.\r
+  //\r
+  Status = Dhcp6->Configure (Dhcp6, &Config);\r
+  FreePool (Retransmit);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Initialize the record fields for DHCPv6 offer in private data.\r
+  //\r
+  Private->OfferNum      = 0;\r
+  Private->SelectIndex   = 0;\r
+  ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));\r
+  ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));\r
+  \r
+  //\r
+  // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.\r
+  //\r
+  Status = Dhcp6->Start (Dhcp6);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  \r
+  //\r
+  // Get the acquired IPv6 address and store them.\r
+  //\r
+  Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  \r
+  ASSERT (Mode.Ia->State == Dhcp6Bound);\r
+  CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));\r
+  \r
+  AsciiPrint ("\n  Station IPv6 address is ");\r
+  HttpBootShowIp6Addr (&Private->StationIp.v6);\r
+  AsciiPrint ("\n");\r
+  \r
+ON_EXIT:\r
+  if (EFI_ERROR (Status)) {\r
+    Dhcp6->Stop (Dhcp6);\r
+    Dhcp6->Configure (Dhcp6, NULL);\r
+  } else {\r
+    ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));\r
+    ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));\r
+    Dhcp6->Configure (Dhcp6, &Config);\r
+  }\r
+\r
+  return Status; \r
+    \r
+}\r
+\r