123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- /* Copyright (C) 2012-2016 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
- *
- * You can redistribute this program and/or modify it under the terms of
- * the GNU Lesser Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- */
- using System;
- using System.Collections.Generic;
- using System.Text;
- using SCSI;
- using Utilities;
- namespace ISCSI.Client
- {
- public class ClientHelper
- {
- /// <param name="targetName">Set to null for discovery session</param>
- internal static LoginRequestPDU GetFirstStageLoginRequest(string initiatorName, string targetName, ISCSISession session, ConnectionParameters connection)
- {
- LoginRequestPDU request = new LoginRequestPDU();
- request.InitiatorTaskTag = session.GetNextTaskTag();
- request.ISID = session.ISID;
- request.TSIH = 0; // used on the first connection for a new session
- request.CID = connection.CID;
- request.CmdSN = session.GetNextCmdSN(false);
- request.ExpStatSN = 0;
- // The stage codes are:
- // 0 - SecurityNegotiation
- // 1 - LoginOperationalNegotiation
- // 3 - FullFeaturePhase
- request.CurrentStage = 0;
- request.NextStage = 1;
- request.Transit = true;
- request.VersionMax = 0;
- request.VersionMin = 0;
- KeyValuePairList<string, string> loginParameters = new KeyValuePairList<string, string>();
- loginParameters.Add("InitiatorName", initiatorName);
- loginParameters.Add("AuthMethod", "None");
- if (targetName == null)
- {
- loginParameters.Add("SessionType", "Discovery");
- }
- else
- {
- // RFC 3720: For any connection within a session whose type is not "Discovery", the first Login Request MUST also include the TargetName key=value pair.
- loginParameters.Add("SessionType", "Normal");
- loginParameters.Add("TargetName", targetName);
-
- }
- request.LoginParameters = loginParameters;
- return request;
- }
- internal static LoginRequestPDU GetSecondStageLoginRequest(LoginResponsePDU firstStageResponse, ISCSISession session, ConnectionParameters connection, bool isDiscovery)
- {
- LoginRequestPDU request = new LoginRequestPDU();
- request.ISID = firstStageResponse.ISID;
- request.TSIH = firstStageResponse.TSIH;
- request.CID = connection.CID;
- request.InitiatorTaskTag = session.GetNextTaskTag();
- request.CmdSN = session.GetNextCmdSN(false);
- request.CurrentStage = firstStageResponse.NextStage;
- request.NextStage = 3;
- request.Transit = true;
- request.VersionMax = 0;
- request.VersionMin = 0;
- KeyValuePairList<string, string> loginParameters = new KeyValuePairList<string, string>();
- loginParameters.Add("HeaderDigest", "None");
- loginParameters.Add("DataDigest", "None");
- loginParameters.Add("MaxRecvDataSegmentLength", connection.InitiatorMaxRecvDataSegmentLength.ToString());
- if (!isDiscovery)
- {
- loginParameters.Add("MaxConnections", ISCSIClient.DesiredParameters.MaxConnections.ToString());
- loginParameters.Add("InitialR2T", ISCSIClient.DesiredParameters.InitialR2T ? "Yes" : "No");
- loginParameters.Add("ImmediateData", ISCSIClient.DesiredParameters.ImmediateData ? "Yes" : "No");
- loginParameters.Add("MaxBurstLength", ISCSIClient.DesiredParameters.MaxBurstLength.ToString());
- loginParameters.Add("FirstBurstLength", ISCSIClient.DesiredParameters.FirstBurstLength.ToString());
- loginParameters.Add("MaxOutstandingR2T", ISCSIClient.DesiredParameters.MaxOutstandingR2T.ToString());
- loginParameters.Add("DataPDUInOrder", ISCSIClient.DesiredParameters.DataPDUInOrder ? "Yes" : "No");
- loginParameters.Add("DataSequenceInOrder", ISCSIClient.DesiredParameters.DataSequenceInOrder ? "Yes" : "No");
- loginParameters.Add("ErrorRecoveryLevel", ISCSIClient.DesiredParameters.ErrorRecoveryLevel.ToString());
- }
- loginParameters.Add("DefaultTime2Wait", ISCSIClient.DesiredParameters.DefaultTime2Wait.ToString());
- loginParameters.Add("DefaultTime2Retain", ISCSIClient.DesiredParameters.DefaultTime2Retain.ToString());
- request.LoginParameters = loginParameters;
- return request;
- }
- internal static LoginRequestPDU GetSingleStageLoginRequest(string initiatorName, string targetName, ISCSISession session, ConnectionParameters connection)
- {
- LoginRequestPDU request = new LoginRequestPDU();
- request.ISID = session.ISID;
- request.TSIH = session.TSIH;
- request.CID = connection.CID;
- request.InitiatorTaskTag = session.GetNextTaskTag();
- request.CmdSN = session.GetNextCmdSN(false);
- request.CurrentStage = 1;
- request.NextStage = 3;
- request.Transit = true;
- request.VersionMax = 0;
- request.VersionMin = 0;
- KeyValuePairList<string, string> loginParameters = new KeyValuePairList<string, string>();
- loginParameters.Add("InitiatorName", initiatorName);
- if (targetName == null)
- {
- loginParameters.Add("SessionType", "Discovery");
- }
- else
- {
- loginParameters.Add("SessionType", "Normal");
- loginParameters.Add("TargetName", targetName);
- }
- loginParameters.Add("DataDigest", "None");
- loginParameters.Add("MaxRecvDataSegmentLength", connection.InitiatorMaxRecvDataSegmentLength.ToString());
- if (targetName != null)
- {
- loginParameters.Add("MaxConnections", ISCSIClient.DesiredParameters.MaxConnections.ToString());
- loginParameters.Add("InitialR2T", ISCSIClient.DesiredParameters.InitialR2T ? "Yes" : "No");
- loginParameters.Add("ImmediateData", ISCSIClient.DesiredParameters.ImmediateData ? "Yes" : "No");
- loginParameters.Add("MaxBurstLength", ISCSIClient.DesiredParameters.MaxBurstLength.ToString());
- loginParameters.Add("FirstBurstLength", ISCSIClient.DesiredParameters.FirstBurstLength.ToString());
- loginParameters.Add("MaxOutstandingR2T", ISCSIClient.DesiredParameters.MaxOutstandingR2T.ToString());
- loginParameters.Add("DataPDUInOrder", ISCSIClient.DesiredParameters.DataPDUInOrder ? "Yes" : "No");
- loginParameters.Add("DataSequenceInOrder", ISCSIClient.DesiredParameters.DataSequenceInOrder ? "Yes" : "No");
- loginParameters.Add("ErrorRecoveryLevel", ISCSIClient.DesiredParameters.ErrorRecoveryLevel.ToString());
- }
- loginParameters.Add("DefaultTime2Wait", ISCSIClient.DesiredParameters.DefaultTime2Wait.ToString());
- loginParameters.Add("DefaultTime2Retain", ISCSIClient.DesiredParameters.DefaultTime2Retain.ToString());
- request.LoginParameters = loginParameters;
- return request;
- }
- internal static void UpdateOperationalParameters(KeyValuePairList<string, string> loginParameters, ISCSISession session, ConnectionParameters connection)
- {
- string value = loginParameters.ValueOf("MaxRecvDataSegmentLength");
- if (value != null)
- {
- connection.TargetMaxRecvDataSegmentLength = Convert.ToInt32(value);
- }
- value = loginParameters.ValueOf("MaxConnections");
- if (value != null)
- {
- session.MaxConnections = Convert.ToInt32(value);
- }
- else
- {
- session.MaxConnections = ISCSIClient.DesiredParameters.MaxConnections;
- }
- value = loginParameters.ValueOf("InitialR2T");
- if (value != null)
- {
- session.InitialR2T = (value == "Yes") ? true : false;
- }
- else
- {
- session.InitialR2T = ISCSIClient.DesiredParameters.InitialR2T;
- }
- value = loginParameters.ValueOf("ImmediateData");
- if (value != null)
- {
- session.ImmediateData = (value == "Yes") ? true : false;
- }
- else
- {
- session.ImmediateData = ISCSIClient.DesiredParameters.ImmediateData;
- }
- value = loginParameters.ValueOf("MaxBurstLength");
- if (value != null)
- {
- session.MaxBurstLength = Convert.ToInt32(value);
- }
- else
- {
- session.MaxBurstLength = ISCSIClient.DesiredParameters.MaxBurstLength;
- }
- value = loginParameters.ValueOf("FirstBurstLength");
- if (value != null)
- {
- session.FirstBurstLength = Convert.ToInt32(value);
- }
- else
- {
- session.FirstBurstLength = ISCSIClient.DesiredParameters.FirstBurstLength;
- }
- value = loginParameters.ValueOf("DefaultTime2Wait");
- if (value != null)
- {
- session.DefaultTime2Wait = Convert.ToInt32(value);
- }
- else
- {
- session.DefaultTime2Wait = ISCSIClient.DesiredParameters.DefaultTime2Wait;
- }
- value = loginParameters.ValueOf("DefaultTime2Retain");
- if (value != null)
- {
- session.DefaultTime2Retain = Convert.ToInt32(value);
- }
- else
- {
- session.DefaultTime2Retain = ISCSIClient.DesiredParameters.DefaultTime2Retain;
- }
- value = loginParameters.ValueOf("MaxOutstandingR2T");
- if (value != null)
- {
- session.MaxOutstandingR2T = Convert.ToInt32(value);
- }
- else
- {
- session.MaxOutstandingR2T = ISCSIClient.DesiredParameters.MaxOutstandingR2T;
- }
- value = loginParameters.ValueOf("DataPDUInOrder");
- if (value != null)
- {
- session.DataPDUInOrder = (value == "Yes") ? true : false;
- }
- else
- {
- session.DataPDUInOrder = ISCSIClient.DesiredParameters.DataPDUInOrder;
- }
- value = loginParameters.ValueOf("DataSequenceInOrder");
- if (value != null)
- {
- session.DataSequenceInOrder = (value == "Yes") ? true : false;
- }
- else
- {
- session.DataSequenceInOrder = ISCSIClient.DesiredParameters.DataSequenceInOrder;
- }
- value = loginParameters.ValueOf("ErrorRecoveryLevel");
- if (value != null)
- {
- session.ErrorRecoveryLevel = Convert.ToInt32(value);
- }
- else
- {
- session.ErrorRecoveryLevel = ISCSIClient.DesiredParameters.ErrorRecoveryLevel;
- }
- }
- internal static LogoutRequestPDU GetLogoutRequest(ISCSISession session, ConnectionParameters connection)
- {
- LogoutRequestPDU request = new LogoutRequestPDU();
- request.ReasonCode = LogoutReasonCode.CloseTheSession;
- request.InitiatorTaskTag = session.GetNextTaskTag();
- request.CID = connection.CID;
- request.CmdSN = session.GetNextCmdSN(true);
- return request;
- }
- internal static TextRequestPDU GetSendTargetsRequest(ISCSISession session, ConnectionParameters connection)
- {
- TextRequestPDU request = new TextRequestPDU();
- request.Text = "SendTargets=All";
- request.InitiatorTaskTag = session.GetNextTaskTag();
- request.CmdSN = session.GetNextCmdSN(true);
- request.Final = true;
- request.TargetTransferTag = 0xFFFFFFFF;
- return request;
- }
- internal static SCSICommandPDU GetReportLUNsCommand(ISCSISession session, ConnectionParameters connection, uint allocationLength)
- {
- SCSICommandDescriptorBlock reportLUNs = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.ReportLUNs);
- reportLUNs.TransferLength = allocationLength;
-
- SCSICommandPDU scsiCommand = new SCSICommandPDU();
- scsiCommand.CommandDescriptorBlock = reportLUNs.GetBytes();
- scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
- scsiCommand.Final = true;
- scsiCommand.Read = true;
- scsiCommand.CmdSN = session.GetNextCmdSN(true);
- scsiCommand.ExpectedDataTransferLength = allocationLength;
- return scsiCommand;
- }
- internal static SCSICommandPDU GetReadCapacity10Command(ISCSISession session, ConnectionParameters connection, ushort LUN)
- {
- SCSICommandDescriptorBlock readCapacity10 = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.ReadCapacity10);
- readCapacity10.TransferLength = ReadCapacity10Parameter.Length;
- SCSICommandPDU scsiCommand = new SCSICommandPDU();
- scsiCommand.CommandDescriptorBlock = readCapacity10.GetBytes();
- scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
- scsiCommand.Final = true;
- scsiCommand.Read = true;
- scsiCommand.LUN = LUN;
- scsiCommand.CmdSN = session.GetNextCmdSN(true);
- scsiCommand.ExpectedDataTransferLength = ReadCapacity10Parameter.Length;
- return scsiCommand;
- }
- internal static SCSICommandPDU GetReadCapacity16Command(ISCSISession session, ConnectionParameters connection, ushort LUN)
- {
- SCSICommandDescriptorBlock serviceActionIn = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.ServiceActionIn);
- serviceActionIn.ServiceAction = ServiceAction.ReadCapacity16;
- serviceActionIn.TransferLength = ReadCapacity16Parameter.Length;
- SCSICommandPDU scsiCommand = new SCSICommandPDU();
- scsiCommand.CommandDescriptorBlock = serviceActionIn.GetBytes();
- scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
- scsiCommand.Final = true;
- scsiCommand.Read = true;
- scsiCommand.LUN = LUN;
- scsiCommand.CmdSN = session.GetNextCmdSN(true);
- scsiCommand.ExpectedDataTransferLength = ReadCapacity16Parameter.Length;
- return scsiCommand;
- }
- internal static SCSICommandPDU GetRead16Command(ISCSISession session, ConnectionParameters connection, ushort LUN, ulong sectorIndex, uint sectorCount, int bytesPerSector)
- {
- SCSICommandDescriptorBlock read16 = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.Read16);
- read16.LogicalBlockAddress64 = sectorIndex;
- read16.TransferLength = sectorCount;
- SCSICommandPDU scsiCommand = new SCSICommandPDU();
- scsiCommand.CommandDescriptorBlock = read16.GetBytes();
- scsiCommand.LUN = LUN;
- scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
- scsiCommand.Final = true;
- scsiCommand.Read = true;
- scsiCommand.CmdSN = session.GetNextCmdSN(true);
- scsiCommand.ExpectedDataTransferLength = (uint)(sectorCount * bytesPerSector);
- return scsiCommand;
- }
- internal static SCSICommandPDU GetWrite16Command(ISCSISession session, ConnectionParameters connection, ushort LUN, ulong sectorIndex, byte[] data, int bytesPerSector)
- {
- SCSICommandDescriptorBlock write16 = SCSICommandDescriptorBlock.Create(SCSIOpCodeName.Write16);
- write16.LogicalBlockAddress64 = sectorIndex;
- write16.TransferLength = (uint)(data.Length / bytesPerSector);
- SCSICommandPDU scsiCommand = new SCSICommandPDU();
- scsiCommand.CommandDescriptorBlock = write16.GetBytes();
- if (session.ImmediateData)
- {
- int immediateDataLength = Math.Min(data.Length, session.FirstBurstLength);
- scsiCommand.Data = ByteReader.ReadBytes(data, 0, immediateDataLength);
- }
- scsiCommand.LUN = LUN;
- scsiCommand.InitiatorTaskTag = session.GetNextTaskTag();
- scsiCommand.Final = true;
- scsiCommand.Write = true;
- scsiCommand.CmdSN = session.GetNextCmdSN(true);
- scsiCommand.ExpectedDataTransferLength = (uint)(data.Length);
- return scsiCommand;
- }
- internal static List<SCSIDataOutPDU> GetWriteData(ISCSISession session, ConnectionParameters connection, ushort LUN, ulong sectorIndex, byte[] data, int bytesPerSector, ReadyToTransferPDU readyToTransfer)
- {
- List<SCSIDataOutPDU> result = new List<SCSIDataOutPDU>();
- // if readyToTransfer.DesiredDataTransferLength <= connection.TargetMaxRecvDataSegmentLength we must send multiple Data-Out PDUs
- // We assume DesiredDataTransferLength does not violate session.MaxBurstLength
- int numberOfChunks = (int)Math.Ceiling((double)readyToTransfer.DesiredDataTransferLength / connection.TargetMaxRecvDataSegmentLength);
- for (int chunkIndex = 0; chunkIndex < numberOfChunks; chunkIndex++)
- {
- int chunkOffset = chunkIndex * connection.TargetMaxRecvDataSegmentLength;
- int chunkLength = (int)Math.Min(connection.TargetMaxRecvDataSegmentLength, readyToTransfer.DesiredDataTransferLength - chunkOffset);
- SCSIDataOutPDU dataOut = new SCSIDataOutPDU();
- dataOut.BufferOffset = readyToTransfer.BufferOffset + (uint)chunkOffset;
- dataOut.Data = ByteReader.ReadBytes(data, (int)dataOut.BufferOffset, chunkLength);
- dataOut.TargetTransferTag = readyToTransfer.TargetTransferTag;
- dataOut.InitiatorTaskTag = readyToTransfer.InitiatorTaskTag;
- if (chunkIndex == numberOfChunks - 1)
- {
- dataOut.Final = true;
- }
- result.Add(dataOut);
- }
- return result;
- }
- internal static NOPOutPDU GetPingRequest(ISCSISession session, ConnectionParameters connection)
- {
- // Microsoft iSCSI Target v3.1 expects that CmdSN won't be incremented after this request regardless of whether the ImmediateDelivery bit is set or not,
- // So we set the ImmediateDelivery bit to work around the issue.
- NOPOutPDU request = new NOPOutPDU();
- request.ImmediateDelivery = true;
- request.InitiatorTaskTag = session.GetNextTaskTag();
- request.CmdSN = session.GetNextCmdSN(false);
- // RFC 3720: The NOP-Out MUST only have the Target Transfer Tag set if it is issued in response to a NOP-In (with a valid Target Transfer Tag).
- // Otherwise, the Target Transfer Tag MUST be set to 0xffffffff.
- request.TargetTransferTag = 0xFFFFFFFF;
- return request;
- }
- internal static NOPOutPDU GetPingResponse(NOPInPDU request, ISCSISession session, ConnectionParameters connection)
- {
- NOPOutPDU response = new NOPOutPDU();
- // If the Initiator Task Tag contains 0xffffffff, the I bit MUST be set to 1 and the CmdSN is not advanced after this PDU is sent.
- response.ImmediateDelivery = true;
- // RFC 3720: The NOP-Out MUST have the Initiator Task Tag set to a valid value only if a response in the form of NOP-In is requested.
- // Otherwise, the Initiator Task Tag MUST be set to 0xffffffff
- response.InitiatorTaskTag = 0xFFFFFFFF;
- response.CmdSN = session.GetNextCmdSN(false);
- response.TargetTransferTag = request.TargetTransferTag;
- // p.s. the Data Segment (of the request sent by the target) MUST NOT contain any data
- return response;
- }
- public static ulong GetRandomISID()
- {
- byte a = 0x80; // Random
- ushort b = (ushort)(new Random().Next(UInt16.MaxValue + 1));
- byte c = (byte)(new Random().Next(Byte.MaxValue + 1));
- ushort d = 0;
- ulong isid = (ulong)a << 40 | (ulong)b << 24 | (ulong)c << 16 | (ulong)d;
- return isid;
- }
- }
- }
|