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