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); } }
public void AddTarget(ISCSITarget target) { lock (Lock) { int index = IndexOfTarget(target.TargetName); if (index >= 0) { throw new ArgumentException("A target with the same iSCSI Target Name already exists"); } m_targets.Add(target); } }
public void RemoveSession(ISCSISession session, SessionTerminationReason reason) { lock (m_activeSessions) { int index = GetSessionIndex(session.InitiatorName, session.ISID, session.TSIH); if (index >= 0) { ISCSITarget target = m_activeSessions[index].Target; if (target != null) { target.NotifySessionTermination(session.InitiatorName, session.ISID, reason); } m_activeSessions.RemoveAt(index); } } }
private LoginResponseStatusName SetUpNormalSession(LoginRequestPDU request, string targetName, ConnectionParameters connection) { ISCSISession session = connection.Session; session.IsDiscovery = false; // If there's an existing session between this initiator and the target, we should terminate the // old session before reinstating a new iSCSI session in its place. ISCSISession existingSession = m_sessionManager.FindSession(session.InitiatorName, request.ISID, targetName); if (existingSession != null) { Log(Severity.Verbose, "[{0}] Terminating old session with target: {1}", connection.ConnectionIdentifier, targetName); List <ConnectionState> existingConnections = m_connectionManager.GetSessionConnections(existingSession); foreach (ConnectionState existingConnection in existingConnections) { m_connectionManager.ReleaseConnection(existingConnection); } m_sessionManager.RemoveSession(existingSession, SessionTerminationReason.ImplicitLogout); } // We use m_targets.Lock to synchronize between the login logic and the target removal logic. lock (m_targets.Lock) { ISCSITarget target = m_targets.FindTarget(targetName); if (target != null) { if (!target.AuthorizeInitiator(session.InitiatorName, session.ISID, connection.InitiatorEndPoint)) { Log(Severity.Warning, "[{0}] Initiator was not authorized to access {1}", connection.ConnectionIdentifier, targetName); return(LoginResponseStatusName.AuthorizationFailure); } session.Target = target; } else { Log(Severity.Warning, "[{0}] Initiator requested an unknown target: {1}", connection.ConnectionIdentifier, targetName); return(LoginResponseStatusName.NotFound); } } return(LoginResponseStatusName.Success); }
public void AddTarget(ISCSITarget target) { m_targets.AddTarget(target); }
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 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); }