]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
NetworkPkg: remove unnecessary timeout event when setting IPv6 address.
[mirror_edk2.git] / NetworkPkg / UefiPxeBcDxe / PxeBcDhcp6.c
index 3e59f3f233441a51aa3fb0370ab6d6eff1434dc1..6ad5f5f1ac9fd5eb318a3fcd55e597d13e92dbd7 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
   Functions implementation related with DHCPv6 for UefiPxeBc Driver.\r
 \r
-  Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
+  (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
+  Copyright (c) 2009 - 2015, 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
 #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
@@ -482,7 +489,7 @@ PxeBcParseDhcp6Packet (
   }\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
@@ -658,7 +665,6 @@ PxeBcRequestBootService (
 {\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
@@ -676,7 +682,6 @@ PxeBcRequestBootService (
   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
@@ -788,8 +793,8 @@ PxeBcRequestBootService (
     \r
   Status = PxeBc->UdpRead (\r
                     PxeBc,\r
-                    OpFlags,\r
-                    &Private->StationIp,\r
+                    EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,\r
+                    NULL,\r
                     &SrcPort,\r
                     &Private->ServerIp,\r
                     &DestPort,\r
@@ -844,19 +849,29 @@ PxeBcRetryDhcp6Binl (
   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
-             &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
+  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
@@ -1219,9 +1234,128 @@ PxeBcUnregisterIp6Address (
   }\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
-  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
@@ -1240,34 +1374,38 @@ PxeBcRegisterIp6Address (
   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
+  BOOLEAN                          NoGateway;\r
+  EFI_IPv6_ADDRESS                 *Ip6Addr;\r
+  UINTN                            Index;\r
 \r
   Status     = EFI_SUCCESS;\r
-  TimeOutEvt = NULL;\r
   MappedEvt  = NULL;\r
+  Ip6Addr    = NULL;\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
-  // 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
+  //\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
@@ -1287,20 +1425,6 @@ PxeBcRegisterIp6Address (
     goto ON_EXIT;\r
   }\r
 \r
-  //\r
-  // Create a timer as setting address timeout event since DAD in IP6 driver.\r
-  //\r
-  Status = gBS->CreateEvent (\r
-                  EVT_TIMER,\r
-                  TPL_CALLBACK,\r
-                  NULL,\r
-                  NULL,\r
-                  &TimeOutEvt\r
-                  );\r
-  if (EFI_ERROR (Status)) {\r
-    goto ON_EXIT;\r
-  }\r
-\r
   //\r
   // Create a notify event to set address flag when DAD if IP6 driver succeeded.\r
   //\r
@@ -1315,6 +1439,7 @@ PxeBcRegisterIp6Address (
     goto ON_EXIT;\r
   }\r
 \r
+  Private->IsAddressOk = FALSE;\r
   Status = Ip6Cfg->RegisterDataNotify (\r
                      Ip6Cfg,\r
                      Ip6ConfigDataTypeManualAddress,\r
@@ -1332,19 +1457,66 @@ PxeBcRegisterIp6Address (
                      );\r
   if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {\r
     goto ON_EXIT;\r
-  }\r
+  } else if (Status == EFI_NOT_READY) {\r
+    //\r
+    // Poll the network until the asynchronous process is finished.\r
+    //\r
+    while (!Private->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
-  //\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
+    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
-  while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
-    Ip6->Poll (Ip6);\r
-    if (Private->IsAddressOk) {\r
-      Status = EFI_SUCCESS;\r
-      break;\r
+    for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) {\r
+      if (CompareMem (Ip6Addr + Index, Address, 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
+  //\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
@@ -1357,12 +1529,106 @@ ON_EXIT:
               );\r
     gBS->CloseEvent (MappedEvt);\r
   }\r
-  if (TimeOutEvt != NULL) {\r
-    gBS->CloseEvent (TimeOutEvt);\r
+  if (Ip6Addr != NULL) {\r
+    FreePool (Ip6Addr);\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
@@ -1544,7 +1810,6 @@ PxeBcDhcp6Discover (
   UINT8                               *RequestOpt;\r
   UINT8                               *DiscoverOpt;\r
   UINTN                               ReadSize;\r
-  UINT16                              OpFlags;\r
   UINT16                              OpCode;\r
   UINT16                              OpLen;\r
   UINT32                              Xid;\r
@@ -1555,7 +1820,6 @@ PxeBcDhcp6Discover (
   Request     = Private->Dhcp6Request;\r
   SrcPort     = PXEBC_BS_DISCOVER_PORT;\r
   DestPort    = PXEBC_BS_DISCOVER_PORT;\r
-  OpFlags     = 0;\r
 \r
   if (!UseBis && Layer != NULL) {\r
     *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;\r
@@ -1599,7 +1863,7 @@ PxeBcDhcp6Discover (
 \r
   Status = PxeBc->UdpWrite (\r
                     PxeBc,\r
-                    OpFlags,\r
+                    0,\r
                     &Private->ServerIp,\r
                     &DestPort,\r
                     NULL,\r
@@ -1626,10 +1890,18 @@ PxeBcDhcp6Discover (
   }\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
-                    &Private->StationIp,\r
+                    EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,\r
+                    NULL,\r
                     &SrcPort,\r
                     &Private->ServerIp,\r
                     &DestPort,\r
@@ -1638,6 +1910,10 @@ PxeBcDhcp6Discover (
                     &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
@@ -1670,9 +1946,17 @@ PxeBcDhcp6Sarr (
   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
+  Ip6Cfg     = Private->Ip6Cfg;\r
+  Timer      = NULL;\r
 \r
   //\r
   // Build option list for the request packet.\r
@@ -1695,7 +1979,7 @@ PxeBcDhcp6Sarr (
   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
@@ -1707,8 +1991,8 @@ PxeBcDhcp6Sarr (
   // Configure the DHCPv6 instance for PXE boot.\r
   //\r
   Status = Dhcp6->Configure (Dhcp6, &Config);\r
+  FreePool (Retransmit);\r
   if (EFI_ERROR (Status)) {\r
-    FreePool (Retransmit);\r
     return Status;\r
   }\r
 \r
@@ -1726,6 +2010,52 @@ PxeBcDhcp6Sarr (
   // 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
@@ -1744,36 +2074,23 @@ PxeBcDhcp6Sarr (
   }\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
-    PxeBcUnregisterIp6Address (Private);\r
     Dhcp6->Stop (Dhcp6);\r
     return Status;\r
   }\r
-\r
-  AsciiPrint ("\n  Station IP address is ");\r
-\r
-  PxeBcShowIp6Addr (&Private->StationIp.v6);\r
-\r
+  \r
   return EFI_SUCCESS;\r
 }\r
-\r