123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- /** @file
- iSCSI DHCP4 related configuration routines.
- Copyright (c) 2004 - 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.
- @param[in] RootPath The RootPath.
- @param[in] Length Length of the RootPath option payload.
- @param[in, out] ConfigData The iSCSI attempt configuration data read
- from a 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
- IScsiDhcpExtractRootPath (
- IN CHAR8 *RootPath,
- IN UINT8 Length,
- IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
- )
- {
- EFI_STATUS Status;
- UINT8 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;
- //
- // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
- //
- IScsiRootPathIdLen = (UINT8)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 += IScsiRootPathIdLen;
- Length = (UINT8)(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 = RP_FIELD_IDX_SERVERNAME;
- ZeroMem (&Fields[0], sizeof (Fields));
- //
- // Extract the fields in the Root Path option string.
- //
- for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (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 ((!NET_IS_DIGIT (*(Field->Str))) && (*(Field->Str) != '[')) {
- ConfigNvData->DnsMode = TRUE;
- 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 {
- ConfigNvData->DnsMode = FALSE;
- 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;
- }
- /**
- The callback function registered to the DHCP4 instance that is used to select
- the qualified DHCP OFFER.
- @param[in] This The DHCP4 protocol.
- @param[in] Context The context set when configuring the DHCP4 protocol.
- @param[in] CurrentState The current state of the DHCP4 protocol.
- @param[in] Dhcp4Event The event occurs in the current state.
- @param[in] Packet The DHCP packet that is to be sent or was already received.
- @param[out] NewPacket The packet used to replace the above Packet.
- @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted
- in the Dhcp4Event.
- @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements.
- @retval Others Other errors as indicated.
- **/
- EFI_STATUS
- EFIAPI
- IScsiDhcpSelectOffer (
- IN EFI_DHCP4_PROTOCOL *This,
- IN VOID *Context,
- IN EFI_DHCP4_STATE CurrentState,
- IN EFI_DHCP4_EVENT Dhcp4Event,
- IN EFI_DHCP4_PACKET *Packet OPTIONAL,
- OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
- )
- {
- EFI_STATUS Status;
- UINT32 OptionCount;
- EFI_DHCP4_PACKET_OPTION **OptionList;
- UINT32 Index;
- if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
- return EFI_SUCCESS;
- }
- OptionCount = 0;
- Status = This->Parse (This, Packet, &OptionCount, NULL);
- if (Status != EFI_BUFFER_TOO_SMALL) {
- return EFI_NOT_READY;
- }
- OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
- if (OptionList == NULL) {
- return EFI_NOT_READY;
- }
- Status = This->Parse (This, Packet, &OptionCount, OptionList);
- if (EFI_ERROR (Status)) {
- FreePool (OptionList);
- return EFI_NOT_READY;
- }
- for (Index = 0; Index < OptionCount; Index++) {
- if (OptionList[Index]->OpCode != DHCP4_TAG_ROOTPATH) {
- continue;
- }
- Status = IScsiDhcpExtractRootPath (
- (CHAR8 *)&OptionList[Index]->Data[0],
- OptionList[Index]->Length,
- (ISCSI_ATTEMPT_CONFIG_NVDATA *)Context
- );
- break;
- }
- if (Index == OptionCount) {
- Status = EFI_NOT_READY;
- }
- FreePool (OptionList);
- return Status;
- }
- /**
- Parse the DHCP ACK to get the address configuration and DNS information.
- @param[in] Dhcp4 The DHCP4 protocol.
- @param[in, out] ConfigData The session 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 Other errors as indicated.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
- **/
- EFI_STATUS
- IScsiParseDhcpAck (
- IN EFI_DHCP4_PROTOCOL *Dhcp4,
- IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
- )
- {
- EFI_STATUS Status;
- EFI_DHCP4_MODE_DATA Dhcp4ModeData;
- UINT32 OptionCount;
- EFI_DHCP4_PACKET_OPTION **OptionList;
- UINT32 Index;
- ISCSI_SESSION_CONFIG_NVDATA *NvData;
- Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- if (Dhcp4ModeData.State != Dhcp4Bound) {
- return EFI_NO_MAPPING;
- }
- NvData = &ConfigData->SessionConfigData;
- CopyMem (&NvData->LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
- CopyMem (&NvData->SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
- CopyMem (&NvData->Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
- OptionCount = 0;
- OptionList = NULL;
- Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
- if (Status != EFI_BUFFER_TOO_SMALL) {
- return EFI_DEVICE_ERROR;
- }
- OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
- if (OptionList == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
- if (EFI_ERROR (Status)) {
- FreePool (OptionList);
- return EFI_DEVICE_ERROR;
- }
- for (Index = 0; Index < OptionCount; Index++) {
- //
- // Get DNS server addresses and DHCP server address from this offer.
- //
- if (OptionList[Index]->OpCode == DHCP4_TAG_DNS_SERVER) {
- if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
- Status = EFI_INVALID_PARAMETER;
- break;
- }
- //
- // Primary DNS server address.
- //
- CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
- if (OptionList[Index]->Length > 4) {
- //
- // Secondary DNS server address.
- //
- CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));
- }
- } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {
- if (OptionList[Index]->Length != 4) {
- Status = EFI_INVALID_PARAMETER;
- break;
- }
- CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
- }
- }
- FreePool (OptionList);
- return Status;
- }
- /**
- This function will switch the IP4 configuration policy to Static.
- @param[in] Ip4Config2 Pointer to the IP4 configuration protocol.
- @retval EFI_SUCCESS The policy is already configured to static.
- @retval Others Other error as indicated.
- **/
- EFI_STATUS
- IScsiSetIp4Policy (
- IN EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2
- )
- {
- EFI_IP4_CONFIG2_POLICY Policy;
- EFI_STATUS Status;
- UINTN DataSize;
- DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
- Status = Ip4Config2->GetData (
- Ip4Config2,
- Ip4Config2DataTypePolicy,
- &DataSize,
- &Policy
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- if (Policy != Ip4Config2PolicyStatic) {
- Policy = Ip4Config2PolicyStatic;
- Status = Ip4Config2->SetData (
- Ip4Config2,
- Ip4Config2DataTypePolicy,
- sizeof (EFI_IP4_CONFIG2_POLICY),
- &Policy
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- }
- return EFI_SUCCESS;
- }
- /**
- 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_OUT_OF_RESOURCES Failed to allocate memory.
- @retval EFI_NO_MEDIA There was a media error.
- @retval Others Other errors as indicated.
- **/
- EFI_STATUS
- IScsiDoDhcp (
- IN EFI_HANDLE Image,
- IN EFI_HANDLE Controller,
- IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
- )
- {
- EFI_HANDLE Dhcp4Handle;
- EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
- EFI_DHCP4_PROTOCOL *Dhcp4;
- EFI_STATUS Status;
- EFI_DHCP4_PACKET_OPTION *ParaList;
- EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData;
- ISCSI_SESSION_CONFIG_NVDATA *NvData;
- EFI_STATUS MediaStatus;
- Dhcp4Handle = NULL;
- Ip4Config2 = NULL;
- Dhcp4 = NULL;
- ParaList = NULL;
- //
- // 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;
- }
- //
- // DHCP4 service allows only one of its children to be configured in
- // the active state, If the DHCP4 D.O.R.A started by IP4 auto
- // configuration and has not been completed, the Dhcp4 state machine
- // will not be in the right state for the iSCSI to start a new round D.O.R.A.
- // So, we need to switch its policy to static.
- //
- Status = gBS->HandleProtocol (Controller, &gEfiIp4Config2ProtocolGuid, (VOID **)&Ip4Config2);
- if (!EFI_ERROR (Status)) {
- Status = IScsiSetIp4Policy (Ip4Config2);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- }
- //
- // Create a DHCP4 child instance and get the protocol.
- //
- Status = NetLibCreateServiceChild (
- Controller,
- Image,
- &gEfiDhcp4ServiceBindingProtocolGuid,
- &Dhcp4Handle
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = gBS->OpenProtocol (
- Dhcp4Handle,
- &gEfiDhcp4ProtocolGuid,
- (VOID **)&Dhcp4,
- Image,
- Controller,
- EFI_OPEN_PROTOCOL_BY_DRIVER
- );
- if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
- NvData = &ConfigData->SessionConfigData;
- ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);
- if (ParaList == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
- }
- //
- // Ask the server to reply with Netmask, Router, DNS, and RootPath options.
- //
- ParaList->OpCode = DHCP4_TAG_PARA_LIST;
- ParaList->Length = (UINT8)(NvData->TargetInfoFromDhcp ? 4 : 3);
- ParaList->Data[0] = DHCP4_TAG_NETMASK;
- ParaList->Data[1] = DHCP4_TAG_ROUTER;
- ParaList->Data[2] = DHCP4_TAG_DNS_SERVER;
- ParaList->Data[3] = DHCP4_TAG_ROOTPATH;
- ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));
- Dhcp4ConfigData.OptionCount = 1;
- Dhcp4ConfigData.OptionList = &ParaList;
- if (NvData->TargetInfoFromDhcp) {
- //
- // Use callback to select an offer that contains target information.
- //
- Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer;
- Dhcp4ConfigData.CallbackContext = ConfigData;
- }
- Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);
- if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
- Status = Dhcp4->Start (Dhcp4, NULL);
- if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
- //
- // Parse the ACK to get required information.
- //
- Status = IScsiParseDhcpAck (Dhcp4, ConfigData);
- ON_EXIT:
- if (ParaList != NULL) {
- FreePool (ParaList);
- }
- if (Dhcp4 != NULL) {
- Dhcp4->Stop (Dhcp4);
- Dhcp4->Configure (Dhcp4, NULL);
- gBS->CloseProtocol (
- Dhcp4Handle,
- &gEfiDhcp4ProtocolGuid,
- Image,
- Controller
- );
- }
- NetLibDestroyServiceChild (
- Controller,
- Image,
- &gEfiDhcp4ServiceBindingProtocolGuid,
- Dhcp4Handle
- );
- return Status;
- }
|