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