X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=NetworkPkg%2FUefiPxeBcDxe%2FPxeBcDhcp6.c;h=327b4cf1cfc5fc0f3e1ef388d51e05e188871756;hp=c3ae23ec82f52d9059704456e62402c62b719b5d;hb=9f4f29cbeec4b7eaa822e5377a749d0721853e36;hpb=eb2710af5bee8637741d92ed8d32df562941e6d9 diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c index c3ae23ec82..327b4cf1cf 100644 --- a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c @@ -1,7 +1,8 @@ /** @file Functions implementation related with DHCPv6 for UefiPxeBc Driver. - Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -15,6 +16,12 @@ #include "PxeBcImpl.h" +// +// Well-known multi-cast address defined in section-24.1 of rfc-3315 +// +// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2 +// +EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}}; /** Parse out a DHCPv6 option by OptTag, and find the position in buffer. @@ -85,18 +92,20 @@ PxeBcBuildDhcp6Options ( // // Append client option request option // - OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ORO); - OptList[Index]->OpLen = HTONS (4); + OptList[Index]->OpCode = HTONS (DHCP6_OPT_ORO); + OptList[Index]->OpLen = HTONS (8); OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data; - OptEnt.Oro->OpCode[0] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL); - OptEnt.Oro->OpCode[1] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM); + 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 (PXEBC_DHCP6_OPT_UNDI); + OptList[Index]->OpCode = HTONS (DHCP6_OPT_UNDI); OptList[Index]->OpLen = HTONS ((UINT16)3); OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data; @@ -116,7 +125,7 @@ PxeBcBuildDhcp6Options ( // // Append client system architecture option // - OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ARCH); + OptList[Index]->OpCode = HTONS (DHCP6_OPT_ARCH); OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH)); OptEnt.Arch = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data; Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE); @@ -127,7 +136,7 @@ PxeBcBuildDhcp6Options ( // // Append vendor class option to store the PXE class identifier. // - OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS); + OptList[Index]->OpCode = HTONS (DHCP6_OPT_VENDOR_CLASS); OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS)); OptEnt.VendorClass = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data; OptEnt.VendorClass->Vendor = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM); @@ -173,17 +182,24 @@ PxeBcBuildDhcp6Options ( @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. + **/ -VOID +EFI_STATUS PxeBcCacheDhcp6Packet ( IN EFI_DHCP6_PACKET *Dst, IN EFI_DHCP6_PACKET *Src ) { - ASSERT (Dst->Size >= Src->Length); + if (Dst->Size < Src->Length) { + return EFI_BUFFER_TOO_SMALL; + } CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length); Dst->Length = Src->Length; + + return EFI_SUCCESS; } @@ -209,10 +225,173 @@ PxeBcFreeBootFileOption ( } } +/** + Retrieve the boot server address using the EFI_DNS6_PROTOCOL. + + @param[in] Private Pointer to PxeBc private data. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +PxeBcDns6 ( + IN PXEBC_PRIVATE_DATA *Private, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ) +{ + EFI_STATUS Status; + EFI_DNS6_PROTOCOL *Dns6; + EFI_DNS6_CONFIG_DATA Dns6ConfigData; + EFI_DNS6_COMPLETION_TOKEN Token; + EFI_HANDLE Dns6Handle; + EFI_IPv6_ADDRESS *DnsServerList; + BOOLEAN IsDone; + + Dns6 = NULL; + Dns6Handle = NULL; + DnsServerList = Private->DnsServer; + ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN)); + + // + // Create a DNSv6 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Private->Controller, + Private->Image, + &gEfiDns6ServiceBindingProtocolGuid, + &Dns6Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + (VOID **) &Dns6, + Private->Image, + Private->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Configure DNS6 instance for the DNS server address and protocol. + // + ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA)); + Dns6ConfigData.DnsServerCount = 1; + Dns6ConfigData.DnsServerList = DnsServerList; + Dns6ConfigData.EnableDnsCache = TRUE; + Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP; + IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &Private->TmpStationIp.v6); + Status = Dns6->Configure ( + Dns6, + &Dns6ConfigData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + // + // Create event to set the IsDone flag when name resolution is finished. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Start asynchronous name resolution. + // + Status = Dns6->HostNameToIp (Dns6, HostName, &Token); + if (EFI_ERROR (Status)) { + goto Exit; + } + + while (!IsDone) { + Dns6->Poll (Dns6); + } + + // + // Name resolution is done, check result. + // + Status = Token.Status; + if (!EFI_ERROR (Status)) { + if (Token.RspData.H2AData == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // We just return the first IPv6 address from DNS protocol. + // + IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); + Status = EFI_SUCCESS; + } + +Exit: + FreePool (HostName); + + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + if (Token.RspData.H2AData != NULL) { + if (Token.RspData.H2AData->IpList != NULL) { + FreePool (Token.RspData.H2AData->IpList); + } + FreePool (Token.RspData.H2AData); + } + + if (Dns6 != NULL) { + Dns6->Configure (Dns6, NULL); + + gBS->CloseProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + Private->Image, + Private->Controller + ); + } + + if (Dns6Handle != NULL) { + NetLibDestroyServiceChild ( + Private->Controller, + Private->Image, + &gEfiDns6ServiceBindingProtocolGuid, + Dns6Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} /** Parse the Boot File URL option. + @param[in] Private Pointer to PxeBc private data. @param[out] FileName The pointer to the boot file name. @param[in, out] SrvAddr The pointer to the boot server address. @param[in] BootFile The pointer to the boot file URL option data. @@ -225,6 +404,7 @@ PxeBcFreeBootFileOption ( **/ EFI_STATUS PxeBcExtractBootFileUrl ( + IN PXEBC_PRIVATE_DATA *Private, OUT UINT8 **FileName, IN OUT EFI_IPv6_ADDRESS *SrvAddr, IN CHAR8 *BootFile, @@ -232,16 +412,20 @@ PxeBcExtractBootFileUrl ( ) { UINT16 PrefixLen; - UINT8 *BootFileNamePtr; - UINT8 *BootFileName; + CHAR8 *BootFileNamePtr; + CHAR8 *BootFileName; UINT16 BootFileNameLen; CHAR8 *TmpStr; CHAR8 TmpChar; CHAR8 *ServerAddressOption; CHAR8 *ServerAddress; CHAR8 *ModeStr; + CHAR16 *HostName; + BOOLEAN IpExpressedUrl; + UINTN Len; EFI_STATUS Status; + IpExpressedUrl = TRUE; // // The format of the Boot File URL option is: // @@ -257,8 +441,8 @@ PxeBcExtractBootFileUrl ( // // - // Based upon RFC 5970 and UEFI 2.3 Errata D specification, bootfile-url format - // is tftp://[SERVER_ADDRESS]/BOOTFILE_NAME + // Based upon RFC 5970 and UEFI 2.6, bootfile-url format can be + // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME or tftp://domain_name/BOOTFILE_NAME // As an example where the BOOTFILE_NAME is the EFI loader and // SERVER_ADDRESS is the ASCII encoding of an IPV6 address. // @@ -284,43 +468,76 @@ PxeBcExtractBootFileUrl ( // Get the part of SERVER_ADDRESS string. // ServerAddressOption = TmpStr; - if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) { - FreePool (TmpStr); - return EFI_INVALID_PARAMETER; - } + if (*ServerAddressOption == PXEBC_ADDR_START_DELIMITER) { + ServerAddressOption ++; + ServerAddress = ServerAddressOption; + while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) { + ServerAddress++; + } + + if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + *ServerAddress = '\0'; + + // + // Convert the string of server address to Ipv6 address format and store it. + // + Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr); + if (EFI_ERROR (Status)) { + FreePool (TmpStr); + return Status; + } - ServerAddressOption ++; - ServerAddress = ServerAddressOption; - while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) { - ServerAddress++; - } + } else { + IpExpressedUrl = FALSE; + ServerAddress = ServerAddressOption; + while (*ServerAddress != '\0' && *ServerAddress != PXEBC_TFTP_URL_SEPARATOR) { + ServerAddress++; + } - if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) { - FreePool (TmpStr); - return EFI_INVALID_PARAMETER; - } + if (*ServerAddress != PXEBC_TFTP_URL_SEPARATOR) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + *ServerAddress = '\0'; - *ServerAddress = '\0'; + Len = AsciiStrSize (ServerAddressOption); + HostName = AllocateZeroPool (Len * sizeof (CHAR16)); + if (HostName == NULL) { + FreePool (TmpStr); + return EFI_OUT_OF_RESOURCES; + } + AsciiStrToUnicodeStrS ( + ServerAddressOption, + HostName, + Len + ); - // - // Convert the string of server address to Ipv6 address format and store it. - // - Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr); - if (EFI_ERROR (Status)) { - FreePool (TmpStr); - return Status; + // + // Perform DNS resolution. + // + Status = PxeBcDns6 (Private,HostName, SrvAddr); + if (EFI_ERROR (Status)) { + FreePool (TmpStr); + return Status; + } } // // Get the part of BOOTFILE_NAME string. // - BootFileNamePtr = (UINT8*)((UINTN)ServerAddress + 1); - if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) { - FreePool (TmpStr); - return EFI_INVALID_PARAMETER; + BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1); + if (IpExpressedUrl) { + if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + ++BootFileNamePtr; } - ++BootFileNamePtr; BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1); if (BootFileNameLen != 0 || FileName != NULL) { // @@ -337,12 +554,12 @@ PxeBcExtractBootFileUrl ( // // Extract boot file name from URL. // - BootFileName = (UINT8 *) AllocateZeroPool (BootFileNameLen); + BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen); if (BootFileName == NULL) { FreePool (TmpStr); return EFI_OUT_OF_RESOURCES; } - *FileName = BootFileName; + *FileName = (UINT8*) BootFileName; // // Decode percent-encoding in boot file name. @@ -464,17 +681,19 @@ PxeBcParseDhcp6Packet ( // while (Offset < Length) { - if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) { + if (NTOHS (Option->OpCode) == DHCP6_OPT_IA_NA) { Options[PXEBC_DHCP6_IDX_IA_NA] = Option; - } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) { + } 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[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option; - } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) { + } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_PARAM) { Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option; - } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) { + } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) { Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option; + } else if (NTOHS (Option->OpCode) == DHCP6_OPT_DNS_SERVERS) { + Options[PXEBC_DHCP6_IDX_DNS_SERVER] = Option; } Offset += (NTOHS (Option->OpLen) + 4); @@ -482,7 +701,7 @@ PxeBcParseDhcp6Packet ( } // - // The offer with assigned client address is a proxy offer. + // 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[PXEBC_DHCP6_IDX_IA_NA]; @@ -490,7 +709,7 @@ PxeBcParseDhcp6Packet ( Option = PxeBcParseDhcp6Options ( Option->Data + 12, NTOHS (Option->OpLen), - PXEBC_DHCP6_OPT_STATUS_CODE + DHCP6_OPT_STATUS_CODE ); if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) { IsProxyOffer = FALSE; @@ -538,8 +757,11 @@ PxeBcParseDhcp6Packet ( @param[in] Ack The pointer to the DHCPv6 ack packet. @param[in] Verified If TRUE, parse the ACK packet and store info into mode data. + @retval EFI_SUCCESS Cache and parse the packet successfully. + @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet. + **/ -VOID +EFI_STATUS PxeBcCopyDhcp6Ack ( IN PXEBC_PRIVATE_DATA *Private, IN EFI_DHCP6_PACKET *Ack, @@ -547,10 +769,14 @@ PxeBcCopyDhcp6Ack ( ) { EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; Mode = Private->PxeBc.Mode; - PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack); + Status = PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack); + if (EFI_ERROR (Status)) { + return Status; + } if (Verified) { // @@ -560,6 +786,8 @@ PxeBcCopyDhcp6Ack ( CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length); Mode->DhcpAckReceived = TRUE; } + + return EFI_SUCCESS; } @@ -569,8 +797,11 @@ PxeBcCopyDhcp6Ack ( @param[in] Private The pointer to PxeBc private data. @param[in] OfferIndex The received order of offer packets. + @retval EFI_SUCCESS Cache and parse the packet successfully. + @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet. + **/ -VOID +EFI_STATUS PxeBcCopyDhcp6Proxy ( IN PXEBC_PRIVATE_DATA *Private, IN UINT32 OfferIndex @@ -578,6 +809,7 @@ PxeBcCopyDhcp6Proxy ( { EFI_PXE_BASE_CODE_MODE *Mode; EFI_DHCP6_PACKET *Offer; + EFI_STATUS Status; ASSERT (OfferIndex < Private->OfferNum); ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM); @@ -588,7 +820,10 @@ PxeBcCopyDhcp6Proxy ( // // Cache the proxy offer packet and parse it. // - PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer); + Status = PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer); + if (EFI_ERROR(Status)) { + return Status; + } PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6); // @@ -596,6 +831,8 @@ PxeBcCopyDhcp6Proxy ( // CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length); Mode->ProxyOfferReceived = TRUE; + + return EFI_SUCCESS; } /** @@ -658,7 +895,6 @@ PxeBcRequestBootService ( { EFI_PXE_BASE_CODE_UDP_PORT SrcPort; EFI_PXE_BASE_CODE_UDP_PORT DestPort; - EFI_PXE_BASE_CODE_MODE *Mode; EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover; UINTN DiscoverLen; @@ -672,13 +908,12 @@ PxeBcRequestBootService ( UINT16 OpCode; UINT16 OpLen; EFI_STATUS Status; - EFI_DHCP6_PACKET *ProxyOffer; + EFI_DHCP6_PACKET *IndexOffer; UINT8 *Option; PxeBc = &Private->PxeBc; - Mode = PxeBc->Mode; Request = Private->Dhcp6Request; - ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer; + IndexOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer; SrcPort = PXEBC_BS_DISCOVER_PORT; DestPort = PXEBC_BS_DISCOVER_PORT; OpFlags = 0; @@ -695,7 +930,7 @@ PxeBcRequestBootService ( // // Build the request packet by the cached request packet before. // - Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId; + Discover->TransactionId = IndexOffer->Dhcp6.Header.TransactionId; Discover->MessageType = Request->Dhcp6.Header.MessageType; RequestOpt = Request->Dhcp6.Option; DiscoverOpt = Discover->DhcpOptions; @@ -705,29 +940,31 @@ PxeBcRequestBootService ( // // Find Server ID Option from ProxyOffer. // - Option = PxeBcDhcp6SeekOption ( - ProxyOffer->Dhcp6.Option, - ProxyOffer->Length - 4, - PXEBC_DHCP6_OPT_SERVER_ID - ); - if (Option == NULL) { - return EFI_NOT_FOUND; - } + if (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl) { + Option = PxeBcDhcp6SeekOption ( + IndexOffer->Dhcp6.Option, + IndexOffer->Length - 4, + DHCP6_OPT_SERVER_ID + ); + if (Option == NULL) { + return EFI_NOT_FOUND; + } - // - // Add Server ID Option. - // - OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen); - CopyMem (DiscoverOpt, Option, OpLen + 4); - DiscoverOpt += (OpLen + 4); - DiscoverLen += (OpLen + 4); + // + // Add Server ID Option. + // + OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen); + CopyMem (DiscoverOpt, Option, OpLen + 4); + DiscoverOpt += (OpLen + 4); + DiscoverLen += (OpLen + 4); + } while (RequestLen < Request->Length) { OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode); OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen); if (OpCode != EFI_DHCP6_IA_TYPE_NA && OpCode != EFI_DHCP6_IA_TYPE_TA && - OpCode != PXEBC_DHCP6_OPT_SERVER_ID + OpCode != DHCP6_OPT_SERVER_ID ) { // // Copy all the options except IA option and Server ID @@ -746,7 +983,7 @@ PxeBcRequestBootService ( Option = PxeBcDhcp6SeekOption ( Discover->DhcpOptions, (UINT32)(RequestLen - 4), - PXEBC_DHCP6_OPT_ELAPSED_TIME + DHCP6_OPT_ELAPSED_TIME ); if (Option != NULL) { CalcElapsedTime (Private); @@ -788,8 +1025,8 @@ PxeBcRequestBootService ( Status = PxeBc->UdpRead ( PxeBc, - OpFlags, - &Private->StationIp, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP, + NULL, &SrcPort, &Private->ServerIp, &DestPort, @@ -844,19 +1081,30 @@ PxeBcRetryDhcp6Binl ( Mode = Private->PxeBc.Mode; Private->IsDoDiscover = FALSE; Offer = &Private->OfferBuffer[Index].Dhcp6; - - ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); - // - // Parse out the next server address from the last offer, and store it - // - Status = PxeBcExtractBootFileUrl ( - &Private->BootFileName, - &Private->ServerIp.v6, - (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data), - NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen) - ); - if (EFI_ERROR (Status)) { - return Status; + if (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) { + // + // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead. + // + CopyMem ( + &Private->ServerIp.v6, + &mAllDhcpRelayAndServersAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + // + // Parse out the next server address from the last offer, and store it + // + Status = PxeBcExtractBootFileUrl ( + Private, + &Private->BootFileName, + &Private->ServerIp.v6, + (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data), + NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen) + ); + if (EFI_ERROR (Status)) { + return Status; + } } // @@ -901,8 +1149,10 @@ PxeBcRetryDhcp6Binl ( @param[in] Private The pointer to PXEBC_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. **/ -VOID +EFI_STATUS PxeBcCacheDhcp6Offer ( IN PXEBC_PRIVATE_DATA *Private, IN EFI_DHCP6_PACKET *RcvdOffer @@ -911,6 +1161,7 @@ PxeBcCacheDhcp6Offer ( PXEBC_DHCP6_PACKET_CACHE *Cache6; EFI_DHCP6_PACKET *Offer; PXEBC_OFFER_TYPE OfferType; + EFI_STATUS Status; Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6; Offer = &Cache6->Packet.Offer; @@ -918,13 +1169,16 @@ PxeBcCacheDhcp6Offer ( // // Cache the content of DHCPv6 packet firstly. // - PxeBcCacheDhcp6Packet (Offer, RcvdOffer); + Status = PxeBcCacheDhcp6Packet (Offer, RcvdOffer); + if (EFI_ERROR (Status)) { + return Status; + } // // Validate the DHCPv6 packet, and parse the options and offer type. // if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) { - return ; + return EFI_ABORTED; } // @@ -946,14 +1200,15 @@ PxeBcCacheDhcp6Offer ( // Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; Private->OfferCount[OfferType]++; - } else if (Private->OfferCount[OfferType] > 0) { + } else if ((OfferType == PxeOfferTypeProxyPxe10 || OfferType == PxeOfferTypeProxyWfm11a) && + Private->OfferCount[OfferType] < 1) { // // Only cache the first PXE10/WFM11a offer, and discard the others. // Private->OfferIndex[OfferType][0] = Private->OfferNum; Private->OfferCount[OfferType] = 1; } else { - return; + return EFI_ABORTED; } } else { // @@ -964,6 +1219,8 @@ PxeBcCacheDhcp6Offer ( } Private->OfferNum++; + + return EFI_SUCCESS; } @@ -1078,8 +1335,10 @@ PxeBcSelectDhcp6Offer ( @param[in] Private The pointer to PXEBC_PRIVATE_DATA. - @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully. - @retval EFI_NO_RESPONSE No response to the following request packet. + @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully. + @retval EFI_NO_RESPONSE No response to the following request packet. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_BUFFER_TOO_SMALL Can't cache the offer pacet. **/ EFI_STATUS @@ -1100,6 +1359,17 @@ PxeBcHandleDhcp6Offer ( Cache6 = &Private->OfferBuffer[SelectIndex].Dhcp6; Status = EFI_SUCCESS; + // + // First try to cache DNS server address if DHCP6 offer provides. + // + if (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER] != NULL) { + Private->DnsServer = AllocateZeroPool (NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->OpLen)); + if (Private->DnsServer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Private->DnsServer, Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->Data, sizeof (EFI_IPv6_ADDRESS)); + } + if (Cache6->OfferType == PxeOfferTypeDhcpBinl) { // // DhcpBinl offer is selected, so need try to request bootfilename by this offer. @@ -1178,7 +1448,7 @@ PxeBcHandleDhcp6Offer ( // // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it. // - PxeBcCopyDhcp6Proxy (Private, ProxyIndex); + Status = PxeBcCopyDhcp6Proxy (Private, ProxyIndex); } } else { // @@ -1192,7 +1462,7 @@ PxeBcHandleDhcp6Offer ( // // All PXE boot information is ready by now. // - PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE); + Status = PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE); Private->PxeBc.Mode->DhcpDiscoverValid = TRUE; } @@ -1219,9 +1489,128 @@ PxeBcUnregisterIp6Address ( } } +/** + 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 PXEBC_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 +PxeBcCheckRouteTable ( + IN PXEBC_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; + 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; +} /** - Register the ready address by Ip6Config protocol. + Register the ready station address and gateway by Ip6Config protocol. @param[in] Private The pointer to PXEBC_PRIVATE_DATA. @param[in] Address The pointer to the ready address. @@ -1240,34 +1629,38 @@ PxeBcRegisterIp6Address ( EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; EFI_IP6_CONFIG_POLICY Policy; EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr; + EFI_IPv6_ADDRESS GatewayAddr; UINTN DataSize; - EFI_EVENT TimeOutEvt; EFI_EVENT MappedEvt; EFI_STATUS Status; + BOOLEAN NoGateway; + EFI_IPv6_ADDRESS *Ip6Addr; + UINTN Index; Status = EFI_SUCCESS; - TimeOutEvt = NULL; MappedEvt = NULL; + Ip6Addr = NULL; DataSize = sizeof (EFI_IP6_CONFIG_POLICY); Ip6Cfg = Private->Ip6Cfg; Ip6 = Private->Ip6; + NoGateway = FALSE; ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS)); - // - // Get and store the current policy of IP6 driver. - // - Status = Ip6Cfg->GetData ( - Ip6Cfg, - Ip6ConfigDataTypePolicy, - &DataSize, - &Private->Ip6Policy - ); + Status = Ip6->Configure (Ip6, &Private->Ip6CfgData); if (EFI_ERROR (Status)) { goto ON_EXIT; } + // + // Retrieve the gateway address from IP6 route table. + // + Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr); + if (EFI_ERROR (Status)) { + NoGateway = TRUE; + } + // // There is no channel between IP6 and PXE driver about address setting, // so it has to set the new address by Ip6ConfigProtocol manually. @@ -1287,20 +1680,6 @@ PxeBcRegisterIp6Address ( goto ON_EXIT; } - // - // Create a timer as setting address timeout event since DAD in IP6 driver. - // - Status = gBS->CreateEvent ( - EVT_TIMER, - TPL_CALLBACK, - NULL, - NULL, - &TimeOutEvt - ); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - // // Create a notify event to set address flag when DAD if IP6 driver succeeded. // @@ -1315,6 +1694,7 @@ PxeBcRegisterIp6Address ( goto ON_EXIT; } + Private->IsAddressOk = FALSE; Status = Ip6Cfg->RegisterDataNotify ( Ip6Cfg, Ip6ConfigDataTypeManualAddress, @@ -1332,19 +1712,66 @@ PxeBcRegisterIp6Address ( ); 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 (!Private->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; + } - // - // Start the 5 secondes timer to wait for setting address. - // - Status = EFI_NO_MAPPING; - gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT); + 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; + } - while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { - Ip6->Poll (Ip6); - if (Private->IsAddressOk) { - Status = EFI_SUCCESS; - break; + for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) { + if (CompareMem (Ip6Addr + Index, Address, sizeof (EFI_IPv6_ADDRESS)) == 0) { + break; + } + } + if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) { + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Set the default gateway address back if needed. + // + if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) { + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeGateway, + sizeof (EFI_IPv6_ADDRESS), + &GatewayAddr + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; } } @@ -1357,12 +1784,106 @@ ON_EXIT: ); gBS->CloseEvent (MappedEvt); } - if (TimeOutEvt != NULL) { - gBS->CloseEvent (TimeOutEvt); + if (Ip6Addr != NULL) { + FreePool (Ip6Addr); + } + return Status; +} + +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +PxeBcSetIp6Policy ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_IP6_CONFIG_POLICY Policy; + EFI_STATUS Status; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + UINTN DataSize; + + Ip6Cfg = Private->Ip6Cfg; + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + + // + // Get and store the current policy of IP6 driver. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + &DataSize, + &Private->Ip6Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Private->Ip6Policy == Ip6ConfigPolicyManual) { + Policy = Ip6ConfigPolicyAutomatic; + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + // + // There is no need to recover later. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + } } + return Status; } +/** + This function will register the station IP address and flush IP instance to start using the new IP address. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +PxeBcSetIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PROTOCOL *Dhcp6; + + Dhcp6 = Private->Dhcp6; + + CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + + Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL); + if (EFI_ERROR (Status)) { + PxeBcUnregisterIp6Address (Private); + Dhcp6->Stop (Dhcp6); + return Status; + } + + AsciiPrint ("\n Station IP address is "); + PxeBcShowIp6Addr (&Private->StationIp.v6); + + return EFI_SUCCESS; +} /** EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver @@ -1436,6 +1957,14 @@ PxeBcDhcp6CallBack ( switch (Dhcp6Event) { case Dhcp6SendSolicit: + if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) { + // + // If the to be sent packet exceeds the maximum length, abort the DHCP process. + // + Status = EFI_ABORTED; + break; + } + // // Record the first Solicate msg time // @@ -1451,6 +1980,12 @@ PxeBcDhcp6CallBack ( case Dhcp6RcvdAdvertise: Status = EFI_NOT_READY; + if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) { + // + // Ignore the incoming packets which exceed the maximum length. + // + break; + } if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) { // // Cache the dhcp offers to OfferBuffer[] for select later, and record @@ -1461,6 +1996,14 @@ PxeBcDhcp6CallBack ( break; case Dhcp6SendRequest: + if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) { + // + // If the to be sent packet exceeds the maximum length, abort the DHCP process. + // + Status = EFI_ABORTED; + break; + } + // // Store the request packet as seed packet for discover. // @@ -1487,6 +2030,9 @@ PxeBcDhcp6CallBack ( SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer; *NewPacket = AllocateZeroPool (SelectAd->Size); ASSERT (*NewPacket != NULL); + if (*NewPacket == NULL) { + return EFI_ABORTED; + } CopyMem (*NewPacket, SelectAd, SelectAd->Size); } break; @@ -1497,7 +2043,10 @@ PxeBcDhcp6CallBack ( // without verification. // ASSERT (Private->SelectIndex != 0); - PxeBcCopyDhcp6Ack (Private, Packet, FALSE); + Status = PxeBcCopyDhcp6Ack (Private, Packet, FALSE); + if (EFI_ERROR (Status)) { + Status = EFI_ABORTED; + } break; default: @@ -1544,7 +2093,6 @@ PxeBcDhcp6Discover ( UINT8 *RequestOpt; UINT8 *DiscoverOpt; UINTN ReadSize; - UINT16 OpFlags; UINT16 OpCode; UINT16 OpLen; UINT32 Xid; @@ -1555,7 +2103,6 @@ PxeBcDhcp6Discover ( Request = Private->Dhcp6Request; SrcPort = PXEBC_BS_DISCOVER_PORT; DestPort = PXEBC_BS_DISCOVER_PORT; - OpFlags = 0; if (!UseBis && Layer != NULL) { *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK; @@ -1599,7 +2146,7 @@ PxeBcDhcp6Discover ( Status = PxeBc->UdpWrite ( PxeBc, - OpFlags, + 0, &Private->ServerIp, &DestPort, NULL, @@ -1626,10 +2173,18 @@ PxeBcDhcp6Discover ( } ReadSize = (UINTN) Reply->Size; + // + // Start Udp6Read instance + // + Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + if (EFI_ERROR (Status)) { + return Status; + } + Status = PxeBc->UdpRead ( PxeBc, - OpFlags, - &Private->StationIp, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP, + NULL, &SrcPort, &Private->ServerIp, &DestPort, @@ -1638,6 +2193,10 @@ PxeBcDhcp6Discover ( &ReadSize, (VOID *) &Reply->Dhcp6 ); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); if (EFI_ERROR (Status)) { return Status; } @@ -1670,9 +2229,17 @@ PxeBcDhcp6Sarr ( UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE]; UINT32 OptCount; EFI_STATUS Status; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_STATUS TimerStatus; + EFI_EVENT Timer; + UINT64 GetMappingTimeOut; + UINTN DataSize; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; Status = EFI_SUCCESS; PxeMode = Private->PxeBc.Mode; + Ip6Cfg = Private->Ip6Cfg; + Timer = NULL; // // Build option list for the request packet. @@ -1695,7 +2262,7 @@ PxeBcDhcp6Sarr ( Config.IaInfoEvent = NULL; Config.RapidCommit = FALSE; Config.ReconfigureAccept = FALSE; - Config.IaDescriptor.IaId = 1; + Config.IaDescriptor.IaId = Private->IaId; Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; Config.SolicitRetransmission = Retransmit; Retransmit->Irt = 4; @@ -1707,8 +2274,8 @@ PxeBcDhcp6Sarr ( // Configure the DHCPv6 instance for PXE boot. // Status = Dhcp6->Configure (Dhcp6, &Config); + FreePool (Retransmit); if (EFI_ERROR (Status)) { - FreePool (Retransmit); return Status; } @@ -1726,6 +2293,52 @@ PxeBcDhcp6Sarr ( // Start DHCPv6 S.A.R.R. process to acquire IPv6 address. // Status = Dhcp6->Start (Dhcp6); + if (Status == EFI_NO_MAPPING) { + // + // IP6 Linklocal address is not available for use, so stop current Dhcp process + // and wait for duplicate address detection to finish. + // + Dhcp6->Stop (Dhcp6); + + // + // Get Duplicate Address Detection Transmits count. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &DadXmits + ); + if (EFI_ERROR (Status)) { + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); + if (EFI_ERROR (Status)) { + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY; + Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Timer); + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + do { + + TimerStatus = gBS->CheckEvent (Timer); + if (!EFI_ERROR (TimerStatus)) { + Status = Dhcp6->Start (Dhcp6); + } + } while (TimerStatus == EFI_NOT_READY); + + gBS->CloseEvent (Timer); + } if (EFI_ERROR (Status)) { if (Status == EFI_ICMP_ERROR) { PxeMode->IcmpErrorReceived = TRUE; @@ -1743,37 +2356,29 @@ PxeBcDhcp6Sarr ( return Status; } - ASSERT (Mode.Ia->State == Dhcp6Bound); - CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS)); - CopyMem (&PxeMode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); - - Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6); - if (EFI_ERROR (Status)) { - Dhcp6->Stop (Dhcp6); - return Status; + ASSERT ((Mode.Ia != NULL) && (Mode.Ia->State == Dhcp6Bound)); + // + // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the + // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when + // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as + // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery + // to find a valid router address. + // + CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS)); + if (Mode.ClientId != NULL) { + FreePool (Mode.ClientId); } - - Status = PxeBcFlushStaionIp (Private, &Private->StationIp, NULL); - if (EFI_ERROR (Status)) { - PxeBcUnregisterIp6Address (Private); - Dhcp6->Stop (Dhcp6); - return Status; + if (Mode.Ia != NULL) { + FreePool (Mode.Ia); } - // // Check the selected offer whether BINL retry is needed. // Status = PxeBcHandleDhcp6Offer (Private); if (EFI_ERROR (Status)) { - PxeBcUnregisterIp6Address (Private); Dhcp6->Stop (Dhcp6); return Status; } - - AsciiPrint ("\n Station IP address is "); - - PxeBcShowIp6Addr (&Private->StationIp.v6); - + return EFI_SUCCESS; } -