internal static List <ISCSIPDU> GetSCSIResponsePDU(SCSICommandPDU command, ISCSITarget target, SessionParameters session, ConnectionParameters connection)
        {
            // We return either SCSIResponsePDU or List<SCSIDataInPDU>
            List <ISCSIPDU> responseList = new List <ISCSIPDU>();

            string connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection);

            if (command.Write && command.DataSegmentLength < command.ExpectedDataTransferLength)
            {
                uint transferTag = session.GetNextTransferTag();

                // Store segment (we only execute the command after receiving all of its data)
                byte[] commandDataBuffer = new byte[command.ExpectedDataTransferLength];
                Array.Copy(command.Data, 0, commandDataBuffer, 0, command.DataSegmentLength);

                // Send R2T
                ReadyToTransferPDU response = new ReadyToTransferPDU();
                response.InitiatorTaskTag          = command.InitiatorTaskTag;
                response.R2TSN                     = 0; // R2Ts are sequenced per command and must start with 0 for each new command;
                response.TargetTransferTag         = transferTag;
                response.BufferOffset              = command.DataSegmentLength;
                response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, command.ExpectedDataTransferLength - response.BufferOffset);

                connection.AddTransfer(transferTag, command.CommandDescriptorBlock, commandDataBuffer, 1);

                responseList.Add(response);
                return(responseList);
            }

            byte[]             scsiResponse;
            SCSIStatusCodeName status = target.ExecuteCommand(command.CommandDescriptorBlock, command.LUN, command.Data, out scsiResponse);

            if (!command.Read || status != SCSIStatusCodeName.Good)
            {
                // RFC 3720: if the command is completed with an error, then the response and sense data MUST be sent in a SCSI Response PDU
                SCSIResponsePDU response = new SCSIResponsePDU();
                response.InitiatorTaskTag = command.InitiatorTaskTag;
                response.Status           = status;
                response.Data             = scsiResponse;
                if (command.Read)
                {
                    EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength);
                }
                responseList.Add(response);
            }
            else if (scsiResponse.Length <= connection.InitiatorMaxRecvDataSegmentLength)
            {
                SCSIDataInPDU response = new SCSIDataInPDU();
                response.InitiatorTaskTag = command.InitiatorTaskTag;
                response.Status           = status;
                response.StatusPresent    = true;
                response.Final            = true;
                response.Data             = scsiResponse;
                EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength);
                responseList.Add(response);
            }
            else // we have to split the response to multiple Data-In PDUs
            {
                int bytesLeftToSend = scsiResponse.Length;

                uint dataSN = 0;
                while (bytesLeftToSend > 0)
                {
                    int dataSegmentLength = Math.Min(connection.InitiatorMaxRecvDataSegmentLength, bytesLeftToSend);
                    int dataOffset        = scsiResponse.Length - bytesLeftToSend;

                    SCSIDataInPDU response = new SCSIDataInPDU();
                    response.InitiatorTaskTag = command.InitiatorTaskTag;
                    if (bytesLeftToSend == dataSegmentLength)
                    {
                        // last Data-In PDU
                        response.Status        = status;
                        response.StatusPresent = true;
                        response.Final         = true;
                    }
                    response.BufferOffset = (uint)dataOffset;
                    response.DataSN       = dataSN;
                    dataSN++;

                    response.Data = new byte[dataSegmentLength];
                    Array.Copy(scsiResponse, dataOffset, response.Data, 0, dataSegmentLength);
                    responseList.Add(response);

                    bytesLeftToSend -= dataSegmentLength;
                }
            }

            return(responseList);
        }
        internal static ISCSIPDU GetSCSIDataOutResponsePDU(SCSIDataOutPDU request, ISCSITarget target, SessionParameters session, ConnectionParameters connection)
        {
            string        connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection);
            TransferEntry transfer             = connection.GetTransferEntry(request.TargetTransferTag);

            if (transfer == null)
            {
                ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Invalid TargetTransferTag {1}", connectionIdentifier, request.TargetTransferTag);
                RejectPDU reject = new RejectPDU();
                reject.InitiatorTaskTag = request.InitiatorTaskTag;
                reject.Reason           = RejectReason.InvalidPDUField;
                reject.Data             = ByteReader.ReadBytes(request.GetBytes(), 0, 48);
                return(reject);
            }

            ushort LUN         = (ushort)request.LUN;
            Disk   disk        = target.Disks[LUN];
            uint   offset      = request.BufferOffset;
            uint   totalLength = (uint)transfer.CommandDataBuffer.Length;

            // Store segment (we only execute the command after receiving all of its data)
            Array.Copy(request.Data, 0, transfer.CommandDataBuffer, offset, request.DataSegmentLength);

            ISCSIServer.Log(String.Format("[{0}][GetSCSIDataOutResponsePDU] Buffer offset: {1}, Total length: {2}", connectionIdentifier, offset, totalLength));

            if (offset + request.DataSegmentLength == totalLength)
            {
                // Last Data-out PDU
                ISCSIServer.Log("[{0}][GetSCSIDataOutResponsePDU] Last Data-out PDU", connectionIdentifier);

                byte[]             scsiResponse;
                SCSIStatusCodeName status   = target.ExecuteCommand(transfer.CommandBytes, request.LUN, transfer.CommandDataBuffer, out scsiResponse);
                SCSIResponsePDU    response = new SCSIResponsePDU();
                response.InitiatorTaskTag = request.InitiatorTaskTag;
                response.Status           = status;
                response.Data             = scsiResponse;
                connection.RemoveTransfer(request.TargetTransferTag);
                return(response);
            }
            else
            {
                // Send R2T
                ReadyToTransferPDU response = new ReadyToTransferPDU();
                response.InitiatorTaskTag          = request.InitiatorTaskTag;
                response.TargetTransferTag         = request.TargetTransferTag;
                response.R2TSN                     = transfer.NextR2NSN;
                response.BufferOffset              = offset + request.DataSegmentLength; // where we left off
                response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, totalLength - response.BufferOffset);

                transfer.NextR2NSN++;

                return(response);
            }
        }
        internal static LoginResponsePDU GetLoginResponsePDU(LoginRequestPDU request, List <ISCSITarget> availableTargets, SessionParameters session, ConnectionParameters connection, ref ISCSITarget target, GetNextTSIH GetNextTSIH)
        {
            LoginResponsePDU response = new LoginResponsePDU();

            response.Transit  = request.Transit;
            response.Continue = false;

            // The stage codes are:
            // 0 - SecurityNegotiation
            // 1 - LoginOperationalNegotiation
            // 3 - FullFeaturePhase

            response.CurrentStage = request.CurrentStage;
            response.NextStage    = request.NextStage;

            response.VersionMax    = request.VersionMax;
            response.VersionActive = request.VersionMin;
            response.ISID          = request.ISID;

            response.Status = LoginResponseStatusName.Success;

            response.InitiatorTaskTag = request.InitiatorTaskTag;

            if (request.TSIH == 0)
            {
                // For a new session, the request TSIH is zero,
                // As part of the response, the target generates a TSIH.
                session.TSIH = GetNextTSIH();
            }
            response.TSIH = session.TSIH;

            if (request.Transit && request.Continue)
            {
                response.Status = LoginResponseStatusName.InitiatorError;
                return(response);
            }
            else if (request.Continue)
            {
                response.Status = LoginResponseStatusName.Success;
                return(response);
            }

            // RFC 3720:  The login process proceeds in two stages - the security negotiation
            // stage and the operational parameter negotiation stage.  Both stages are optional
            // but at least one of them has to be present.

            bool firstLoginRequest = (!session.IsDiscovery && target == null);

            if (firstLoginRequest)
            {
                string sessionType = request.LoginParameters.ValueOf("SessionType");
                if (sessionType == "Discovery")
                {
                    session.IsDiscovery = true;
                }
                else //sessionType == "Normal" or unspecified (default is Normal)
                {
                    session.IsDiscovery = false;
                    // 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.

                    if (request.LoginParameters.ContainsKey("TargetName"))
                    {
                        string targetName  = request.LoginParameters.ValueOf("TargetName");
                        int    targetIndex = GetTargetIndex(availableTargets, targetName);
                        if (targetIndex >= 0)
                        {
                            target = availableTargets[targetIndex];
                        }
                        else
                        {
                            response.Status = LoginResponseStatusName.NotFound;
                            return(response);
                        }
                    }
                    else
                    {
                        response.Status = LoginResponseStatusName.InitiatorError;
                        return(response);
                    }
                }
            }

            if (request.CurrentStage == 0)
            {
                response.LoginParameters.Add("AuthMethod", "None");

                if (request.Transit)
                {
                    if (request.NextStage == 3)
                    {
                        session.IsFullFeaturePhase = true;
                    }
                    else if (request.NextStage != 1)
                    {
                        response.Status = LoginResponseStatusName.InitiatorError;
                    }
                }
            }
            else if (request.CurrentStage == 1)
            {
                UpdateOperationalParameters(request.LoginParameters, session, connection);
                response.LoginParameters = GetLoginOperationalParameters(session, connection);

                if (request.Transit)
                {
                    if (request.NextStage == 3)
                    {
                        session.IsFullFeaturePhase = true;
                    }
                    else
                    {
                        response.Status = LoginResponseStatusName.InitiatorError;
                    }
                }
            }
            else
            {
                // Not valid
                response.Status = LoginResponseStatusName.InitiatorError;
            }

            return(response);
        }
        public static KeyValuePairList <string, string> GetLoginOperationalParameters(SessionParameters sessionParameters, ConnectionParameters connectionParameters)
        {
            KeyValuePairList <string, string> loginParameters = new KeyValuePairList <string, string>();

            loginParameters.Add("HeaderDigest", "None");
            loginParameters.Add("DataDigest", "None");
            loginParameters.Add("MaxRecvDataSegmentLength", connectionParameters.TargetMaxRecvDataSegmentLength.ToString());
            if (!sessionParameters.IsDiscovery)
            {
                loginParameters.Add("ErrorRecoveryLevel", ISCSIServer.OfferedErrorRecoveryLevel.ToString());
                loginParameters.Add("InitialR2T", sessionParameters.InitialR2T ? "Yes" : "No");    // Microsoft iSCSI Target support InitialR2T = No
                loginParameters.Add("ImmediateData", sessionParameters.ImmediateData ? "Yes" : "No");
                loginParameters.Add("MaxBurstLength", sessionParameters.MaxBurstLength.ToString());
                loginParameters.Add("FirstBurstLength", sessionParameters.FirstBurstLength.ToString());
                loginParameters.Add("MaxConnections", sessionParameters.MaxConnections.ToString());
                loginParameters.Add("DataPDUInOrder", sessionParameters.DataPDUInOrder ? "Yes" : "No");
                loginParameters.Add("DataSequenceInOrder", sessionParameters.DataSequenceInOrder ? "Yes" : "No");
                loginParameters.Add("MaxOutstandingR2T", sessionParameters.MaxOutstandingR2T.ToString());
            }
            loginParameters.Add("DefaultTime2Wait", sessionParameters.DefaultTime2Wait.ToString());
            loginParameters.Add("DefaultTime2Retain", sessionParameters.DefaultTime2Retain.ToString());

            return(loginParameters);
        }
        public static void UpdateOperationalParameters(KeyValuePairList <string, string> loginParameters, SessionParameters sessionParameters, ConnectionParameters connectionParameters)
        {
            sessionParameters.InitialR2T          = ISCSIServer.OfferedInitialR2T;
            sessionParameters.ImmediateData       = ISCSIServer.OfferedImmediateData;
            sessionParameters.MaxBurstLength      = ISCSIServer.OfferedMaxBurstLength;
            sessionParameters.FirstBurstLength    = ISCSIServer.OfferedFirstBurstLength;
            sessionParameters.MaxConnections      = ISCSIServer.OfferedMaxConnections;
            sessionParameters.DataPDUInOrder      = ISCSIServer.OfferedDataPDUInOrder;
            sessionParameters.DataSequenceInOrder = ISCSIServer.OfferedDataSequenceInOrder;
            sessionParameters.DefaultTime2Wait    = ISCSIServer.OfferedDefaultTime2Wait;
            sessionParameters.DefaultTime2Retain  = ISCSIServer.OfferedDefaultTime2Retain;
            sessionParameters.MaxOutstandingR2T   = ISCSIServer.OfferedMaxOutstandingR2T;

            string value = loginParameters.ValueOf("MaxRecvDataSegmentLength");

            if (value != null)
            {
                connectionParameters.InitiatorMaxRecvDataSegmentLength = Convert.ToInt32(value);
            }

            value = loginParameters.ValueOf("InitialR2T");
            if (value != null)
            {
                sessionParameters.InitialR2T = (value == "Yes") || ISCSIServer.OfferedInitialR2T;
            }

            value = loginParameters.ValueOf("ImmediateData");
            if (value != null)
            {
                sessionParameters.ImmediateData = (value == "Yes") && ISCSIServer.OfferedImmediateData;
            }

            value = loginParameters.ValueOf("MaxBurstLength");
            if (value != null)
            {
                sessionParameters.MaxBurstLength = Math.Min(Convert.ToInt32(value), ISCSIServer.OfferedMaxBurstLength);
            }

            value = loginParameters.ValueOf("FirstBurstLength");
            if (value != null)
            {
                sessionParameters.FirstBurstLength = Math.Min(Convert.ToInt32(value), ISCSIServer.OfferedFirstBurstLength);
            }

            value = loginParameters.ValueOf("MaxConnections");
            if (value != null)
            {
                sessionParameters.MaxConnections = Math.Min(Convert.ToInt32(value), ISCSIServer.OfferedMaxConnections);
            }

            value = loginParameters.ValueOf("DataPDUInOrder");
            if (value != null)
            {
                sessionParameters.DataPDUInOrder = (value == "Yes") || ISCSIServer.OfferedDataPDUInOrder;
            }

            value = loginParameters.ValueOf("DataSequenceInOrder");
            if (value != null)
            {
                sessionParameters.DataSequenceInOrder = (value == "Yes") || ISCSIServer.OfferedDataSequenceInOrder;
            }

            value = loginParameters.ValueOf("DefaultTime2Wait");
            if (value != null)
            {
                sessionParameters.DefaultTime2Wait = Math.Max(Convert.ToInt32(value), ISCSIServer.OfferedDefaultTime2Wait);
            }

            value = loginParameters.ValueOf("DefaultTime2Retain");
            if (value != null)
            {
                sessionParameters.DefaultTime2Retain = Math.Min(Convert.ToInt32(value), ISCSIServer.OfferedDefaultTime2Retain);
            }

            value = loginParameters.ValueOf("MaxOutstandingR2T");
            if (value != null)
            {
                sessionParameters.MaxOutstandingR2T = Math.Min(Convert.ToInt32(value), ISCSIServer.OfferedMaxOutstandingR2T);
            }
        }
Exemple #6
0
 public static string GetConnectionIdentifier(SessionParameters session, ConnectionParameters connection)
 {
     return(String.Format("ISID={0},TSIH={1},CID={2}", session.ISID.ToString("x"), session.TSIH.ToString("x"), connection.CID.ToString("x")));
 }