X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=NetworkPkg%2FUefiPxeBcDxe%2FPxeBcDhcp6.c;h=1eb64a6a0f54d4043f59bee2fef3b90fea952df0;hb=2001537d53dbd0604f1457c56d4aae370a882d4f;hp=0b7cf1f947f3ac945928870a9e0318300d14ac5b;hpb=a3bcde70e6dc69000f85cc5deee98101d2ae200a;p=mirror_edk2.git diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c index 0b7cf1f947..1eb64a6a0f 100644 --- a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c @@ -1,7 +1,7 @@ /** @file Functions implementation related with DHCPv6 for UefiPxeBc Driver. - Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ Copyright (c) 2009 - 2012, 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 +15,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. @@ -97,7 +103,7 @@ PxeBcBuildDhcp6Options ( // Append client network device interface option // OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_UNDI); - OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_UNDI)); + OptList[Index]->OpLen = HTONS ((UINT16)3); OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data; if (Private->Nii != NULL) { @@ -110,7 +116,6 @@ PxeBcBuildDhcp6Options ( OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; } - OptEnt.Undi->Reserved = 0; Index++; OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); @@ -233,12 +238,14 @@ PxeBcExtractBootFileUrl ( ) { UINT16 PrefixLen; - UINT8 *BootFileNamePtr; - UINT8 *BootFileName; + CHAR8 *BootFileNamePtr; + CHAR8 *BootFileName; UINT16 BootFileNameLen; CHAR8 *TmpStr; + CHAR8 TmpChar; CHAR8 *ServerAddressOption; CHAR8 *ServerAddress; + CHAR8 *ModeStr; EFI_STATUS Status; // @@ -256,9 +263,8 @@ PxeBcExtractBootFileUrl ( // // - // Based upon RFC 5970 and UEFI errata that will appear in chapter 21.3 of UEFI 2.3 - // specification after 2.3 errata B and future UEFI Specifications after 2.3. - // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME + // Based upon RFC 5970 and UEFI 2.3 Errata D specification, bootfile-url format + // is tftp://[SERVER_ADDRESS]/BOOTFILE_NAME // As an example where the BOOTFILE_NAME is the EFI loader and // SERVER_ADDRESS is the ASCII encoding of an IPV6 address. // @@ -314,7 +320,7 @@ PxeBcExtractBootFileUrl ( // // Get the part of BOOTFILE_NAME string. // - BootFileNamePtr = (UINT8*)((UINTN)ServerAddress + 1); + BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1); if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) { FreePool (TmpStr); return EFI_INVALID_PARAMETER; @@ -323,18 +329,47 @@ PxeBcExtractBootFileUrl ( ++BootFileNamePtr; BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1); if (BootFileNameLen != 0 || FileName != NULL) { - BootFileName = (UINT8 *) AllocateZeroPool (BootFileNameLen); + // + // Remove trailing mode=octet if present and ignore. All other modes are + // invalid for netboot6, so reject them. + // + ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet"); + if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') { + *ModeStr = '\0'; + } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Extract boot file name from URL. + // + BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen); if (BootFileName == NULL) { FreePool (TmpStr); return EFI_OUT_OF_RESOURCES; } + *FileName = (UINT8*) BootFileName; - CopyMem (BootFileName, BootFileNamePtr, BootFileNameLen); - BootFileName[BootFileNameLen - 1] = '\0'; - *FileName = BootFileName; + // + // Decode percent-encoding in boot file name. + // + while (*BootFileNamePtr != '\0') { + if (*BootFileNamePtr == '%') { + TmpChar = *(BootFileNamePtr+ 3); + *(BootFileNamePtr+ 3) = '\0'; + *BootFileName = (UINT8) AsciiStrHexToUintn ((CHAR8*)(BootFileNamePtr + 1)); + BootFileName++; + *(BootFileNamePtr+ 3) = TmpChar; + BootFileNamePtr += 3; + } else { + *BootFileName = *BootFileNamePtr; + BootFileName++; + BootFileNamePtr++; + } + } + *BootFileName = '\0'; } - FreePool (TmpStr); return EFI_SUCCESS; @@ -457,13 +492,13 @@ PxeBcParseDhcp6Packet ( // An ia_na option, embeded with valid ia_addr option and a status_code of success. // Option = Options[PXEBC_DHCP6_IDX_IA_NA]; - if (Option != NULL && NTOHS(Option->OpLen) >= 12) { + if (Option != NULL) { Option = PxeBcParseDhcp6Options ( Option->Data + 12, NTOHS (Option->OpLen), PXEBC_DHCP6_OPT_STATUS_CODE ); - if (Option != NULL && Option->Data[0] == 0) { + if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) { IsProxyOffer = FALSE; } } @@ -472,11 +507,12 @@ PxeBcParseDhcp6Packet ( // The offer with "PXEClient" is a pxe offer. // Option = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS]; - EnterpriseNum = PXEBC_DHCP6_ENTERPRISE_NUM; + EnterpriseNum = HTONL(PXEBC_DHCP6_ENTERPRISE_NUM); + if (Option != NULL && NTOHS(Option->OpLen) >= 13 && CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 && - CompareMem (&Option->Data[4], DEFAULT_CLASS_ID_DATA, 9) == 0) { + CompareMem (&Option->Data[6], DEFAULT_CLASS_ID_DATA, 9) == 0) { IsPxeOffer = TRUE; } @@ -568,6 +604,223 @@ PxeBcCopyDhcp6Proxy ( Mode->ProxyOfferReceived = TRUE; } +/** + Seek the address of the first byte of the option header. + + @param[in] Buf The pointer to the buffer. + @param[in] SeekLen The length to seek. + @param[in] OptType The option type. + + @retval NULL If it failed to seek the option. + @retval others The position to the option. + +**/ +UINT8 * +PxeBcDhcp6SeekOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN UINT16 OptType + ) +{ + UINT8 *Cursor; + UINT8 *Option; + UINT16 DataLen; + UINT16 OpCode; + + Option = NULL; + Cursor = Buf; + + while (Cursor < Buf + SeekLen) { + OpCode = ReadUnaligned16 ((UINT16 *) Cursor); + if (OpCode == HTONS (OptType)) { + Option = Cursor; + break; + } + DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2))); + Cursor += (DataLen + 4); + } + + return Option; +} + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Index PxeBc option boot item type. + + @retval EFI_SUCCESS Successfully discovered the boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover the boot file. + +**/ +EFI_STATUS +PxeBcRequestBootService ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 Index + ) +{ + 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; + EFI_DHCP6_PACKET *Request; + UINTN RequestLen; + EFI_DHCP6_PACKET *Reply; + UINT8 *RequestOpt; + UINT8 *DiscoverOpt; + UINTN ReadSize; + UINT16 OpFlags; + UINT16 OpCode; + UINT16 OpLen; + EFI_STATUS Status; + EFI_DHCP6_PACKET *ProxyOffer; + UINT8 *Option; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Request = Private->Dhcp6Request; + ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer; + SrcPort = PXEBC_BS_DISCOVER_PORT; + DestPort = PXEBC_BS_DISCOVER_PORT; + OpFlags = 0; + + if (Request == NULL) { + return EFI_DEVICE_ERROR; + } + + Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET)); + if (Discover == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Build the request packet by the cached request packet before. + // + Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId; + Discover->MessageType = Request->Dhcp6.Header.MessageType; + RequestOpt = Request->Dhcp6.Option; + DiscoverOpt = Discover->DhcpOptions; + DiscoverLen = sizeof (EFI_DHCP6_HEADER); + RequestLen = DiscoverLen; + + // + // 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; + } + + // + // 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 + ) { + // + // Copy all the options except IA option and Server ID + // + CopyMem (DiscoverOpt, RequestOpt, OpLen + 4); + DiscoverOpt += (OpLen + 4); + DiscoverLen += (OpLen + 4); + } + RequestOpt += (OpLen + 4); + RequestLen += (OpLen + 4); + } + + // + // Update Elapsed option in the package + // + Option = PxeBcDhcp6SeekOption ( + Discover->DhcpOptions, + (UINT32)(RequestLen - 4), + PXEBC_DHCP6_OPT_ELAPSED_TIME + ); + if (Option != NULL) { + CalcElapsedTime (Private); + WriteUnaligned16 ((UINT16*)(Option + 4), HTONS((UINT16) Private->ElapsedTime)); + } + + Status = PxeBc->UdpWrite ( + PxeBc, + OpFlags, + &Private->ServerIp, + &DestPort, + NULL, + &Private->StationIp, + &SrcPort, + NULL, + NULL, + &DiscoverLen, + (VOID *) Discover + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Cache the right PXE reply packet here, set valid flag later. + // Especially for PXE discover packet, store it into mode data here. + // + Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer; + 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, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP, + &Private->StationIp, + &SrcPort, + &Private->ServerIp, + &DestPort, + NULL, + NULL, + &ReadSize, + (VOID *) &Reply->Dhcp6 + ); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update length + // + Reply->Length = (UINT32) ReadSize; + + return EFI_SUCCESS; +} + /** Retry to request bootfile name by the BINL offer. @@ -588,7 +841,6 @@ PxeBcRetryDhcp6Binl ( EFI_PXE_BASE_CODE_MODE *Mode; PXEBC_DHCP6_PACKET_CACHE *Offer; PXEBC_DHCP6_PACKET_CACHE *Cache6; - EFI_IP_ADDRESS ServerIp; EFI_STATUS Status; ASSERT (Index < PXEBC_OFFER_MAX_NUM); @@ -598,31 +850,36 @@ 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 ( - NULL, - &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->OfferType == PxeOfferTypeDhcpBinl) { + // + // 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->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; + } } // // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer. // - Status = PxeBcDhcp6Discover ( - Private, - 0, - NULL, - FALSE, - &ServerIp - ); + Status = PxeBcRequestBootService (Private, Index); + if (EFI_ERROR (Status)) { return Status; } @@ -1003,6 +1260,8 @@ PxeBcRegisterIp6Address ( EFI_EVENT TimeOutEvt; EFI_EVENT MappedEvt; EFI_STATUS Status; + UINT64 DadTriggerTime; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; Status = EFI_SUCCESS; TimeOutEvt = NULL; @@ -1046,6 +1305,20 @@ PxeBcRegisterIp6Address ( goto ON_EXIT; } + // + // 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)) { + goto ON_EXIT; + } + // // Create a timer as setting address timeout event since DAD in IP6 driver. // @@ -1097,7 +1370,8 @@ PxeBcRegisterIp6Address ( // Start the 5 secondes timer to wait for setting address. // Status = EFI_NO_MAPPING; - gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT); + DadTriggerTime = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY; + gBS->SetTimer (TimeOutEvt, TimerRelative, DadTriggerTime); while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { Ip6->Poll (Ip6); @@ -1195,6 +1469,13 @@ PxeBcDhcp6CallBack ( switch (Dhcp6Event) { case Dhcp6SendSolicit: + // + // Record the first Solicate msg time + // + if (Private->SolicitTimes == 0) { + CalcElapsedTime (Private); + Private->SolicitTimes++; + } // // Cache the dhcp discover packet to mode data directly. // @@ -1378,6 +1659,14 @@ 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, @@ -1390,6 +1679,10 @@ PxeBcDhcp6Discover ( &ReadSize, (VOID *) &Reply->Dhcp6 ); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); if (EFI_ERROR (Status)) { return Status; } @@ -1422,9 +1715,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. @@ -1447,7 +1748,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; @@ -1459,8 +1760,8 @@ PxeBcDhcp6Sarr ( // Configure the DHCPv6 instance for PXE boot. // Status = Dhcp6->Configure (Dhcp6, &Config); + FreePool (Retransmit); if (EFI_ERROR (Status)) { - FreePool (Retransmit); return Status; } @@ -1478,6 +1779,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;