]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
Fix a bug that the gateway is not necessary in a simple PXE network.
[mirror_edk2.git] / NetworkPkg / UefiPxeBcDxe / PxeBcDhcp6.c
index 64e15030c5eb6ed154356a699b33cbbca3b960fc..07f1724365f4dc7078d2c833ec4abefe9808ae93 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
   Functions implementation related with DHCPv6 for UefiPxeBc Driver.\r
 \r
 /** @file\r
   Functions implementation related with DHCPv6 for UefiPxeBc Driver.\r
 \r
-  Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+  (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
+  Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>\r
 \r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
 \r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
 \r
 #include "PxeBcImpl.h"\r
 \r
 \r
 #include "PxeBcImpl.h"\r
 \r
+//\r
+// Well-known multi-cast address defined in section-24.1 of rfc-3315\r
+//\r
+//   ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2\r
+//\r
+EFI_IPv6_ADDRESS   mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};\r
 \r
 /**\r
   Parse out a DHCPv6 option by OptTag, and find the position in buffer.\r
 \r
 /**\r
   Parse out a DHCPv6 option by OptTag, and find the position in buffer.\r
@@ -232,12 +239,14 @@ PxeBcExtractBootFileUrl (
   )\r
 {\r
   UINT16                     PrefixLen;\r
   )\r
 {\r
   UINT16                     PrefixLen;\r
-  UINT8                      *BootFileNamePtr;\r
-  UINT8                      *BootFileName;\r
+  CHAR8                      *BootFileNamePtr;\r
+  CHAR8                      *BootFileName;\r
   UINT16                     BootFileNameLen;\r
   CHAR8                      *TmpStr;\r
   UINT16                     BootFileNameLen;\r
   CHAR8                      *TmpStr;\r
+  CHAR8                      TmpChar;\r
   CHAR8                      *ServerAddressOption;\r
   CHAR8                      *ServerAddress;\r
   CHAR8                      *ServerAddressOption;\r
   CHAR8                      *ServerAddress;\r
+  CHAR8                      *ModeStr;\r
   EFI_STATUS                 Status;\r
 \r
   //\r
   EFI_STATUS                 Status;\r
 \r
   //\r
@@ -312,7 +321,7 @@ PxeBcExtractBootFileUrl (
   //\r
   // Get the part of BOOTFILE_NAME string.\r
   //\r
   //\r
   // Get the part of BOOTFILE_NAME string.\r
   //\r
-  BootFileNamePtr = (UINT8*)((UINTN)ServerAddress + 1);\r
+  BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1);\r
   if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {\r
     FreePool (TmpStr);\r
     return EFI_INVALID_PARAMETER;\r
   if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {\r
     FreePool (TmpStr);\r
     return EFI_INVALID_PARAMETER;\r
@@ -321,18 +330,47 @@ PxeBcExtractBootFileUrl (
   ++BootFileNamePtr;\r
   BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);\r
   if (BootFileNameLen != 0 || FileName != NULL) {\r
   ++BootFileNamePtr;\r
   BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);\r
   if (BootFileNameLen != 0 || FileName != NULL) {\r
-    BootFileName = (UINT8 *) AllocateZeroPool (BootFileNameLen);\r
+    //\r
+    // Remove trailing mode=octet if present and ignore.  All other modes are\r
+    // invalid for netboot6, so reject them.\r
+    //\r
+    ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet");\r
+    if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') {\r
+      *ModeStr = '\0';\r
+    } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Extract boot file name from URL.\r
+    //\r
+    BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen);\r
     if (BootFileName == NULL) {\r
       FreePool (TmpStr);\r
       return EFI_OUT_OF_RESOURCES;\r
     }\r
     if (BootFileName == NULL) {\r
       FreePool (TmpStr);\r
       return EFI_OUT_OF_RESOURCES;\r
     }\r
+    *FileName = (UINT8*) BootFileName;\r
 \r
 \r
-    CopyMem (BootFileName, BootFileNamePtr, BootFileNameLen);\r
-    BootFileName[BootFileNameLen - 1] = '\0';\r
-    *FileName = BootFileName;\r
+    //\r
+    // Decode percent-encoding in boot file name.\r
+    //\r
+    while (*BootFileNamePtr != '\0') {\r
+      if (*BootFileNamePtr == '%') {\r
+        TmpChar = *(BootFileNamePtr+ 3);\r
+        *(BootFileNamePtr+ 3) = '\0';\r
+        *BootFileName = (UINT8) AsciiStrHexToUintn ((CHAR8*)(BootFileNamePtr + 1));\r
+        BootFileName++;\r
+        *(BootFileNamePtr+ 3) = TmpChar;\r
+        BootFileNamePtr += 3;\r
+      } else {\r
+        *BootFileName = *BootFileNamePtr;\r
+        BootFileName++;\r
+        BootFileNamePtr++;\r
+      }\r
+    }\r
+    *BootFileName = '\0';\r
   }\r
 \r
   }\r
 \r
-\r
   FreePool (TmpStr);\r
 \r
   return EFI_SUCCESS;\r
   FreePool (TmpStr);\r
 \r
   return EFI_SUCCESS;\r
@@ -451,17 +489,17 @@ PxeBcParseDhcp6Packet (
   }\r
 \r
   //\r
   }\r
 \r
   //\r
-  // The offer with assigned client address is a proxy offer.\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[PXEBC_DHCP6_IDX_IA_NA];\r
   // An ia_na option, embeded with valid ia_addr option and a status_code of success.\r
   //\r
   Option = Options[PXEBC_DHCP6_IDX_IA_NA];\r
-  if (Option != NULL && NTOHS(Option->OpLen) >= 12) {\r
+  if (Option != NULL) {\r
     Option = PxeBcParseDhcp6Options (\r
                Option->Data + 12,\r
                NTOHS (Option->OpLen),\r
                PXEBC_DHCP6_OPT_STATUS_CODE\r
                );\r
     Option = PxeBcParseDhcp6Options (\r
                Option->Data + 12,\r
                NTOHS (Option->OpLen),\r
                PXEBC_DHCP6_OPT_STATUS_CODE\r
                );\r
-    if (Option != NULL && Option->Data[0] == 0) {\r
+    if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {\r
       IsProxyOffer = FALSE;\r
     }\r
   }\r
       IsProxyOffer = FALSE;\r
     }\r
   }\r
@@ -470,11 +508,12 @@ PxeBcParseDhcp6Packet (
   // The offer with "PXEClient" is a pxe offer.\r
   //\r
   Option        = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];\r
   // The offer with "PXEClient" is a pxe offer.\r
   //\r
   Option        = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];\r
-  EnterpriseNum = PXEBC_DHCP6_ENTERPRISE_NUM;\r
+  EnterpriseNum = HTONL(PXEBC_DHCP6_ENTERPRISE_NUM);\r
+\r
   if (Option != NULL &&\r
       NTOHS(Option->OpLen) >= 13 &&\r
       CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&\r
   if (Option != NULL &&\r
       NTOHS(Option->OpLen) >= 13 &&\r
       CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&\r
-      CompareMem (&Option->Data[4], DEFAULT_CLASS_ID_DATA, 9) == 0) {\r
+      CompareMem (&Option->Data[6], DEFAULT_CLASS_ID_DATA, 9) == 0) {\r
     IsPxeOffer = TRUE;\r
   }\r
 \r
     IsPxeOffer = TRUE;\r
   }\r
 \r
@@ -566,6 +605,221 @@ PxeBcCopyDhcp6Proxy (
   Mode->ProxyOfferReceived = TRUE;\r
 }\r
 \r
   Mode->ProxyOfferReceived = TRUE;\r
 }\r
 \r
+/**\r
+  Seek the address of the first byte of the option header.\r
+\r
+  @param[in]  Buf           The pointer to the buffer.\r
+  @param[in]  SeekLen       The length to seek.\r
+  @param[in]  OptType       The option type.\r
+\r
+  @retval     NULL          If it failed to seek the option.\r
+  @retval     others        The position to the option.\r
+\r
+**/\r
+UINT8 *\r
+PxeBcDhcp6SeekOption (\r
+  IN UINT8           *Buf,\r
+  IN UINT32          SeekLen,\r
+  IN UINT16          OptType\r
+  )\r
+{\r
+  UINT8              *Cursor;\r
+  UINT8              *Option;\r
+  UINT16             DataLen;\r
+  UINT16             OpCode;\r
+\r
+  Option = NULL;\r
+  Cursor = Buf;\r
+\r
+  while (Cursor < Buf + SeekLen) {\r
+    OpCode = ReadUnaligned16 ((UINT16 *) Cursor);\r
+    if (OpCode == HTONS (OptType)) {\r
+      Option = Cursor;\r
+      break;\r
+    }\r
+    DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));\r
+    Cursor += (DataLen + 4);\r
+  }\r
+\r
+  return Option;\r
+}\r
+\r
+\r
+/**\r
+  Build and send out the request packet for the bootfile, and parse the reply.\r
+\r
+  @param[in]  Private               The pointer to PxeBc private data.\r
+  @param[in]  Index                 PxeBc option boot item type.\r
+\r
+  @retval     EFI_SUCCESS           Successfully discovered the boot file.\r
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.\r
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.\r
+  @retval     Others                Failed to discover the boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcRequestBootService (\r
+  IN  PXEBC_PRIVATE_DATA              *Private,\r
+  IN  UINT32                          Index\r
+  )\r
+{\r
+  EFI_PXE_BASE_CODE_UDP_PORT          SrcPort;\r
+  EFI_PXE_BASE_CODE_UDP_PORT          DestPort;\r
+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;\r
+  EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;\r
+  UINTN                               DiscoverLen;\r
+  EFI_DHCP6_PACKET                    *Request;\r
+  UINTN                               RequestLen;\r
+  EFI_DHCP6_PACKET                    *Reply;\r
+  UINT8                               *RequestOpt;\r
+  UINT8                               *DiscoverOpt;\r
+  UINTN                               ReadSize;\r
+  UINT16                              OpFlags;\r
+  UINT16                              OpCode;\r
+  UINT16                              OpLen;\r
+  EFI_STATUS                          Status;\r
+  EFI_DHCP6_PACKET                    *ProxyOffer;\r
+  UINT8                               *Option;\r
+\r
+  PxeBc       = &Private->PxeBc;\r
+  Request     = Private->Dhcp6Request;\r
+  ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer;\r
+  SrcPort     = PXEBC_BS_DISCOVER_PORT;\r
+  DestPort    = PXEBC_BS_DISCOVER_PORT;\r
+  OpFlags     = 0;\r
+\r
+  if (Request == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));\r
+  if (Discover == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Build the request packet by the cached request packet before.\r
+  //\r
+  Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId;\r
+  Discover->MessageType   = Request->Dhcp6.Header.MessageType;\r
+  RequestOpt              = Request->Dhcp6.Option;\r
+  DiscoverOpt             = Discover->DhcpOptions;\r
+  DiscoverLen             = sizeof (EFI_DHCP6_HEADER);\r
+  RequestLen              = DiscoverLen;\r
+\r
+  //\r
+  // Find Server ID Option from ProxyOffer.\r
+  //\r
+  Option = PxeBcDhcp6SeekOption (\r
+             ProxyOffer->Dhcp6.Option,\r
+             ProxyOffer->Length - 4,\r
+             PXEBC_DHCP6_OPT_SERVER_ID\r
+             );\r
+  if (Option == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  \r
+  //\r
+  // Add Server ID Option.\r
+  //\r
+  OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen);\r
+  CopyMem (DiscoverOpt, Option, OpLen + 4);\r
+  DiscoverOpt += (OpLen + 4);\r
+  DiscoverLen += (OpLen + 4);\r
+\r
+  while (RequestLen < Request->Length) {\r
+    OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);\r
+    OpLen  = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);\r
+    if (OpCode != EFI_DHCP6_IA_TYPE_NA &&\r
+        OpCode != EFI_DHCP6_IA_TYPE_TA &&\r
+        OpCode != PXEBC_DHCP6_OPT_SERVER_ID\r
+        ) {\r
+      //\r
+      // Copy all the options except IA option and Server ID\r
+      //\r
+      CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);\r
+      DiscoverOpt += (OpLen + 4);\r
+      DiscoverLen += (OpLen + 4);\r
+    }\r
+    RequestOpt += (OpLen + 4);\r
+    RequestLen += (OpLen + 4);\r
+  }\r
+\r
+  //\r
+  // Update Elapsed option in the package \r
+  //\r
+  Option = PxeBcDhcp6SeekOption (\r
+             Discover->DhcpOptions,\r
+             (UINT32)(RequestLen - 4),\r
+             PXEBC_DHCP6_OPT_ELAPSED_TIME\r
+             );\r
+  if (Option != NULL) {\r
+    CalcElapsedTime (Private);\r
+    WriteUnaligned16 ((UINT16*)(Option + 4), HTONS((UINT16) Private->ElapsedTime));\r
+  }  \r
+\r
+  Status = PxeBc->UdpWrite (\r
+                    PxeBc,\r
+                    OpFlags,\r
+                    &Private->ServerIp,\r
+                    &DestPort,\r
+                    NULL,\r
+                    &Private->StationIp,\r
+                    &SrcPort,\r
+                    NULL,\r
+                    NULL,\r
+                    &DiscoverLen,\r
+                    (VOID *) Discover\r
+                    );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Cache the right PXE reply packet here, set valid flag later.\r
+  // Especially for PXE discover packet, store it into mode data here.\r
+  //\r
+  Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;\r
+  ReadSize = (UINTN) Reply->Size;\r
+\r
+  //\r
+  // Start Udp6Read instance\r
+  //\r
+  Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+    \r
+  Status = PxeBc->UdpRead (\r
+                    PxeBc,\r
+                    EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP,\r
+                    &Private->StationIp,\r
+                    &SrcPort,\r
+                    &Private->ServerIp,\r
+                    &DestPort,\r
+                    NULL,\r
+                    NULL,\r
+                    &ReadSize,\r
+                    (VOID *) &Reply->Dhcp6\r
+                    );\r
+  //\r
+  // Stop Udp6Read instance\r
+  //\r
+  Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Update length\r
+  //\r
+  Reply->Length = (UINT32) ReadSize;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
 \r
 /**\r
   Retry to request bootfile name by the BINL offer.\r
 \r
 /**\r
   Retry to request bootfile name by the BINL offer.\r
@@ -586,7 +840,6 @@ PxeBcRetryDhcp6Binl (
   EFI_PXE_BASE_CODE_MODE    *Mode;\r
   PXEBC_DHCP6_PACKET_CACHE  *Offer;\r
   PXEBC_DHCP6_PACKET_CACHE  *Cache6;\r
   EFI_PXE_BASE_CODE_MODE    *Mode;\r
   PXEBC_DHCP6_PACKET_CACHE  *Offer;\r
   PXEBC_DHCP6_PACKET_CACHE  *Cache6;\r
-  EFI_IP_ADDRESS            ServerIp;\r
   EFI_STATUS                Status;\r
 \r
   ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
   EFI_STATUS                Status;\r
 \r
   ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
@@ -596,31 +849,36 @@ PxeBcRetryDhcp6Binl (
   Mode                  = Private->PxeBc.Mode;\r
   Private->IsDoDiscover = FALSE;\r
   Offer                 = &Private->OfferBuffer[Index].Dhcp6;\r
   Mode                  = Private->PxeBc.Mode;\r
   Private->IsDoDiscover = FALSE;\r
   Offer                 = &Private->OfferBuffer[Index].Dhcp6;\r
-\r
-  ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
-  //\r
-  // Parse out the next server address from the last offer, and store it\r
-  //\r
-  Status = PxeBcExtractBootFileUrl (\r
-             NULL,\r
-             &ServerIp.v6,\r
-             (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
-             NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
-             );\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
+  if (Offer->OfferType == PxeOfferTypeDhcpBinl) {\r
+    //\r
+    // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.\r
+    //\r
+    CopyMem (\r
+      &Private->ServerIp.v6,\r
+      &mAllDhcpRelayAndServersAddress,\r
+      sizeof (EFI_IPv6_ADDRESS)\r
+      );\r
+  } else {\r
+    ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
+    //\r
+    // Parse out the next server address from the last offer, and store it\r
+    //\r
+    Status = PxeBcExtractBootFileUrl (\r
+               &Private->BootFileName,\r
+               &Private->ServerIp.v6,\r
+               (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
+               NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
   }\r
 \r
   //\r
   // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.\r
   //\r
   }\r
 \r
   //\r
   // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.\r
   //\r
-  Status = PxeBcDhcp6Discover (\r
-             Private,\r
-             0,\r
-             NULL,\r
-             FALSE,\r
-             &ServerIp\r
-             );\r
+  Status = PxeBcRequestBootService (Private, Index);\r
+\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
@@ -976,9 +1234,128 @@ PxeBcUnregisterIp6Address (
   }\r
 }\r
 \r
   }\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 PXEBC_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
+PxeBcCheckRouteTable (\r
+  IN  PXEBC_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
+  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
 \r
 /**\r
-  Register the ready address by Ip6Config protocol.\r
+  Register the ready station address and gateway by Ip6Config protocol.\r
 \r
   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.\r
   @param[in]  Address             The pointer to the ready address.\r
 \r
   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.\r
   @param[in]  Address             The pointer to the ready address.\r
@@ -997,10 +1374,14 @@ PxeBcRegisterIp6Address (
   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;\r
   EFI_IP6_CONFIG_POLICY            Policy;\r
   EFI_IP6_CONFIG_MANUAL_ADDRESS    CfgAddr;\r
   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;\r
   EFI_IP6_CONFIG_POLICY            Policy;\r
   EFI_IP6_CONFIG_MANUAL_ADDRESS    CfgAddr;\r
+  EFI_IPv6_ADDRESS                 GatewayAddr;\r
   UINTN                            DataSize;\r
   EFI_EVENT                        TimeOutEvt;\r
   EFI_EVENT                        MappedEvt;\r
   EFI_STATUS                       Status;\r
   UINTN                            DataSize;\r
   EFI_EVENT                        TimeOutEvt;\r
   EFI_EVENT                        MappedEvt;\r
   EFI_STATUS                       Status;\r
+  UINT64                           DadTriggerTime;\r
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS    DadXmits;\r
+  BOOLEAN                          NoGateway;\r
 \r
   Status     = EFI_SUCCESS;\r
   TimeOutEvt = NULL;\r
 \r
   Status     = EFI_SUCCESS;\r
   TimeOutEvt = NULL;\r
@@ -1008,23 +1389,24 @@ PxeBcRegisterIp6Address (
   DataSize   = sizeof (EFI_IP6_CONFIG_POLICY);\r
   Ip6Cfg     = Private->Ip6Cfg;\r
   Ip6        = Private->Ip6;\r
   DataSize   = sizeof (EFI_IP6_CONFIG_POLICY);\r
   Ip6Cfg     = Private->Ip6Cfg;\r
   Ip6        = Private->Ip6;\r
+  NoGateway  = FALSE;\r
 \r
   ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
   CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));\r
 \r
 \r
   ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
   CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));\r
 \r
-  //\r
-  // Get and store the current policy of IP6 driver.\r
-  //\r
-  Status = Ip6Cfg->GetData (\r
-                     Ip6Cfg,\r
-                     Ip6ConfigDataTypePolicy,\r
-                     &DataSize,\r
-                     &Private->Ip6Policy\r
-                     );\r
+  Status = Ip6->Configure (Ip6, &Private->Ip6CfgData);\r
   if (EFI_ERROR (Status)) {\r
     goto ON_EXIT;\r
   }\r
 \r
   if (EFI_ERROR (Status)) {\r
     goto ON_EXIT;\r
   }\r
 \r
+  //\r
+  // Retrieve the gateway address from IP6 route table.\r
+  //\r
+  Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);\r
+  if (EFI_ERROR (Status)) {\r
+    NoGateway = TRUE;\r
+  }\r
+  \r
   //\r
   // There is no channel between IP6 and PXE driver about address setting,\r
   // so it has to set the new address by Ip6ConfigProtocol manually.\r
   //\r
   // There is no channel between IP6 and PXE driver about address setting,\r
   // so it has to set the new address by Ip6ConfigProtocol manually.\r
@@ -1044,6 +1426,20 @@ PxeBcRegisterIp6Address (
     goto ON_EXIT;\r
   }\r
 \r
     goto ON_EXIT;\r
   }\r
 \r
+  //\r
+  // Get Duplicate Address Detection Transmits count.\r
+  //\r
+  DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+  Status = Ip6Cfg->GetData (\r
+                     Ip6Cfg,\r
+                     Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+                     &DataSize,\r
+                     &DadXmits\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
   //\r
   // Create a timer as setting address timeout event since DAD in IP6 driver.\r
   //\r
   //\r
   // Create a timer as setting address timeout event since DAD in IP6 driver.\r
   //\r
@@ -1095,7 +1491,8 @@ PxeBcRegisterIp6Address (
   // Start the 5 secondes timer to wait for setting address.\r
   //\r
   Status = EFI_NO_MAPPING;\r
   // Start the 5 secondes timer to wait for setting address.\r
   //\r
   Status = EFI_NO_MAPPING;\r
-  gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT);\r
+  DadTriggerTime = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY;\r
+  gBS->SetTimer (TimeOutEvt, TimerRelative, DadTriggerTime);\r
 \r
   while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
     Ip6->Poll (Ip6);\r
 \r
   while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
     Ip6->Poll (Ip6);\r
@@ -1105,6 +1502,21 @@ PxeBcRegisterIp6Address (
     }\r
   }\r
 \r
     }\r
   }\r
 \r
+  //\r
+  // Set the default gateway address back if needed.\r
+  //\r
+  if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) {\r
+    Status = Ip6Cfg->SetData (\r
+                       Ip6Cfg,\r
+                       Ip6ConfigDataTypeGateway,\r
+                       sizeof (EFI_IPv6_ADDRESS),\r
+                       &GatewayAddr\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
 ON_EXIT:\r
   if (MappedEvt != NULL) {\r
     Ip6Cfg->UnregisterDataNotify (\r
 ON_EXIT:\r
   if (MappedEvt != NULL) {\r
     Ip6Cfg->UnregisterDataNotify (\r
@@ -1120,6 +1532,100 @@ ON_EXIT:
   return Status;\r
 }\r
 \r
   return Status;\r
 }\r
 \r
+/**\r
+  Set the IP6 policy to Automatic.\r
+\r
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.\r
+\r
+  @retval     EFI_SUCCESS         Switch the IP policy succesfully.\r
+  @retval     Others              Unexpect error happened.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcSetIp6Policy (\r
+  IN PXEBC_PRIVATE_DATA            *Private\r
+  )\r
+{\r
+  EFI_IP6_CONFIG_POLICY            Policy;\r
+  EFI_STATUS                       Status;\r
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;\r
+  UINTN                            DataSize;\r
+\r
+  Ip6Cfg      = Private->Ip6Cfg;\r
+  DataSize    = sizeof (EFI_IP6_CONFIG_POLICY);\r
+\r
+  //\r
+  // Get and store the current policy of IP6 driver.\r
+  //\r
+  Status = Ip6Cfg->GetData (\r
+                     Ip6Cfg,\r
+                     Ip6ConfigDataTypePolicy,\r
+                     &DataSize,\r
+                     &Private->Ip6Policy\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Private->Ip6Policy == Ip6ConfigPolicyManual) {\r
+    Policy = Ip6ConfigPolicyAutomatic;\r
+    Status = Ip6Cfg->SetData (\r
+                       Ip6Cfg,\r
+                       Ip6ConfigDataTypePolicy,\r
+                       sizeof(EFI_IP6_CONFIG_POLICY),\r
+                       &Policy\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // There is no need to recover later.\r
+      //\r
+      Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function will register the station IP address and flush IP instance to start using the new IP address.\r
+  \r
+  @param[in]  Private             The pointer to PXEBC_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
+PxeBcSetIp6Address (\r
+  IN  PXEBC_PRIVATE_DATA              *Private\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_DHCP6_PROTOCOL          *Dhcp6;\r
+    \r
+  Dhcp6 = Private->Dhcp6;\r
+\r
+  CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+  CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);\r
+  if (EFI_ERROR (Status)) {\r
+    Dhcp6->Stop (Dhcp6);\r
+    return Status;\r
+  }\r
+\r
+  Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    PxeBcUnregisterIp6Address (Private);\r
+    Dhcp6->Stop (Dhcp6);\r
+    return Status;\r
+  }\r
+\r
+  AsciiPrint ("\n  Station IP address is ");\r
+  PxeBcShowIp6Addr (&Private->StationIp.v6);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
 \r
 /**\r
   EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver\r
 \r
 /**\r
   EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver\r
@@ -1193,6 +1699,13 @@ PxeBcDhcp6CallBack (
   switch (Dhcp6Event) {\r
 \r
   case Dhcp6SendSolicit:\r
   switch (Dhcp6Event) {\r
 \r
   case Dhcp6SendSolicit:\r
+    //\r
+    // Record the first Solicate msg time\r
+    //\r
+    if (Private->SolicitTimes == 0) {\r
+      CalcElapsedTime (Private);\r
+      Private->SolicitTimes++;\r
+    }\r
     //\r
     // Cache the dhcp discover packet to mode data directly.\r
     //\r
     //\r
     // Cache the dhcp discover packet to mode data directly.\r
     //\r
@@ -1376,6 +1889,14 @@ PxeBcDhcp6Discover (
   }\r
   ReadSize = (UINTN) Reply->Size;\r
 \r
   }\r
   ReadSize = (UINTN) Reply->Size;\r
 \r
+  //\r
+  // Start Udp6Read instance\r
+  //\r
+  Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  \r
   Status = PxeBc->UdpRead (\r
                     PxeBc,\r
                     OpFlags,\r
   Status = PxeBc->UdpRead (\r
                     PxeBc,\r
                     OpFlags,\r
@@ -1388,6 +1909,10 @@ PxeBcDhcp6Discover (
                     &ReadSize,\r
                     (VOID *) &Reply->Dhcp6\r
                     );\r
                     &ReadSize,\r
                     (VOID *) &Reply->Dhcp6\r
                     );\r
+  //\r
+  // Stop Udp6Read instance\r
+  //\r
+  Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
@@ -1420,9 +1945,17 @@ PxeBcDhcp6Sarr (
   UINT8                            Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];\r
   UINT32                           OptCount;\r
   EFI_STATUS                       Status;\r
   UINT8                            Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];\r
   UINT32                           OptCount;\r
   EFI_STATUS                       Status;\r
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;\r
+  EFI_STATUS                       TimerStatus;\r
+  EFI_EVENT                        Timer;\r
+  UINT64                           GetMappingTimeOut;\r
+  UINTN                            DataSize;\r
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS    DadXmits;\r
 \r
   Status     = EFI_SUCCESS;\r
   PxeMode    = Private->PxeBc.Mode;\r
 \r
   Status     = EFI_SUCCESS;\r
   PxeMode    = Private->PxeBc.Mode;\r
+  Ip6Cfg     = Private->Ip6Cfg;\r
+  Timer      = NULL;\r
 \r
   //\r
   // Build option list for the request packet.\r
 \r
   //\r
   // Build option list for the request packet.\r
@@ -1445,7 +1978,7 @@ PxeBcDhcp6Sarr (
   Config.IaInfoEvent           = NULL;\r
   Config.RapidCommit           = FALSE;\r
   Config.ReconfigureAccept     = FALSE;\r
   Config.IaInfoEvent           = NULL;\r
   Config.RapidCommit           = FALSE;\r
   Config.ReconfigureAccept     = FALSE;\r
-  Config.IaDescriptor.IaId     = 1;\r
+  Config.IaDescriptor.IaId     = Private->IaId;\r
   Config.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;\r
   Config.SolicitRetransmission = Retransmit;\r
   Retransmit->Irt              = 4;\r
   Config.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;\r
   Config.SolicitRetransmission = Retransmit;\r
   Retransmit->Irt              = 4;\r
@@ -1457,8 +1990,8 @@ PxeBcDhcp6Sarr (
   // Configure the DHCPv6 instance for PXE boot.\r
   //\r
   Status = Dhcp6->Configure (Dhcp6, &Config);\r
   // Configure the DHCPv6 instance for PXE boot.\r
   //\r
   Status = Dhcp6->Configure (Dhcp6, &Config);\r
+  FreePool (Retransmit);\r
   if (EFI_ERROR (Status)) {\r
   if (EFI_ERROR (Status)) {\r
-    FreePool (Retransmit);\r
     return Status;\r
   }\r
 \r
     return Status;\r
   }\r
 \r
@@ -1476,6 +2009,52 @@ PxeBcDhcp6Sarr (
   // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.\r
   //\r
   Status = Dhcp6->Start (Dhcp6);\r
   // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.\r
   //\r
   Status = Dhcp6->Start (Dhcp6);\r
+  if (Status == EFI_NO_MAPPING) {\r
+    //\r
+    // IP6 Linklocal address is not available for use, so stop current Dhcp process\r
+    // and wait for duplicate address detection to finish.\r
+    //\r
+    Dhcp6->Stop (Dhcp6);\r
+\r
+    //\r
+    // Get Duplicate Address Detection Transmits count.\r
+    //\r
+    DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+    Status = Ip6Cfg->GetData (\r
+                       Ip6Cfg,\r
+                       Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+                       &DataSize,\r
+                       &DadXmits\r
+                       );\r
+    if (EFI_ERROR (Status)) {\r
+      Dhcp6->Configure (Dhcp6, NULL);\r
+      return Status;\r
+    }\r
+\r
+    Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);\r
+    if (EFI_ERROR (Status)) {\r
+      Dhcp6->Configure (Dhcp6, NULL);\r
+      return Status;\r
+    }\r
+\r
+    GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY;\r
+    Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);\r
+    if (EFI_ERROR (Status)) {\r
+      gBS->CloseEvent (Timer);\r
+      Dhcp6->Configure (Dhcp6, NULL);\r
+      return Status;\r
+    }\r
+\r
+    do {\r
+      \r
+      TimerStatus = gBS->CheckEvent (Timer);\r
+      if (!EFI_ERROR (TimerStatus)) {\r
+        Status = Dhcp6->Start (Dhcp6);\r
+      }\r
+    } while (TimerStatus == EFI_NOT_READY);\r
+    \r
+    gBS->CloseEvent (Timer);\r
+  }\r
   if (EFI_ERROR (Status)) {\r
     if (Status == EFI_ICMP_ERROR) {\r
       PxeMode->IcmpErrorReceived = TRUE;\r
   if (EFI_ERROR (Status)) {\r
     if (Status == EFI_ICMP_ERROR) {\r
       PxeMode->IcmpErrorReceived = TRUE;\r
@@ -1494,36 +2073,23 @@ PxeBcDhcp6Sarr (
   }\r
 \r
   ASSERT (Mode.Ia->State == Dhcp6Bound);\r
   }\r
 \r
   ASSERT (Mode.Ia->State == Dhcp6Bound);\r
-  CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));\r
-  CopyMem (&PxeMode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
-\r
-  Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);\r
-  if (EFI_ERROR (Status)) {\r
-    Dhcp6->Stop (Dhcp6);\r
-    return Status;\r
-  }\r
-\r
-  Status = PxeBcFlushStaionIp (Private, &Private->StationIp, NULL);\r
-  if (EFI_ERROR (Status)) {\r
-    PxeBcUnregisterIp6Address (Private);\r
-    Dhcp6->Stop (Dhcp6);\r
-    return Status;\r
-  }\r
+  //\r
+  // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the\r
+  // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when\r
+  // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as\r
+  // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery \r
+  // to find a valid router address.\r
+  //\r
+  CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));\r
 \r
   //\r
   // Check the selected offer whether BINL retry is needed.\r
   //\r
   Status = PxeBcHandleDhcp6Offer (Private);\r
   if (EFI_ERROR (Status)) {\r
 \r
   //\r
   // Check the selected offer whether BINL retry is needed.\r
   //\r
   Status = PxeBcHandleDhcp6Offer (Private);\r
   if (EFI_ERROR (Status)) {\r
-    PxeBcUnregisterIp6Address (Private);\r
     Dhcp6->Stop (Dhcp6);\r
     return Status;\r
   }\r
     Dhcp6->Stop (Dhcp6);\r
     return Status;\r
   }\r
-\r
-  AsciiPrint ("\n  Station IP address is ");\r
-\r
-  PxeBcShowIp6Addr (&Private->StationIp.v6);\r
-\r
+  \r
   return EFI_SUCCESS;\r
 }\r
   return EFI_SUCCESS;\r
 }\r
-\r