/** @file Functions implementation related with DHCPv4 for HTTP boot driver. Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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 programmable 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. @retval EFI_SUCCESS Packet is copied. @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet. **/ EFI_STATUS HttpBootCacheDhcp4Packet ( IN EFI_DHCP4_PACKET *Dst, IN EFI_DHCP4_PACKET *Src ) { if (Dst->Size < Src->Length) { return EFI_BUFFER_TOO_SMALL; } CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); Dst->Length = Src->Length; return EFI_SUCCESS; } /** 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 >= 10) && (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 10) == 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 { if (Cache4->UriParser != NULL) { FreePool (Cache4->UriParser); } 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. @retval EFI_SUCCESS Cache and parse the packet successfully. @retval Others Operation failed. **/ EFI_STATUS 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; EFI_STATUS Status; 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. // Status = HttpBootCacheDhcp4Packet (Offer, RcvdOffer); if (EFI_ERROR (Status)) { return Status; } // // Validate the DHCPv4 packet, and parse the options and offer type. // if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) { return EFI_ABORTED; } // // 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++; return EFI_SUCCESS; } /** 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; BOOLEAN Received; if ((Dhcp4Event != Dhcp4SendDiscover) && (Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SendRequest) && (Dhcp4Event != Dhcp4RcvdAck) && (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)); } // // Callback to user if any packets sent or received. // if (Private->HttpBootCallback != NULL && Dhcp4Event != Dhcp4SelectOffer) { Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck); Status = Private->HttpBootCallback->Callback ( Private->HttpBootCallback, HttpBootDhcp4, Received, Packet->Length, &Packet->Dhcp4 ); if (EFI_ERROR (Status)) { return EFI_ABORTED; } } Status = EFI_SUCCESS; switch (Dhcp4Event) { case Dhcp4RcvdOffer: Status = EFI_NOT_READY; if (Packet->Length > HTTP_BOOT_DHCP4_PACKET_MAX_SIZE) { // // Ignore the incoming packets which exceed the maximum length. // break; } if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { // // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record // the OfferIndex and OfferCount. // If error happens, just ignore this packet and continue to wait more offer. // 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; }