]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
NetworkPkg: Check allocated buffer pointer before use.
[mirror_edk2.git] / NetworkPkg / UefiPxeBcDxe / PxeBcDhcp6.c
index 6ab2afa088af05fc5113a25d364211044590e3c9..327b4cf1cfc5fc0f3e1ef388d51e05e188871756 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
   Functions implementation related with DHCPv6 for UefiPxeBc Driver.\r
 \r
-  Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>\r
+  (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
+  Copyright (c) 2009 - 2018, 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
@@ -91,18 +92,20 @@ PxeBcBuildDhcp6Options (
   //\r
   // Append client option request option\r
   //\r
-  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_ORO);\r
-  OptList[Index]->OpLen      = HTONS (4);\r
+  OptList[Index]->OpCode     = HTONS (DHCP6_OPT_ORO);\r
+  OptList[Index]->OpLen      = HTONS (8);\r
   OptEnt.Oro                 = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;\r
-  OptEnt.Oro->OpCode[0]      = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL);\r
-  OptEnt.Oro->OpCode[1]      = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM);\r
+  OptEnt.Oro->OpCode[0]      = HTONS(DHCP6_OPT_BOOT_FILE_URL);\r
+  OptEnt.Oro->OpCode[1]      = HTONS(DHCP6_OPT_BOOT_FILE_PARAM);\r
+  OptEnt.Oro->OpCode[2]      = HTONS(DHCP6_OPT_DNS_SERVERS);\r
+  OptEnt.Oro->OpCode[3]      = HTONS(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 (PXEBC_DHCP6_OPT_UNDI);\r
+  OptList[Index]->OpCode     = HTONS (DHCP6_OPT_UNDI);\r
   OptList[Index]->OpLen      = HTONS ((UINT16)3);\r
   OptEnt.Undi                = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;\r
 \r
@@ -122,7 +125,7 @@ PxeBcBuildDhcp6Options (
   //\r
   // Append client system architecture option\r
   //\r
-  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_ARCH);\r
+  OptList[Index]->OpCode     = HTONS (DHCP6_OPT_ARCH);\r
   OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));\r
   OptEnt.Arch                = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;\r
   Value                      = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);\r
@@ -133,7 +136,7 @@ PxeBcBuildDhcp6Options (
   //\r
   // Append vendor class option to store the PXE class identifier.\r
   //\r
-  OptList[Index]->OpCode       = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS);\r
+  OptList[Index]->OpCode       = HTONS (DHCP6_OPT_VENDOR_CLASS);\r
   OptList[Index]->OpLen        = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));\r
   OptEnt.VendorClass           = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;\r
   OptEnt.VendorClass->Vendor   = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);\r
@@ -179,17 +182,24 @@ PxeBcBuildDhcp6Options (
   @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
+  @retval     EFI_SUCCESS                Packet is copied.\r
+  @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.\r
+\r
 **/\r
-VOID\r
+EFI_STATUS\r
 PxeBcCacheDhcp6Packet (\r
   IN EFI_DHCP6_PACKET          *Dst,\r
   IN EFI_DHCP6_PACKET          *Src\r
   )\r
 {\r
-  ASSERT (Dst->Size >= Src->Length);\r
+  if (Dst->Size < Src->Length) {\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
 \r
   CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);\r
   Dst->Length = Src->Length;\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 \r
@@ -215,10 +225,173 @@ PxeBcFreeBootFileOption (
   }\r
 }\r
 \r
+/**\r
+  Retrieve the boot server address using the EFI_DNS6_PROTOCOL.\r
+\r
+  @param[in]  Private             Pointer to PxeBc private data.\r
+  @param[in]  HostName            Pointer to buffer containing hostname.\r
+  @param[out] IpAddress           On output, pointer to buffer containing IPv6 address.\r
+\r
+  @retval EFI_SUCCESS             Operation succeeded.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate needed resources.\r
+  @retval EFI_DEVICE_ERROR        An unexpected network error occurred.\r
+  @retval Others                  Other errors as indicated.\r
+  \r
+**/\r
+EFI_STATUS\r
+PxeBcDns6 (\r
+  IN PXEBC_PRIVATE_DATA           *Private,\r
+  IN     CHAR16                   *HostName,\r
+     OUT EFI_IPv6_ADDRESS         *IpAddress                \r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  EFI_DNS6_PROTOCOL               *Dns6;\r
+  EFI_DNS6_CONFIG_DATA            Dns6ConfigData;\r
+  EFI_DNS6_COMPLETION_TOKEN       Token;\r
+  EFI_HANDLE                      Dns6Handle;\r
+  EFI_IPv6_ADDRESS                *DnsServerList;\r
+  BOOLEAN                         IsDone;\r
+  \r
+  Dns6                = NULL;\r
+  Dns6Handle          = NULL;\r
+  DnsServerList       = Private->DnsServer;\r
+  ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));\r
+\r
+  //\r
+  // Create a DNSv6 child instance and get the protocol.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             Private->Controller,\r
+             Private->Image,\r
+             &gEfiDns6ServiceBindingProtocolGuid,\r
+             &Dns6Handle\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  } \r
+  \r
+  Status = gBS->OpenProtocol (\r
+                  Dns6Handle,\r
+                  &gEfiDns6ProtocolGuid,\r
+                  (VOID **) &Dns6,\r
+                  Private->Image,\r
+                  Private->Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Configure DNS6 instance for the DNS server address and protocol.\r
+  //\r
+  ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA));\r
+  Dns6ConfigData.DnsServerCount = 1;\r
+  Dns6ConfigData.DnsServerList  = DnsServerList;\r
+  Dns6ConfigData.EnableDnsCache = TRUE;\r
+  Dns6ConfigData.Protocol       = EFI_IP_PROTO_UDP;\r
+  IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &Private->TmpStationIp.v6);\r
+  Status = Dns6->Configure (\r
+                   Dns6,\r
+                   &Dns6ConfigData\r
+                   );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  Token.Status = EFI_NOT_READY;\r
+  IsDone       = FALSE;\r
+  //\r
+  // Create event to set the  IsDone flag when name resolution is finished.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  PxeBcCommonNotify,\r
+                  &IsDone,\r
+                  &Token.Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Start asynchronous name resolution.\r
+  //\r
+  Status = Dns6->HostNameToIp (Dns6, HostName, &Token);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  while (!IsDone) {\r
+    Dns6->Poll (Dns6);\r
+  }\r
+\r
+  //\r
+  // Name resolution is done, check result.\r
+  //\r
+  Status = Token.Status;  \r
+  if (!EFI_ERROR (Status)) {\r
+    if (Token.RspData.H2AData == NULL) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Exit;\r
+    }\r
+    if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) {\r
+      Status = EFI_DEVICE_ERROR;\r
+      goto Exit;\r
+    }\r
+    //\r
+    // We just return the first IPv6 address from DNS protocol.\r
+    //\r
+    IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);\r
+    Status = EFI_SUCCESS;\r
+  }\r
+  \r
+Exit:\r
+  FreePool (HostName);\r
+\r
+  if (Token.Event != NULL) {\r
+    gBS->CloseEvent (Token.Event);\r
+  }\r
+  if (Token.RspData.H2AData != NULL) {\r
+    if (Token.RspData.H2AData->IpList != NULL) {\r
+      FreePool (Token.RspData.H2AData->IpList);\r
+    }\r
+    FreePool (Token.RspData.H2AData);\r
+  }\r
+\r
+  if (Dns6 != NULL) {\r
+    Dns6->Configure (Dns6, NULL);\r
+    \r
+    gBS->CloseProtocol (\r
+           Dns6Handle,\r
+           &gEfiDns6ProtocolGuid,\r
+           Private->Image,\r
+           Private->Controller\r
+           );\r
+  }\r
+\r
+  if (Dns6Handle != NULL) {\r
+    NetLibDestroyServiceChild (\r
+      Private->Controller,\r
+      Private->Image,\r
+      &gEfiDns6ServiceBindingProtocolGuid,\r
+      Dns6Handle\r
+      );\r
+  }\r
+\r
+  if (DnsServerList != NULL) {\r
+    FreePool (DnsServerList);\r
+  }\r
+  \r
+  return Status;  \r
+}\r
 \r
 /**\r
   Parse the Boot File URL option.\r
 \r
+  @param[in]      Private      Pointer to PxeBc private data.\r
   @param[out]     FileName     The pointer to the boot file name.\r
   @param[in, out] SrvAddr      The pointer to the boot server address.\r
   @param[in]      BootFile     The pointer to the boot file URL option data.\r
@@ -231,6 +404,7 @@ PxeBcFreeBootFileOption (
 **/\r
 EFI_STATUS\r
 PxeBcExtractBootFileUrl (\r
+  IN PXEBC_PRIVATE_DATA      *Private,\r
      OUT UINT8               **FileName,\r
   IN OUT EFI_IPv6_ADDRESS    *SrvAddr,\r
   IN     CHAR8               *BootFile,\r
@@ -246,8 +420,12 @@ PxeBcExtractBootFileUrl (
   CHAR8                      *ServerAddressOption;\r
   CHAR8                      *ServerAddress;\r
   CHAR8                      *ModeStr;\r
+  CHAR16                     *HostName;\r
+  BOOLEAN                    IpExpressedUrl;\r
+  UINTN                      Len;\r
   EFI_STATUS                 Status;\r
 \r
+  IpExpressedUrl = TRUE;\r
   //\r
   // The format of the Boot File URL option is:\r
   //\r
@@ -263,8 +441,8 @@ PxeBcExtractBootFileUrl (
   //\r
 \r
   //\r
-  // Based upon RFC 5970 and UEFI 2.3 Errata D specification, bootfile-url format\r
-  // is tftp://[SERVER_ADDRESS]/BOOTFILE_NAME\r
+  // Based upon RFC 5970 and UEFI 2.6, bootfile-url format can be\r
+  // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME or tftp://domain_name/BOOTFILE_NAME\r
   // As an example where the BOOTFILE_NAME is the EFI loader and\r
   // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.\r
   //\r
@@ -290,43 +468,76 @@ PxeBcExtractBootFileUrl (
   // Get the part of SERVER_ADDRESS string.\r
   //\r
   ServerAddressOption = TmpStr;\r
-  if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) {\r
-    FreePool (TmpStr);\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
+  if (*ServerAddressOption == PXEBC_ADDR_START_DELIMITER) {\r
+    ServerAddressOption ++;\r
+    ServerAddress = ServerAddressOption;\r
+    while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
+      ServerAddress++;\r
+    }\r
+    \r
+    if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
+      FreePool (TmpStr);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    \r
+    *ServerAddress = '\0';\r
+    \r
+    //\r
+    // Convert the string of server address to Ipv6 address format and store it.\r
+    //\r
+    Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (TmpStr);\r
+      return Status;\r
+    }\r
 \r
-  ServerAddressOption ++;\r
-  ServerAddress = ServerAddressOption;\r
-  while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
-    ServerAddress++;\r
-  }\r
+  } else {\r
+    IpExpressedUrl = FALSE;\r
+    ServerAddress = ServerAddressOption;\r
+    while (*ServerAddress != '\0' && *ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {\r
+      ServerAddress++;\r
+    }\r
 \r
-  if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
-    FreePool (TmpStr);\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
+    if (*ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {\r
+      FreePool (TmpStr);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    *ServerAddress = '\0';\r
 \r
-  *ServerAddress = '\0';\r
+    Len = AsciiStrSize (ServerAddressOption);\r
+    HostName = AllocateZeroPool (Len * sizeof (CHAR16));\r
+    if (HostName == NULL) {\r
+      FreePool (TmpStr);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    AsciiStrToUnicodeStrS (\r
+      ServerAddressOption,\r
+      HostName,\r
+      Len\r
+      );\r
 \r
-  //\r
-  // Convert the string of server address to Ipv6 address format and store it.\r
-  //\r
-  Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);\r
-  if (EFI_ERROR (Status)) {\r
-    FreePool (TmpStr);\r
-    return Status;\r
+    //\r
+    // Perform DNS resolution.\r
+    //\r
+    Status = PxeBcDns6 (Private,HostName, SrvAddr);\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (TmpStr);\r
+      return Status;\r
+    }\r
   }\r
 \r
   //\r
   // Get the part of BOOTFILE_NAME string.\r
   //\r
   BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1);\r
-  if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {\r
-    FreePool (TmpStr);\r
-    return EFI_INVALID_PARAMETER;\r
+  if (IpExpressedUrl) {\r
+    if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {\r
+      FreePool (TmpStr);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    ++BootFileNamePtr;\r
   }\r
 \r
-  ++BootFileNamePtr;\r
   BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);\r
   if (BootFileNameLen != 0 || FileName != NULL) {\r
     //\r
@@ -470,17 +681,19 @@ PxeBcParseDhcp6Packet (
   //\r
   while (Offset < Length) {\r
 \r
-    if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) {\r
+    if (NTOHS (Option->OpCode) == DHCP6_OPT_IA_NA) {\r
       Options[PXEBC_DHCP6_IDX_IA_NA] = Option;\r
-    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) {\r
+    } else if (NTOHS (Option->OpCode) == 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[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;\r
-    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) {\r
+    } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_PARAM) {\r
       Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;\r
-    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) {\r
+    } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) {\r
       Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;\r
+    } else if (NTOHS (Option->OpCode) == DHCP6_OPT_DNS_SERVERS) {\r
+      Options[PXEBC_DHCP6_IDX_DNS_SERVER] = Option;\r
     }\r
 \r
     Offset += (NTOHS (Option->OpLen) + 4);\r
@@ -488,7 +701,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
@@ -496,7 +709,7 @@ PxeBcParseDhcp6Packet (
     Option = PxeBcParseDhcp6Options (\r
                Option->Data + 12,\r
                NTOHS (Option->OpLen),\r
-               PXEBC_DHCP6_OPT_STATUS_CODE\r
+               DHCP6_OPT_STATUS_CODE\r
                );\r
     if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {\r
       IsProxyOffer = FALSE;\r
@@ -544,8 +757,11 @@ PxeBcParseDhcp6Packet (
   @param[in]  Ack                 The pointer to the DHCPv6 ack packet.\r
   @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.\r
 \r
+  @retval     EFI_SUCCESS                Cache and parse the packet successfully.\r
+  @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.\r
+\r
 **/\r
-VOID\r
+EFI_STATUS\r
 PxeBcCopyDhcp6Ack (\r
   IN PXEBC_PRIVATE_DATA   *Private,\r
   IN EFI_DHCP6_PACKET     *Ack,\r
@@ -553,10 +769,14 @@ PxeBcCopyDhcp6Ack (
   )\r
 {\r
   EFI_PXE_BASE_CODE_MODE  *Mode;\r
+  EFI_STATUS              Status;\r
 \r
   Mode = Private->PxeBc.Mode;\r
 \r
-  PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);\r
+  Status = PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
 \r
   if (Verified) {\r
     //\r
@@ -566,6 +786,8 @@ PxeBcCopyDhcp6Ack (
     CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);\r
     Mode->DhcpAckReceived = TRUE;\r
   }\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 \r
@@ -575,8 +797,11 @@ PxeBcCopyDhcp6Ack (
   @param[in]  Private               The pointer to PxeBc private data.\r
   @param[in]  OfferIndex            The received order of offer packets.\r
 \r
+  @retval     EFI_SUCCESS                Cache and parse the packet successfully.\r
+  @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.\r
+\r
 **/\r
-VOID\r
+EFI_STATUS\r
 PxeBcCopyDhcp6Proxy (\r
   IN PXEBC_PRIVATE_DATA     *Private,\r
   IN UINT32                 OfferIndex\r
@@ -584,6 +809,7 @@ PxeBcCopyDhcp6Proxy (
 {\r
   EFI_PXE_BASE_CODE_MODE    *Mode;\r
   EFI_DHCP6_PACKET          *Offer;\r
+  EFI_STATUS              Status;\r
 \r
   ASSERT (OfferIndex < Private->OfferNum);\r
   ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);\r
@@ -594,7 +820,10 @@ PxeBcCopyDhcp6Proxy (
   //\r
   // Cache the proxy offer packet and parse it.\r
   //\r
-  PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);\r
+  Status = PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
   PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);\r
 \r
   //\r
@@ -602,6 +831,8 @@ PxeBcCopyDhcp6Proxy (
   //\r
   CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);\r
   Mode->ProxyOfferReceived = TRUE;\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -664,7 +895,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
@@ -678,13 +908,12 @@ PxeBcRequestBootService (
   UINT16                              OpCode;\r
   UINT16                              OpLen;\r
   EFI_STATUS                          Status;\r
-  EFI_DHCP6_PACKET                    *ProxyOffer;\r
+  EFI_DHCP6_PACKET                    *IndexOffer;\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
+  IndexOffer  = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer;\r
   SrcPort     = PXEBC_BS_DISCOVER_PORT;\r
   DestPort    = PXEBC_BS_DISCOVER_PORT;\r
   OpFlags     = 0;\r
@@ -701,7 +930,7 @@ PxeBcRequestBootService (
   //\r
   // Build the request packet by the cached request packet before.\r
   //\r
-  Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId;\r
+  Discover->TransactionId = IndexOffer->Dhcp6.Header.TransactionId;\r
   Discover->MessageType   = Request->Dhcp6.Header.MessageType;\r
   RequestOpt              = Request->Dhcp6.Option;\r
   DiscoverOpt             = Discover->DhcpOptions;\r
@@ -711,29 +940,31 @@ PxeBcRequestBootService (
   //\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
+  if (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl) {  \r
+    Option = PxeBcDhcp6SeekOption (\r
+               IndexOffer->Dhcp6.Option,\r
+               IndexOffer->Length - 4,\r
+               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
+    // 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
 \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
+        OpCode != DHCP6_OPT_SERVER_ID\r
         ) {\r
       //\r
       // Copy all the options except IA option and Server ID\r
@@ -752,7 +983,7 @@ PxeBcRequestBootService (
   Option = PxeBcDhcp6SeekOption (\r
              Discover->DhcpOptions,\r
              (UINT32)(RequestLen - 4),\r
-             PXEBC_DHCP6_OPT_ELAPSED_TIME\r
+             DHCP6_OPT_ELAPSED_TIME\r
              );\r
   if (Option != NULL) {\r
     CalcElapsedTime (Private);\r
@@ -794,8 +1025,8 @@ PxeBcRequestBootService (
     \r
   Status = PxeBc->UdpRead (\r
                     PxeBc,\r
-                    EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP,\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
@@ -850,7 +1081,7 @@ PxeBcRetryDhcp6Binl (
   Mode                  = Private->PxeBc.Mode;\r
   Private->IsDoDiscover = FALSE;\r
   Offer                 = &Private->OfferBuffer[Index].Dhcp6;\r
-  if (Offer->OfferType == PxeOfferTypeDhcpBinl) {\r
+  if (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {\r
     //\r
     // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.\r
     //\r
@@ -865,6 +1096,7 @@ PxeBcRetryDhcp6Binl (
     // Parse out the next server address from the last offer, and store it\r
     //\r
     Status = PxeBcExtractBootFileUrl (\r
+               Private,\r
                &Private->BootFileName,\r
                &Private->ServerIp.v6,\r
                (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
@@ -917,8 +1149,10 @@ PxeBcRetryDhcp6Binl (
   @param[in]  Private               The pointer to PXEBC_PRIVATE_DATA.\r
   @param[in]  RcvdOffer             The pointer to the received offer packet.\r
 \r
+  @retval     EFI_SUCCESS      Cache and parse the packet successfully.\r
+  @retval     Others           Operation failed.\r
 **/\r
-VOID\r
+EFI_STATUS\r
 PxeBcCacheDhcp6Offer (\r
   IN PXEBC_PRIVATE_DATA     *Private,\r
   IN EFI_DHCP6_PACKET       *RcvdOffer\r
@@ -927,6 +1161,7 @@ PxeBcCacheDhcp6Offer (
   PXEBC_DHCP6_PACKET_CACHE  *Cache6;\r
   EFI_DHCP6_PACKET          *Offer;\r
   PXEBC_OFFER_TYPE          OfferType;\r
+  EFI_STATUS                Status;\r
 \r
   Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;\r
   Offer  = &Cache6->Packet.Offer;\r
@@ -934,13 +1169,16 @@ PxeBcCacheDhcp6Offer (
   //\r
   // Cache the content of DHCPv6 packet firstly.\r
   //\r
-  PxeBcCacheDhcp6Packet (Offer, RcvdOffer);\r
+  Status = PxeBcCacheDhcp6Packet (Offer, RcvdOffer);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
 \r
   //\r
   // Validate the DHCPv6 packet, and parse the options and offer type.\r
   //\r
   if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {\r
-    return ;\r
+    return EFI_ABORTED;\r
   }\r
 \r
   //\r
@@ -962,14 +1200,15 @@ PxeBcCacheDhcp6Offer (
       //\r
       Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
       Private->OfferCount[OfferType]++;\r
-    } else if (Private->OfferCount[OfferType] > 0) {\r
+    } else if ((OfferType == PxeOfferTypeProxyPxe10 || OfferType == PxeOfferTypeProxyWfm11a) && \r
+                 Private->OfferCount[OfferType] < 1) {\r
       //\r
       // Only cache the first PXE10/WFM11a offer, and discard the others.\r
       //\r
       Private->OfferIndex[OfferType][0] = Private->OfferNum;\r
       Private->OfferCount[OfferType]    = 1;\r
     } else {\r
-      return;\r
+      return EFI_ABORTED;\r
     }\r
   } else {\r
     //\r
@@ -980,6 +1219,8 @@ PxeBcCacheDhcp6Offer (
   }\r
 \r
   Private->OfferNum++;\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 \r
@@ -1094,8 +1335,10 @@ PxeBcSelectDhcp6Offer (
 \r
   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.\r
 \r
-  @retval     EFI_SUCCESS         Handled the DHCPv6 offer packet successfully.\r
-  @retval     EFI_NO_RESPONSE     No response to the following request packet.\r
+  @retval     EFI_SUCCESS           Handled the DHCPv6 offer packet successfully.\r
+  @retval     EFI_NO_RESPONSE       No response to the following request packet.\r
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.\r
+  @retval     EFI_BUFFER_TOO_SMALL  Can't cache the offer pacet.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -1116,6 +1359,17 @@ PxeBcHandleDhcp6Offer (
   Cache6      = &Private->OfferBuffer[SelectIndex].Dhcp6;\r
   Status      = EFI_SUCCESS;\r
 \r
+  //\r
+  // First try to cache DNS server address if DHCP6 offer provides.\r
+  //\r
+  if (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER] != NULL) {\r
+    Private->DnsServer = AllocateZeroPool (NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->OpLen));\r
+    if (Private->DnsServer == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    CopyMem (Private->DnsServer, Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->Data, sizeof (EFI_IPv6_ADDRESS));\r
+  }\r
+\r
   if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {\r
     //\r
     // DhcpBinl offer is selected, so need try to request bootfilename by this offer.\r
@@ -1194,7 +1448,7 @@ PxeBcHandleDhcp6Offer (
         //\r
         // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.\r
         //\r
-        PxeBcCopyDhcp6Proxy (Private, ProxyIndex);\r
+        Status = PxeBcCopyDhcp6Proxy (Private, ProxyIndex);\r
       }\r
     } else {\r
       //\r
@@ -1208,7 +1462,7 @@ PxeBcHandleDhcp6Offer (
     //\r
     // All PXE boot information is ready by now.\r
     //\r
-    PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);\r
+    Status = PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);\r
     Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;\r
   }\r
 \r
@@ -1235,9 +1489,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
@@ -1256,36 +1629,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
-  UINT64                           DadTriggerTime;\r
-  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS    DadXmits;\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
@@ -1305,34 +1680,6 @@ PxeBcRegisterIp6Address (
     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
-  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
@@ -1347,6 +1694,7 @@ PxeBcRegisterIp6Address (
     goto ON_EXIT;\r
   }\r
 \r
+  Private->IsAddressOk = FALSE;\r
   Status = Ip6Cfg->RegisterDataNotify (\r
                      Ip6Cfg,\r
                      Ip6ConfigDataTypeManualAddress,\r
@@ -1364,20 +1712,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
+    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, 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
-  // Start the 5 secondes timer to wait for setting address.\r
+  // Set the default gateway address back if needed.\r
   //\r
-  Status = EFI_NO_MAPPING;\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
-    if (Private->IsAddressOk) {\r
-      Status = EFI_SUCCESS;\r
-      break;\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
@@ -1390,12 +1784,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
@@ -1469,6 +1957,14 @@ PxeBcDhcp6CallBack (
   switch (Dhcp6Event) {\r
 \r
   case Dhcp6SendSolicit:\r
+    if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {\r
+      //\r
+      // If the to be sent packet exceeds the maximum length, abort the DHCP process.\r
+      //\r
+      Status = EFI_ABORTED;\r
+      break;\r
+    }\r
+    \r
     //\r
     // Record the first Solicate msg time\r
     //\r
@@ -1484,6 +1980,12 @@ PxeBcDhcp6CallBack (
 \r
   case Dhcp6RcvdAdvertise:\r
     Status = EFI_NOT_READY;\r
+    if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {\r
+      //\r
+      // Ignore the incoming packets which exceed the maximum length.\r
+      //\r
+      break;\r
+    }\r
     if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {\r
       //\r
       // Cache the dhcp offers to OfferBuffer[] for select later, and record\r
@@ -1494,6 +1996,14 @@ PxeBcDhcp6CallBack (
     break;\r
 \r
   case Dhcp6SendRequest:\r
+    if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {\r
+      //\r
+      // If the to be sent packet exceeds the maximum length, abort the DHCP process.\r
+      //\r
+      Status = EFI_ABORTED;\r
+      break;\r
+    }\r
+    \r
     //\r
     // Store the request packet as seed packet for discover.\r
     //\r
@@ -1520,6 +2030,9 @@ PxeBcDhcp6CallBack (
       SelectAd   = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;\r
       *NewPacket = AllocateZeroPool (SelectAd->Size);\r
       ASSERT (*NewPacket != NULL);\r
+      if (*NewPacket == NULL) {\r
+        return EFI_ABORTED;\r
+      }\r
       CopyMem (*NewPacket, SelectAd, SelectAd->Size);\r
     }\r
     break;\r
@@ -1530,7 +2043,10 @@ PxeBcDhcp6CallBack (
     // without verification.\r
     //\r
     ASSERT (Private->SelectIndex != 0);\r
-    PxeBcCopyDhcp6Ack (Private, Packet, FALSE);\r
+    Status = PxeBcCopyDhcp6Ack (Private, Packet, FALSE);\r
+    if (EFI_ERROR (Status)) {\r
+      Status = EFI_ABORTED;\r
+    }\r
     break;\r
 \r
   default:\r
@@ -1577,7 +2093,6 @@ PxeBcDhcp6Discover (
   UINT8                               *RequestOpt;\r
   UINT8                               *DiscoverOpt;\r
   UINTN                               ReadSize;\r
-  UINT16                              OpFlags;\r
   UINT16                              OpCode;\r
   UINT16                              OpLen;\r
   UINT32                              Xid;\r
@@ -1588,7 +2103,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
@@ -1632,7 +2146,7 @@ PxeBcDhcp6Discover (
 \r
   Status = PxeBc->UdpWrite (\r
                     PxeBc,\r
-                    OpFlags,\r
+                    0,\r
                     &Private->ServerIp,\r
                     &DestPort,\r
                     NULL,\r
@@ -1669,8 +2183,8 @@ PxeBcDhcp6Discover (
   \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
@@ -1748,7 +2262,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
@@ -1842,37 +2356,29 @@ PxeBcDhcp6Sarr (
     return Status;\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
+  ASSERT ((Mode.Ia != NULL) && (Mode.Ia->State == Dhcp6Bound));\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
+  if (Mode.ClientId != NULL) {\r
+    FreePool (Mode.ClientId);\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
+  if (Mode.Ia != NULL) {\r
+    FreePool (Mode.Ia);\r
   }\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