| /** @file | |
| Functions implementation related with DHCPv4 for HTTP boot driver. | |
| Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials are licensed and made available under | |
| the terms and conditions of the BSD License that accompanies this distribution. | |
| The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php. | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "HttpBootDxe.h" | |
| // | |
| // This is a map from the interested DHCP4 option tags' index to the tag value. | |
| // | |
| UINT8 mInterestedDhcp4Tags[HTTP_BOOT_DHCP4_TAG_INDEX_MAX] = { | |
| DHCP4_TAG_BOOTFILE_LEN, | |
| DHCP4_TAG_OVERLOAD, | |
| DHCP4_TAG_MSG_TYPE, | |
| DHCP4_TAG_SERVER_ID, | |
| DHCP4_TAG_VENDOR_CLASS_ID, | |
| DHCP4_TAG_BOOTFILE, | |
| DHCP4_TAG_DNS_SERVER | |
| }; | |
| // | |
| // There are 4 times retries with the value of 4, 8, 16 and 32, refers to UEFI 2.5 spec. | |
| // | |
| UINT32 mHttpDhcpTimeout[4] = {4, 8, 16, 32}; | |
| /** | |
| Build the options buffer for the DHCPv4 request packet. | |
| @param[in] Private Pointer to HTTP boot driver private data. | |
| @param[out] OptList Pointer to the option pointer array. | |
| @param[in] Buffer Pointer to the buffer to contain the option list. | |
| @return Index The count of the built-in options. | |
| **/ | |
| UINT32 | |
| HttpBootBuildDhcp4Options ( | |
| IN HTTP_BOOT_PRIVATE_DATA *Private, | |
| OUT EFI_DHCP4_PACKET_OPTION **OptList, | |
| IN UINT8 *Buffer | |
| ) | |
| { | |
| HTTP_BOOT_DHCP4_OPTION_ENTRY OptEnt; | |
| UINT16 Value; | |
| UINT32 Index; | |
| Index = 0; | |
| OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer; | |
| // | |
| // Append parameter request list option. | |
| // | |
| OptList[Index]->OpCode = DHCP4_TAG_PARA_LIST; | |
| OptList[Index]->Length = 27; | |
| OptEnt.Para = (HTTP_BOOT_DHCP4_OPTION_PARA *) OptList[Index]->Data; | |
| OptEnt.Para->ParaList[0] = DHCP4_TAG_NETMASK; | |
| OptEnt.Para->ParaList[1] = DHCP4_TAG_TIME_OFFSET; | |
| OptEnt.Para->ParaList[2] = DHCP4_TAG_ROUTER; | |
| OptEnt.Para->ParaList[3] = DHCP4_TAG_TIME_SERVER; | |
| OptEnt.Para->ParaList[4] = DHCP4_TAG_NAME_SERVER; | |
| OptEnt.Para->ParaList[5] = DHCP4_TAG_DNS_SERVER; | |
| OptEnt.Para->ParaList[6] = DHCP4_TAG_HOSTNAME; | |
| OptEnt.Para->ParaList[7] = DHCP4_TAG_BOOTFILE_LEN; | |
| OptEnt.Para->ParaList[8] = DHCP4_TAG_DOMAINNAME; | |
| OptEnt.Para->ParaList[9] = DHCP4_TAG_ROOTPATH; | |
| OptEnt.Para->ParaList[10] = DHCP4_TAG_EXTEND_PATH; | |
| OptEnt.Para->ParaList[11] = DHCP4_TAG_EMTU; | |
| OptEnt.Para->ParaList[12] = DHCP4_TAG_TTL; | |
| OptEnt.Para->ParaList[13] = DHCP4_TAG_BROADCAST; | |
| OptEnt.Para->ParaList[14] = DHCP4_TAG_NIS_DOMAIN; | |
| OptEnt.Para->ParaList[15] = DHCP4_TAG_NIS_SERVER; | |
| OptEnt.Para->ParaList[16] = DHCP4_TAG_NTP_SERVER; | |
| OptEnt.Para->ParaList[17] = DHCP4_TAG_VENDOR; | |
| OptEnt.Para->ParaList[18] = DHCP4_TAG_REQUEST_IP; | |
| OptEnt.Para->ParaList[19] = DHCP4_TAG_LEASE; | |
| OptEnt.Para->ParaList[20] = DHCP4_TAG_SERVER_ID; | |
| OptEnt.Para->ParaList[21] = DHCP4_TAG_T1; | |
| OptEnt.Para->ParaList[22] = DHCP4_TAG_T2; | |
| OptEnt.Para->ParaList[23] = DHCP4_TAG_VENDOR_CLASS_ID; | |
| OptEnt.Para->ParaList[25] = DHCP4_TAG_BOOTFILE; | |
| OptEnt.Para->ParaList[26] = DHCP4_TAG_UUID; | |
| Index++; | |
| OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); | |
| // | |
| // Append UUID/Guid-based client identifier option | |
| // | |
| OptList[Index]->OpCode = DHCP4_TAG_UUID; | |
| OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UUID); | |
| OptEnt.Uuid = (HTTP_BOOT_DHCP4_OPTION_UUID *) OptList[Index]->Data; | |
| OptEnt.Uuid->Type = 0; | |
| if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) { | |
| // | |
| // Zero the Guid to indicate NOT programable if failed to get system Guid. | |
| // | |
| ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID)); | |
| } | |
| Index++; | |
| OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); | |
| // | |
| // Append client network device interface option | |
| // | |
| OptList[Index]->OpCode = DHCP4_TAG_UNDI; | |
| OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UNDI); | |
| OptEnt.Undi = (HTTP_BOOT_DHCP4_OPTION_UNDI *) OptList[Index]->Data; | |
| if (Private->Nii != NULL) { | |
| OptEnt.Undi->Type = Private->Nii->Type; | |
| OptEnt.Undi->MajorVer = Private->Nii->MajorVer; | |
| OptEnt.Undi->MinorVer = Private->Nii->MinorVer; | |
| } else { | |
| OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; | |
| OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; | |
| OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; | |
| } | |
| Index++; | |
| OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); | |
| // | |
| // Append client system architecture option | |
| // | |
| OptList[Index]->OpCode = DHCP4_TAG_ARCH; | |
| OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_ARCH); | |
| OptEnt.Arch = (HTTP_BOOT_DHCP4_OPTION_ARCH *) OptList[Index]->Data; | |
| Value = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE); | |
| CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); | |
| Index++; | |
| OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); | |
| // | |
| // Append vendor class identify option | |
| // | |
| OptList[Index]->OpCode = DHCP4_TAG_VENDOR_CLASS_ID; | |
| OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_CLID); | |
| OptEnt.Clid = (HTTP_BOOT_DHCP4_OPTION_CLID *) OptList[Index]->Data; | |
| CopyMem ( | |
| OptEnt.Clid, | |
| DEFAULT_CLASS_ID_DATA, | |
| sizeof (HTTP_BOOT_DHCP4_OPTION_CLID) | |
| ); | |
| HttpBootUintnToAscDecWithFormat ( | |
| EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE, | |
| OptEnt.Clid->ArchitectureType, | |
| sizeof (OptEnt.Clid->ArchitectureType) | |
| ); | |
| if (Private->Nii != NULL) { | |
| CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName)); | |
| HttpBootUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor)); | |
| HttpBootUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor)); | |
| } | |
| Index++; | |
| return Index; | |
| } | |
| /** | |
| Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer. | |
| @param[in] Buffer Pointer to the option buffer. | |
| @param[in] Length Length of the option buffer. | |
| @param[in] OptTag Tag of the required option. | |
| @retval NULL Failed to find the required option. | |
| @retval Others The position of the required option. | |
| **/ | |
| EFI_DHCP4_PACKET_OPTION * | |
| HttpBootParseDhcp4Options ( | |
| IN UINT8 *Buffer, | |
| IN UINT32 Length, | |
| IN UINT8 OptTag | |
| ) | |
| { | |
| EFI_DHCP4_PACKET_OPTION *Option; | |
| UINT32 Offset; | |
| Option = (EFI_DHCP4_PACKET_OPTION *) Buffer; | |
| Offset = 0; | |
| while (Offset < Length && Option->OpCode != DHCP4_TAG_EOP) { | |
| if (Option->OpCode == OptTag) { | |
| // | |
| // Found the required option. | |
| // | |
| return Option; | |
| } | |
| // | |
| // Skip the current option to the next. | |
| // | |
| if (Option->OpCode == DHCP4_TAG_PAD) { | |
| Offset++; | |
| } else { | |
| Offset += Option->Length + 2; | |
| } | |
| Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset); | |
| } | |
| return NULL; | |
| } | |
| /** | |
| Cache the DHCPv4 packet. | |
| @param[in] Dst Pointer to the cache buffer for DHCPv4 packet. | |
| @param[in] Src Pointer to the DHCPv4 packet to be cached. | |
| **/ | |
| VOID | |
| HttpBootCacheDhcp4Packet ( | |
| IN EFI_DHCP4_PACKET *Dst, | |
| IN EFI_DHCP4_PACKET *Src | |
| ) | |
| { | |
| ASSERT (Dst->Size >= Src->Length); | |
| CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); | |
| Dst->Length = Src->Length; | |
| } | |
| /** | |
| Parse the cached DHCPv4 packet, including all the options. | |
| @param[in] Cache4 Pointer to cached DHCPv4 packet. | |
| @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. | |
| @retval EFI_DEVICE_ERROR Failed to parse an invalid packet. | |
| **/ | |
| EFI_STATUS | |
| HttpBootParseDhcp4Packet ( | |
| IN HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4 | |
| ) | |
| { | |
| EFI_DHCP4_PACKET *Offer; | |
| EFI_DHCP4_PACKET_OPTION **Options; | |
| UINTN Index; | |
| EFI_DHCP4_PACKET_OPTION *Option; | |
| BOOLEAN IsProxyOffer; | |
| BOOLEAN IsHttpOffer; | |
| BOOLEAN IsDnsOffer; | |
| BOOLEAN IpExpressedUri; | |
| UINT8 *Ptr8; | |
| EFI_STATUS Status; | |
| HTTP_BOOT_OFFER_TYPE OfferType; | |
| EFI_IPv4_ADDRESS IpAddr; | |
| BOOLEAN FileFieldOverloaded; | |
| IsDnsOffer = FALSE; | |
| IpExpressedUri = FALSE; | |
| IsProxyOffer = FALSE; | |
| IsHttpOffer = FALSE; | |
| FileFieldOverloaded = FALSE; | |
| ZeroMem (Cache4->OptList, sizeof (Cache4->OptList)); | |
| Offer = &Cache4->Packet.Offer; | |
| Options = Cache4->OptList; | |
| // | |
| // Parse DHCPv4 options in this offer, and store the pointers. | |
| // First, try to parse DHCPv4 options from the DHCP optional parameters field. | |
| // | |
| for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { | |
| Options[Index] = HttpBootParseDhcp4Options ( | |
| Offer->Dhcp4.Option, | |
| GET_OPTION_BUFFER_LEN (Offer), | |
| mInterestedDhcp4Tags[Index] | |
| ); | |
| } | |
| // | |
| // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132. | |
| // If yes, try to parse options from the BootFileName field, then ServerName field. | |
| // | |
| Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD]; | |
| if (Option != NULL) { | |
| if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_FILE) != 0) { | |
| FileFieldOverloaded = TRUE; | |
| for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { | |
| if (Options[Index] == NULL) { | |
| Options[Index] = HttpBootParseDhcp4Options ( | |
| (UINT8 *) Offer->Dhcp4.Header.BootFileName, | |
| sizeof (Offer->Dhcp4.Header.BootFileName), | |
| mInterestedDhcp4Tags[Index] | |
| ); | |
| } | |
| } | |
| } | |
| if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME) != 0) { | |
| for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { | |
| if (Options[Index] == NULL) { | |
| Options[Index] = HttpBootParseDhcp4Options ( | |
| (UINT8 *) Offer->Dhcp4.Header.ServerName, | |
| sizeof (Offer->Dhcp4.Header.ServerName), | |
| mInterestedDhcp4Tags[Index] | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // The offer with "yiaddr" is a proxy offer. | |
| // | |
| if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) { | |
| IsProxyOffer = TRUE; | |
| } | |
| // | |
| // The offer with "HTTPClient" is a Http offer. | |
| // | |
| Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID]; | |
| if ((Option != NULL) && (Option->Length >= 9) && | |
| (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) { | |
| IsHttpOffer = TRUE; | |
| } | |
| // | |
| // The offer with Domain Server is a DNS offer. | |
| // | |
| Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER]; | |
| if (Option != NULL) { | |
| IsDnsOffer = TRUE; | |
| } | |
| // | |
| // Parse boot file name: | |
| // Boot URI information is provided thru 'file' field in DHCP Header or option 67. | |
| // According to RFC 2132, boot file name should be read from DHCP option 67 (bootfile name) if present. | |
| // Otherwise, read from boot file field in DHCP header. | |
| // | |
| if (Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { | |
| // | |
| // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null | |
| // terminated string. So force to append null terminated character at the end of string. | |
| // | |
| Ptr8 = (UINT8*)&Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data[0]; | |
| Ptr8 += Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Length; | |
| if (*(Ptr8 - 1) != '\0') { | |
| *Ptr8 = '\0'; | |
| } | |
| } else if (!FileFieldOverloaded && Offer->Dhcp4.Header.BootFileName[0] != 0) { | |
| // | |
| // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it. | |
| // Do not count dhcp option header here, or else will destroy the serverhostname. | |
| // | |
| Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) | |
| (&Offer->Dhcp4.Header.BootFileName[0] - | |
| OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0])); | |
| } | |
| // | |
| // Http offer must have a boot URI. | |
| // | |
| if (IsHttpOffer && Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| // | |
| // Try to retrieve the IP of HTTP server from URI. | |
| // | |
| if (IsHttpOffer) { | |
| Status = HttpParseUrl ( | |
| (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, | |
| (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data), | |
| FALSE, | |
| &Cache4->UriParser | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| Status = HttpUrlGetIp4 ( | |
| (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, | |
| Cache4->UriParser, | |
| &IpAddr | |
| ); | |
| IpExpressedUri = !EFI_ERROR (Status); | |
| } | |
| // | |
| // Determine offer type of the DHCPv4 packet. | |
| // | |
| if (IsHttpOffer) { | |
| if (IpExpressedUri) { | |
| if (IsProxyOffer) { | |
| OfferType = HttpOfferTypeProxyIpUri; | |
| } else { | |
| OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : HttpOfferTypeDhcpIpUri; | |
| } | |
| } else { | |
| if (!IsProxyOffer) { | |
| OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri; | |
| } else { | |
| OfferType = HttpOfferTypeProxyNameUri; | |
| } | |
| } | |
| } else { | |
| if (!IsProxyOffer) { | |
| OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly; | |
| } else { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| Cache4->OfferType = OfferType; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount. | |
| @param[in] Private Pointer to HTTP boot driver private data. | |
| @param[in] RcvdOffer Pointer to the received offer packet. | |
| **/ | |
| VOID | |
| HttpBootCacheDhcp4Offer ( | |
| IN HTTP_BOOT_PRIVATE_DATA *Private, | |
| IN EFI_DHCP4_PACKET *RcvdOffer | |
| ) | |
| { | |
| HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4; | |
| EFI_DHCP4_PACKET *Offer; | |
| HTTP_BOOT_OFFER_TYPE OfferType; | |
| ASSERT (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM); | |
| Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4; | |
| Offer = &Cache4->Packet.Offer; | |
| // | |
| // Cache the content of DHCPv4 packet firstly. | |
| // | |
| HttpBootCacheDhcp4Packet (Offer, RcvdOffer); | |
| // | |
| // Validate the DHCPv4 packet, and parse the options and offer type. | |
| // | |
| if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) { | |
| return; | |
| } | |
| // | |
| // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. | |
| // | |
| OfferType = Cache4->OfferType; | |
| ASSERT (OfferType < HttpOfferTypeMax); | |
| ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM); | |
| Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; | |
| Private->OfferCount[OfferType]++; | |
| Private->OfferNum++; | |
| } | |
| /** | |
| Select an DHCPv4 or DHCP6 offer, and record SelectIndex and SelectProxyType. | |
| @param[in] Private Pointer to HTTP boot driver private data. | |
| **/ | |
| VOID | |
| HttpBootSelectDhcpOffer ( | |
| IN HTTP_BOOT_PRIVATE_DATA *Private | |
| ) | |
| { | |
| Private->SelectIndex = 0; | |
| Private->SelectProxyType = HttpOfferTypeMax; | |
| if (Private->FilePathUri != NULL) { | |
| // | |
| // We are in home environment, the URI is already specified. | |
| // Just need to choose a DHCP offer. | |
| // The offer with DNS server address takes priority here. | |
| // | |
| if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; | |
| } else if (Private->OfferCount[HttpOfferTypeDhcpIpUriDns] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][0] + 1; | |
| } else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1; | |
| } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1; | |
| } else if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1; | |
| } | |
| } else { | |
| // | |
| // We are in corporate environment. | |
| // | |
| // Priority1: HttpOfferTypeDhcpIpUri or HttpOfferTypeDhcpIpUriDns | |
| // Priority2: HttpOfferTypeDhcpNameUriDns | |
| // Priority3: HttpOfferTypeDhcpOnly + HttpOfferTypeProxyIpUri | |
| // Priority4: HttpOfferTypeDhcpDns + HttpOfferTypeProxyIpUri | |
| // Priority5: HttpOfferTypeDhcpDns + HttpOfferTypeProxyNameUri | |
| // Priority6: HttpOfferTypeDhcpDns + HttpOfferTypeDhcpNameUri | |
| // | |
| if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1; | |
| } else if (Private->OfferCount[HttpOfferTypeDhcpIpUriDns] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][0] + 1; | |
| }else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1; | |
| } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0 && | |
| Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1; | |
| Private->SelectProxyType = HttpOfferTypeProxyIpUri; | |
| } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && | |
| Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; | |
| Private->SelectProxyType = HttpOfferTypeProxyIpUri; | |
| } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && | |
| Private->OfferCount[HttpOfferTypeProxyNameUri] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; | |
| Private->SelectProxyType = HttpOfferTypeProxyNameUri; | |
| } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && | |
| Private->OfferCount[HttpOfferTypeDhcpNameUri] > 0) { | |
| Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; | |
| Private->SelectProxyType = HttpOfferTypeDhcpNameUri; | |
| } | |
| } | |
| } | |
| /** | |
| EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver | |
| to intercept events that occurred in the configuration process. | |
| @param[in] This Pointer to the EFI DHCPv4 Protocol. | |
| @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure(). | |
| @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver. | |
| @param[in] Dhcp4Event The event that occurs in the current state, which usually means a | |
| state transition. | |
| @param[in] Packet The DHCPv4 packet that is going to be sent or already received. | |
| @param[out] NewPacket The packet that is used to replace the above Packet. | |
| @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. | |
| @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol | |
| driver will continue to wait for more DHCPOFFER packets until the | |
| retry timeout expires. | |
| @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process | |
| and return to the Dhcp4Init or Dhcp4InitReboot state. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| HttpBootDhcp4CallBack ( | |
| IN EFI_DHCP4_PROTOCOL *This, | |
| IN VOID *Context, | |
| IN EFI_DHCP4_STATE CurrentState, | |
| IN EFI_DHCP4_EVENT Dhcp4Event, | |
| IN EFI_DHCP4_PACKET *Packet OPTIONAL, | |
| OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL | |
| ) | |
| { | |
| HTTP_BOOT_PRIVATE_DATA *Private; | |
| EFI_DHCP4_PACKET_OPTION *MaxMsgSize; | |
| UINT16 Value; | |
| EFI_STATUS Status; | |
| if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) { | |
| return EFI_SUCCESS; | |
| } | |
| Private = (HTTP_BOOT_PRIVATE_DATA *) Context; | |
| // | |
| // Override the Maximum DHCP Message Size. | |
| // | |
| MaxMsgSize = HttpBootParseDhcp4Options ( | |
| Packet->Dhcp4.Option, | |
| GET_OPTION_BUFFER_LEN (Packet), | |
| DHCP4_TAG_MAXMSG | |
| ); | |
| if (MaxMsgSize != NULL) { | |
| Value = HTONS (HTTP_BOOT_DHCP4_PACKET_MAX_SIZE); | |
| CopyMem (MaxMsgSize->Data, &Value, sizeof (Value)); | |
| } | |
| Status = EFI_SUCCESS; | |
| switch (Dhcp4Event) { | |
| case Dhcp4RcvdOffer: | |
| Status = EFI_NOT_READY; | |
| if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { | |
| // | |
| // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record | |
| // the OfferIndex and OfferCount. | |
| // | |
| HttpBootCacheDhcp4Offer (Private, Packet); | |
| } | |
| break; | |
| case Dhcp4SelectOffer: | |
| // | |
| // Select offer according to the priority in UEFI spec, and record the SelectIndex | |
| // and SelectProxyType. | |
| // | |
| HttpBootSelectDhcpOffer (Private); | |
| if (Private->SelectIndex == 0) { | |
| Status = EFI_ABORTED; | |
| } else { | |
| *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| return Status; | |
| } | |
| /** | |
| This function will register the IPv4 gateway address to the network device. | |
| @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. | |
| @retval EFI_SUCCESS The new IP configuration has been configured successfully. | |
| @retval Others Failed to configure the address. | |
| **/ | |
| EFI_STATUS | |
| HttpBootRegisterIp4Gateway ( | |
| IN HTTP_BOOT_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; | |
| ASSERT (!Private->UsingIpv6); | |
| Ip4Config2 = Private->Ip4Config2; | |
| // | |
| // Configure the gateway if valid. | |
| // | |
| if (!EFI_IP4_EQUAL (&Private->GatewayIp, &mZeroIp4Addr)) { | |
| Status = Ip4Config2->SetData ( | |
| Ip4Config2, | |
| Ip4Config2DataTypeGateway, | |
| sizeof (EFI_IPv4_ADDRESS), | |
| &Private->GatewayIp | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function will register the default DNS addresses to the network device. | |
| @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. | |
| @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes. | |
| @param[in] DnsServerData Point a list of DNS server address in an array | |
| of EFI_IPv4_ADDRESS instances. | |
| @retval EFI_SUCCESS The DNS configuration has been configured successfully. | |
| @retval Others Failed to configure the address. | |
| **/ | |
| EFI_STATUS | |
| HttpBootRegisterIp4Dns ( | |
| IN HTTP_BOOT_PRIVATE_DATA *Private, | |
| IN UINTN DataLength, | |
| IN VOID *DnsServerData | |
| ) | |
| { | |
| EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; | |
| ASSERT (!Private->UsingIpv6); | |
| Ip4Config2 = Private->Ip4Config2; | |
| return Ip4Config2->SetData ( | |
| Ip4Config2, | |
| Ip4Config2DataTypeDnsServer, | |
| DataLength, | |
| DnsServerData | |
| ); | |
| } | |
| /** | |
| This function will switch the IP4 configuration policy to Static. | |
| @param[in] Private Pointer to HTTP boot driver private data. | |
| @retval EFI_SUCCESS The policy is already configured to static. | |
| @retval Others Other error as indicated.. | |
| **/ | |
| EFI_STATUS | |
| HttpBootSetIp4Policy ( | |
| IN HTTP_BOOT_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_IP4_CONFIG2_POLICY Policy; | |
| EFI_STATUS Status; | |
| EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; | |
| UINTN DataSize; | |
| Ip4Config2 = Private->Ip4Config2; | |
| DataSize = sizeof (EFI_IP4_CONFIG2_POLICY); | |
| Status = Ip4Config2->GetData ( | |
| Ip4Config2, | |
| Ip4Config2DataTypePolicy, | |
| &DataSize, | |
| &Policy | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| if (Policy != Ip4Config2PolicyStatic) { | |
| Policy = Ip4Config2PolicyStatic; | |
| Status= Ip4Config2->SetData ( | |
| Ip4Config2, | |
| Ip4Config2DataTypePolicy, | |
| sizeof (EFI_IP4_CONFIG2_POLICY), | |
| &Policy | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information. | |
| @param[in] Private Pointer to HTTP boot driver private data. | |
| @retval EFI_SUCCESS The D.O.R.A process successfully finished. | |
| @retval Others Failed to finish the D.O.R.A process. | |
| **/ | |
| EFI_STATUS | |
| HttpBootDhcp4Dora ( | |
| IN HTTP_BOOT_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_DHCP4_PROTOCOL *Dhcp4; | |
| UINT32 OptCount; | |
| EFI_DHCP4_PACKET_OPTION *OptList[HTTP_BOOT_DHCP4_OPTION_MAX_NUM]; | |
| UINT8 Buffer[HTTP_BOOT_DHCP4_OPTION_MAX_SIZE]; | |
| EFI_DHCP4_CONFIG_DATA Config; | |
| EFI_STATUS Status; | |
| EFI_DHCP4_MODE_DATA Mode; | |
| Dhcp4 = Private->Dhcp4; | |
| ASSERT (Dhcp4 != NULL); | |
| Status = HttpBootSetIp4Policy (Private); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Build option list for the request packet. | |
| // | |
| OptCount = HttpBootBuildDhcp4Options (Private, OptList, Buffer); | |
| ASSERT (OptCount > 0); | |
| ZeroMem (&Config, sizeof(Config)); | |
| Config.OptionCount = OptCount; | |
| Config.OptionList = OptList; | |
| Config.Dhcp4Callback = HttpBootDhcp4CallBack; | |
| Config.CallbackContext = Private; | |
| Config.DiscoverTryCount = HTTP_BOOT_DHCP_RETRIES; | |
| Config.DiscoverTimeout = mHttpDhcpTimeout; | |
| // | |
| // Configure the DHCPv4 instance for HTTP boot. | |
| // | |
| Status = Dhcp4->Configure (Dhcp4, &Config); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Initialize the record fields for DHCPv4 offer in private data. | |
| // | |
| Private->OfferNum = 0; | |
| ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); | |
| ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); | |
| // | |
| // Start DHCPv4 D.O.R.A. process to acquire IPv4 address. | |
| // | |
| Status = Dhcp4->Start (Dhcp4, NULL); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_EXIT; | |
| } | |
| // | |
| // Get the acquired IPv4 address and store them. | |
| // | |
| Status = Dhcp4->GetModeData (Dhcp4, &Mode); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_EXIT; | |
| } | |
| ASSERT (Mode.State == Dhcp4Bound); | |
| CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); | |
| CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); | |
| CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); | |
| Status = HttpBootRegisterIp4Gateway (Private); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_EXIT; | |
| } | |
| AsciiPrint ("\n Station IP address is "); | |
| HttpBootShowIp4Addr (&Private->StationIp.v4); | |
| AsciiPrint ("\n"); | |
| ON_EXIT: | |
| if (EFI_ERROR (Status)) { | |
| Dhcp4->Stop (Dhcp4); | |
| Dhcp4->Configure (Dhcp4, NULL); | |
| } else { | |
| ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); | |
| Dhcp4->Configure (Dhcp4, &Config); | |
| } | |
| return Status; | |
| } |