diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDhcp6.c | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDhcp6.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDhcp6.c | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDhcp6.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDhcp6.c new file mode 100644 index 00000000..78391ef1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDhcp6.c @@ -0,0 +1,545 @@ +/** @file + iSCSI DHCP6 related configuration routines. + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "IScsiImpl.h" + + +/** + Extract the Root Path option and get the required target information from + Boot File Uniform Resource Locator (URL) Option. + + @param[in] RootPath The RootPath string. + @param[in] Length Length of the RootPath option payload. + @param[in, out] ConfigData The iSCSI session configuration data read from + nonvolatile device. + + @retval EFI_SUCCESS All required information is extracted from the + RootPath option. + @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_INVALID_PARAMETER The RootPath is malformatted. + +**/ +EFI_STATUS +IScsiDhcp6ExtractRootPath ( + IN CHAR8 *RootPath, + IN UINT16 Length, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ) +{ + EFI_STATUS Status; + UINT16 IScsiRootPathIdLen; + CHAR8 *TmpStr; + ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX]; + ISCSI_ROOT_PATH_FIELD *Field; + UINT32 FieldIndex; + UINT8 Index; + ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData; + EFI_IP_ADDRESS Ip; + UINT8 IpMode; + + ConfigNvData = &ConfigData->SessionConfigData; + ConfigNvData->DnsMode = FALSE; + // + // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname> + // + IScsiRootPathIdLen = (UINT16) AsciiStrLen (ISCSI_ROOT_PATH_ID); + + if ((Length <= IScsiRootPathIdLen) || + (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) { + return EFI_NOT_FOUND; + } + // + // Skip the iSCSI RootPath ID "iscsi:". + // + RootPath = RootPath + IScsiRootPathIdLen; + Length = (UINT16) (Length - IScsiRootPathIdLen); + + TmpStr = (CHAR8 *) AllocatePool (Length + 1); + if (TmpStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (TmpStr, RootPath, Length); + TmpStr[Length] = '\0'; + + Index = 0; + FieldIndex = 0; + ZeroMem (&Fields[0], sizeof (Fields)); + + // + // Extract SERVERNAME field in the Root Path option. + // + if (TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER) { + // + // The servername is expressed as domain name. + // + ConfigNvData->DnsMode = TRUE; + } else { + Index++; + } + + Fields[RP_FIELD_IDX_SERVERNAME].Str = &TmpStr[Index]; + + if (!ConfigNvData->DnsMode) { + while ((TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER)&& (Index < Length)) { + Index++; + } + + // + // Skip ']' and ':'. + // + TmpStr[Index] = '\0'; + Index += 2; + } else { + while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) { + Index++; + } + // + // Skip ':'. + // + TmpStr[Index] = '\0'; + Index += 1; + } + + Fields[RP_FIELD_IDX_SERVERNAME].Len = (UINT8) AsciiStrLen (Fields[RP_FIELD_IDX_SERVERNAME].Str); + + // + // Extract others fields in the Root Path option string. + // + for (FieldIndex = 1; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) { + + if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) { + Fields[FieldIndex].Str = &TmpStr[Index]; + } + + while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) { + Index++; + } + + if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) { + if (FieldIndex != RP_FIELD_IDX_TARGETNAME) { + TmpStr[Index] = '\0'; + Index++; + } + + if (Fields[FieldIndex].Str != NULL) { + Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str); + } + } + } + + if (FieldIndex != RP_FIELD_IDX_MAX) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) || + (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) || + (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1) + ) { + + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // Get the IP address of the target. + // + Field = &Fields[RP_FIELD_IDX_SERVERNAME]; + if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) { + IpMode = ConfigNvData->IpMode; + } else { + IpMode = ConfigData->AutoConfigureMode; + } + + // + // Server name is expressed as domain name, just save it. + // + if (ConfigNvData->DnsMode) { + if ((Field->Len + 2) > sizeof (ConfigNvData->TargetUrl)) { + return EFI_INVALID_PARAMETER; + } + CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len); + ConfigNvData->TargetUrl[Field->Len + 1] = '\0'; + } else { + ZeroMem(&ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl)); + Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip); + CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS)); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + // + // Check the protocol type. + // + Field = &Fields[RP_FIELD_IDX_PROTOCOL]; + if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // Get the port of the iSCSI target. + // + Field = &Fields[RP_FIELD_IDX_PORT]; + if (Field->Str != NULL) { + ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str); + } else { + ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT; + } + // + // Get the LUN. + // + Field = &Fields[RP_FIELD_IDX_LUN]; + if (Field->Str != NULL) { + Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } else { + ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun)); + } + // + // Get the target iSCSI Name. + // + Field = &Fields[RP_FIELD_IDX_TARGETNAME]; + + if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // Validate the iSCSI name. + // + Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str)); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str); + +ON_EXIT: + + FreePool (TmpStr); + + return Status; +} + +/** + EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol + instance to intercept events that occurs in the DHCPv6 Information Request + exchange process. + + @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that + is used to configure this callback function. + @param[in] Context Pointer to the context that is initialized in + the EFI_DHCP6_PROTOCOL.InfoRequest(). + @param[in] Packet Pointer to Reply packet that has been received. + The EFI DHCPv6 Protocol instance is responsible + for freeing the buffer. + + @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish + Information Request exchange process. + @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue + Information Request exchange process. + @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort + the Information Request exchange process. + @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish + the Information Request exchange process because some + request information are not received. + +**/ +EFI_STATUS +EFIAPI +IScsiDhcp6ParseReply ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT32 Index; + UINT32 OptionCount; + EFI_DHCP6_PACKET_OPTION *BootFileOpt; + EFI_DHCP6_PACKET_OPTION **OptionList; + ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData; + UINT16 ParaLen; + + OptionCount = 0; + BootFileOpt = NULL; + + Status = This->Parse (This, Packet, &OptionCount, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_READY; + } + + OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *)); + if (OptionList == NULL) { + return EFI_NOT_READY; + } + + Status = This->Parse (This, Packet, &OptionCount, OptionList); + if (EFI_ERROR (Status)) { + Status = EFI_NOT_READY; + goto Exit; + } + + ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context; + + for (Index = 0; Index < OptionCount; Index++) { + OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode); + OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen); + + // + // Get DNS server addresses from this reply packet. + // + if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) { + + if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + // + // Primary DNS server address. + // + CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS)); + + if (OptionList[Index]->OpLen > 16) { + // + // Secondary DNS server address + // + CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS)); + } + + } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) { + // + // The server sends this option to inform the client about an URL to a boot file. + // + BootFileOpt = OptionList[Index]; + } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_PARAM) { + // + // The server sends this option to inform the client about DHCP6 server address. + // + if (OptionList[Index]->OpLen < 18) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + // + // Check param-len 1, should be 16 bytes. + // + CopyMem (&ParaLen, &OptionList[Index]->Data[0], sizeof (UINT16)); + if (NTOHS (ParaLen) != 16) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[2], sizeof (EFI_IPv6_ADDRESS)); + } + } + + if (BootFileOpt == NULL) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option + // + Status = IScsiDhcp6ExtractRootPath ( + (CHAR8 *) BootFileOpt->Data, + BootFileOpt->OpLen, + ConfigData + ); + +Exit: + + FreePool (OptionList); + return Status; +} + + +/** + Parse the DHCP ACK to get the address configuration and DNS information. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller; + @param[in, out] ConfigData The attempt configuration data. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_NO_MAPPING DHCP failed to acquire address and other + information. + @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted. + @retval EFI_DEVICE_ERROR Some unexpected error occurred. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the + operation. + @retval EFI_NO_MEDIA There was a media error. + +**/ +EFI_STATUS +IScsiDoDhcp6 ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ) +{ + EFI_HANDLE Dhcp6Handle; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_STATUS Status; + EFI_STATUS TimerStatus; + EFI_DHCP6_PACKET_OPTION *Oro; + EFI_DHCP6_RETRANSMISSION InfoReqReXmit; + EFI_EVENT Timer; + EFI_STATUS MediaStatus; + + // + // Check media status before doing DHCP. + // + MediaStatus = EFI_SUCCESS; + NetLibDetectMediaWaitTimeout (Controller, ISCSI_CHECK_MEDIA_GET_DHCP_WAITING_TIME, &MediaStatus); + if (MediaStatus != EFI_SUCCESS) { + AsciiPrint ("\n Error: Could not detect network connection.\n"); + return EFI_NO_MEDIA; + } + + // + // iSCSI will only request target info from DHCPv6 server. + // + if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) { + return EFI_SUCCESS; + } + + Dhcp6Handle = NULL; + Dhcp6 = NULL; + Oro = NULL; + Timer = NULL; + + // + // Create a DHCP6 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Dhcp6Handle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Dhcp6, + Image, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 5); + if (Oro == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Ask the server to reply with DNS and Boot File URL options by info request. + // All members in EFI_DHCP6_PACKET_OPTION are in network order. + // + Oro->OpCode = HTONS (DHCP6_OPT_ORO); + Oro->OpLen = HTONS (2 * 3); + Oro->Data[1] = DHCP6_OPT_DNS_SERVERS; + Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL; + Oro->Data[5] = DHCP6_OPT_BOOT_FILE_PARAM; + + InfoReqReXmit.Irt = 4; + InfoReqReXmit.Mrc = 1; + InfoReqReXmit.Mrt = 10; + InfoReqReXmit.Mrd = 30; + + Status = Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + NULL, + IScsiDhcp6ParseReply, + ConfigData + ); + if (Status == EFI_NO_MAPPING) { + Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + Timer, + TimerRelative, + ISCSI_GET_MAPPING_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + do { + + TimerStatus = gBS->CheckEvent (Timer); + + if (!EFI_ERROR (TimerStatus)) { + Status = Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + NULL, + IScsiDhcp6ParseReply, + ConfigData + ); + } + + } while (TimerStatus == EFI_NOT_READY); + + } + +ON_EXIT: + + if (Oro != NULL) { + FreePool (Oro); + } + + if (Timer != NULL) { + gBS->CloseEvent (Timer); + } + + if (Dhcp6 != NULL) { + gBS->CloseProtocol ( + Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + Image, + Controller + ); + } + + NetLibDestroyServiceChild ( + Controller, + Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + Dhcp6Handle + ); + + return Status; +} + |