123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- /** @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;
- }
|