]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
Update PXE driver to support PXE forced mode.
[mirror_edk2.git] / NetworkPkg / UefiPxeBcDxe / PxeBcDhcp6.c
index d72e62b6a9e36f01b5f753f89eb961435f4bd786..1eb64a6a0f54d4043f59bee2fef3b90fea952df0 100644 (file)
@@ -1,7 +1,7 @@
 /** @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
+  Copyright (c) 2009 - 2012, 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
@@ -97,7 +103,7 @@ PxeBcBuildDhcp6Options (
   // Append client network device interface option\r
   //\r
   OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_UNDI);\r
   // Append client network device interface option\r
   //\r
   OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_UNDI);\r
-  OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_UNDI));\r
+  OptList[Index]->OpLen      = HTONS ((UINT16)3);\r
   OptEnt.Undi                = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;\r
 \r
   if (Private->Nii != NULL) {\r
   OptEnt.Undi                = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;\r
 \r
   if (Private->Nii != NULL) {\r
@@ -110,7 +116,6 @@ PxeBcBuildDhcp6Options (
     OptEnt.Undi->MinorVer    = DEFAULT_UNDI_MINOR;\r
   }\r
 \r
     OptEnt.Undi->MinorVer    = DEFAULT_UNDI_MINOR;\r
   }\r
 \r
-  OptEnt.Undi->Reserved      = 0;\r
   Index++;\r
   OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
 \r
   Index++;\r
   OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
 \r
@@ -233,12 +238,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
@@ -313,7 +320,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
@@ -322,18 +329,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
@@ -456,13 +492,13 @@ PxeBcParseDhcp6Packet (
   // 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
@@ -471,11 +507,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
@@ -567,6 +604,223 @@ 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_MODE              *Mode;\r
+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;\r
+  EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;\r
+  UINTN                               DiscoverLen;\r
+  EFI_DHCP6_PACKET                    *Request;\r
+  UINTN                               RequestLen;\r
+  EFI_DHCP6_PACKET                    *Reply;\r
+  UINT8                               *RequestOpt;\r
+  UINT8                               *DiscoverOpt;\r
+  UINTN                               ReadSize;\r
+  UINT16                              OpFlags;\r
+  UINT16                              OpCode;\r
+  UINT16                              OpLen;\r
+  EFI_STATUS                          Status;\r
+  EFI_DHCP6_PACKET                    *ProxyOffer;\r
+  UINT8                               *Option;\r
+\r
+  PxeBc       = &Private->PxeBc;\r
+  Mode        = PxeBc->Mode;\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
@@ -587,7 +841,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
@@ -597,31 +850,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
@@ -1002,6 +1260,8 @@ PxeBcRegisterIp6Address (
   EFI_EVENT                        TimeOutEvt;\r
   EFI_EVENT                        MappedEvt;\r
   EFI_STATUS                       Status;\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
 \r
   Status     = EFI_SUCCESS;\r
   TimeOutEvt = NULL;\r
 \r
   Status     = EFI_SUCCESS;\r
   TimeOutEvt = NULL;\r
@@ -1045,6 +1305,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
@@ -1096,7 +1370,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
@@ -1194,6 +1469,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
@@ -1377,6 +1659,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
@@ -1389,6 +1679,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
@@ -1421,9 +1715,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
@@ -1446,7 +1748,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
@@ -1458,8 +1760,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
@@ -1477,6 +1779,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