| /** @file | |
| Functions implementation related with DHCPv6 for HTTP boot driver. | |
| Copyright (c) 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" | |
| /** | |
| 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 (HTTP_BOOT_DHCP6_OPT_ORO); | |
| OptList[Index]->OpLen = HTONS (8); | |
| OptEnt.Oro = (HTTP_BOOT_DHCP6_OPTION_ORO *) OptList[Index]->Data; | |
| OptEnt.Oro->OpCode[0] = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL); | |
| OptEnt.Oro->OpCode[1] = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM); | |
| OptEnt.Oro->OpCode[2] = HTONS(HTTP_BOOT_DHCP6_OPT_DNS_SERVERS); | |
| OptEnt.Oro->OpCode[3] = HTONS(HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS); | |
| Index++; | |
| OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); | |
| // | |
| // Append client network device interface option | |
| // | |
| OptList[Index]->OpCode = HTONS (HTTP_BOOT_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 (HTTP_BOOT_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 (HTTP_BOOT_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 postion 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) == HTTP_BOOT_DHCP6_OPT_IA_NA) { | |
| Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option; | |
| } else if (NTOHS (Option->OpCode) == HTTP_BOOT_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) == HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM) { | |
| Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option; | |
| } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS) { | |
| Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option; | |
| } else if (NTOHS (Option->OpCode) == HTTP_BOOT_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, embeded 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), | |
| HTTP_BOOT_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) >= 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_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. | |
| **/ | |
| VOID | |
| HttpBootCacheDhcp6Packet ( | |
| IN EFI_DHCP6_PACKET *Dst, | |
| IN EFI_DHCP6_PACKET *Src | |
| ) | |
| { | |
| ASSERT (Dst->Size >= Src->Length); | |
| CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length); | |
| Dst->Length = Src->Length; | |
| } | |
| /** | |
| 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. | |
| **/ | |
| VOID | |
| 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; | |
| Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6; | |
| Offer = &Cache6->Packet.Offer; | |
| // | |
| // Cache the content of DHCPv6 packet firstly. | |
| // | |
| HttpBootCacheDhcp6Packet(Offer, RcvdOffer); | |
| // | |
| // Validate the DHCPv6 packet, and parse the options and offer type. | |
| // | |
| if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) { | |
| return ; | |
| } | |
| // | |
| // 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++; | |
| } | |
| /** | |
| 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. | |
| **/ | |
| 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; | |
| if ((Dhcp6Event != Dhcp6RcvdAdvertise) && (Dhcp6Event != Dhcp6SelectAdvertise)) { | |
| return EFI_SUCCESS; | |
| } | |
| ASSERT (Packet != NULL); | |
| Private = (HTTP_BOOT_PRIVATE_DATA *) Context; | |
| Status = EFI_SUCCESS; | |
| switch (Dhcp6Event) { | |
| case Dhcp6RcvdAdvertise: | |
| Status = EFI_NOT_READY; | |
| if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { | |
| // | |
| // Cache the dhcp offers to OfferBuffer[] for select later, and record | |
| // the OfferIndex and OfferCount. | |
| // | |
| 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); | |
| ASSERT (*NewPacket != NULL); | |
| 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 Unexpect 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 succesfully. | |
| @retval Others Unexpect 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; | |
| } | |