
org.jscsi.parser.logout.LogoutRequestParser Maven / Gradle / Ivy
/**
* Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or other materials provided with the
* distribution. * Neither the name of the University of Konstanz nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jscsi.parser.logout;
import java.util.HashMap;
import java.util.Map;
import org.jscsi.exception.InternetSCSIException;
import org.jscsi.parser.Constants;
import org.jscsi.parser.InitiatorMessageParser;
import org.jscsi.parser.ProtocolDataUnit;
import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat;
import org.jscsi.utils.Utils;
/**
* LogoutRequestParser
*
* This class parses a Logout Request message defined in the iSCSI Standard (RFC3720).
*
* The Logout Request is used to perform a controlled closing of a connection.
*
* An initiator MAY use a Logout Request to remove a connection from a session or to close an entire session.
*
* After sending the Logout Request PDU, an initiator MUST NOT send any new iSCSI requests on the closing connection. If
* the Logout Request is intended to close the session, new iSCSI requests MUST NOT be sent on any of the connections
* participating in the session.
*
* When receiving a Logout Request with the reason code of "close the connection" or "close the session", the target
* MUST terminate all pending commands, whether acknowledged via ExpCmdSN or not, on that connection or session
* respectively.
*
* When receiving a Logout Request with the reason code "remove connection for recovery", the target MUST discard all
* requests not yet acknowledged via ExpCmdSN that were issued on the specified connection, and suspend all
* data/status/R2T transfers on behalf of pending commands on the specified connection.
*
* The target then issues the Logout Response and half-closes the TCP connection (sends FIN). After receiving the Logout
* Response and attempting to receive the FIN (if still possible), the initiator MUST completely close the logging-out
* connection. For the terminated commands, no additional responses should be expected.
*
* A Logout for a CID may be performed on a different transport connection when the TCP connection for the CID has
* already been terminated. In such a case, only a logical "closing" of the iSCSI connection for the CID is implied with
* a Logout.
*
* All commands that were not terminated or not completed (with status) and acknowledged when the connection is closed
* completely can be reassigned to a new connection if the target supports connection recovery.
*
* If an initiator intends to start recovery for a failing connection, it MUST use the Logout Request to "clean-up" the
* target end of a failing connection and enable recovery to start, or the Login Request with a non-zero TSIH and the
* same CID on a new connection for the same effect (see Section 10.14.3 CID). In sessions with a single connection, the
* connection can be closed and then a new connection reopened. A connection reinstatement login can be used for
* recovery (see Section 5.3.4 Connection Reinstatement).
*
* A successful completion of a Logout Request with the reason code of "close the connection" or "remove the connection
* for recovery" results at the target in the discarding of unacknowledged commands received on the connection being
* logged out. These are commands that have arrived on the connection being logged out, but have not been delivered to
* SCSI because one or more commands with a smaller CmdSN has not been received by iSCSI. See Section 3.2.2.1 Command
* Numbering and Acknowledging. The resulting holes the in command sequence numbers will have to be handled by
* appropriate recovery (see Chapter 6) unless the session is also closed.
*
* The entire logout discussion in this section is also applicable for an implicit Logout realized via a connection
* reinstatement or session reinstatement. When a Login Request performs an implicit Logout, the implicit Logout is
* performed as if having the reason codes specified below:
*
*
*
* Reason code
* Type of implicit Logout
*
*
* 0
* session reinstatement
*
*
* 1
* connection reinstatement when the operational ErrorRecoveryLevel < 2
*
*
* 2
* connection reinstatement when the operational ErrorRecoveryLevel = 2
*
*
*
*
TotalAHSLength and DataSegmentLength
For this PDU TotalAHSLength and DataSegmentLength MUST be
* 0
.
*
*
ExpStatSN
This is the last ExpStatSN value for the connection to be closed.
*
*
Implicit termination of tasks
A target implicitly terminates the active tasks due to the iSCSI protocol in
* the following cases:
*
*
* - When a connection is implicitly or explicitly logged out with the reason code of "Close the connection" and there
* are active tasks allegiant to that connection.
* - When a connection fails and eventually the connection state times out (state transition M1 in Section 7.2.2 State
* Transition Descriptions for Initiators and Targets) and there are active tasks allegiant to that connection.
* - When a successful recovery Logout is performed while there are active tasks allegiant to that connection, and
* those tasks eventually time out after the Time2Wait and Time2Retain periods without allegiance reassignment.
* - When a connection is implicitly or explicitly logged out with the reason code of "Close the session" and there
* are active tasks in that session.
*
* If the tasks terminated in any of the above cases are SCSI tasks, they must be internally terminated as if with CHECK
* CONDITION status. This status is only meaningful for appropriately handling the internal SCSI state and SCSI side
* effects with respect to ordering because this status is never communicated back as a terminating status to the
* initiator. However additional actions may have to be taken at SCSI level depending on the SCSI context as defined by
* the SCSI standards (e.g., queued commands and ACA, in cases a), b), and c), after the tasks are terminated, the
* target MUST report a Unit Attention condition on the next command processed on any connection for each affected I_T_L
* nexus with the status of CHECK CONDITION, and the ASC/ASCQ value of 47h/7Fh -
* "SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT" - etc. - see [SAM2] and [SPC3]).
*
*
* @author Volker Wildi
*/
public final class LogoutRequestParser extends InitiatorMessageParser {
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* This enumeration defines all the Logout Reasons Codes, which are allowed in a iSCSI Logout Request message
* (RFC3720). All other values are reserved.
*
* All other values are reserved.
*
*/
public static enum LogoutReasonCode {
/**
* Close the session. All commands associated with the session (if any) are terminated.
*/
CLOSE_SESSION((byte) 0),
/**
* Close the connection. All commands associated with connection (if any) are terminated.
*/
CLOSE_CONNECTION((byte) 1),
/**
* Remove the connection for recovery. Connection is closed and all commands associated with it, if any, are to
* be prepared for a new allegiance.
*/
CONNECTION_RECOVERY((byte) 2);
private final byte value;
private static Map mapping;
static {
LogoutReasonCode.mapping = new HashMap();
for (LogoutReasonCode s : values()) {
LogoutReasonCode.mapping.put(s.value, s);
}
}
private LogoutReasonCode (final byte newValue) {
value = newValue;
}
/**
* Returns the value of this enumeration.
*
* @return The value of this enumeration.
*/
public final byte value () {
return value;
}
/**
* Returns the constant defined for the given value
.
*
* @param value The value to search for.
* @return The constant defined for the given value
. Or null
, if this value is not
* defined by this enumeration.
*/
public static final LogoutReasonCode valueOf (final byte value) {
return LogoutReasonCode.mapping.get(value);
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** Reason code indicates the reason for the logout. */
private LogoutReasonCode reasonCode;
/** The Connection ID. */
private short connectionID;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Default constructor, creates a new, empty LogoutRequestParser
object.
*
* @param initProtocolDataUnit The reference ProtocolDataUnit
instance, which contains this
* LogoutRequestParser
subclass object.
*/
public LogoutRequestParser (final ProtocolDataUnit initProtocolDataUnit) {
super(initProtocolDataUnit);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public final String toString () {
final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE);
Utils.printField(sb, "Reson Code", reasonCode.value(), 1);
Utils.printField(sb, "CID", connectionID, 1);
sb.append(super.toString());
return sb.toString();
}
/** {@inheritDoc} */
@Override
public final DataSegmentFormat getDataSegmentFormat () {
return DataSegmentFormat.BINARY;
}
/** {@inheritDoc} */
@Override
public final void clear () {
super.clear();
reasonCode = LogoutReasonCode.CLOSE_SESSION;
connectionID = 0x0000;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* This is the Connection ID of the connection to be closed (including closing the TCP stream). This field
* is only valid if the reason code is not "close the session".
*
* @return The Connection ID of this LogoutRequestParser
object.
*/
public final short getConnectionID () {
return connectionID;
}
/**
* Returns the reason for the logout.
*
* @return The reason code for this logout request.
* @see LogoutReasonCode
*/
public final LogoutReasonCode getReasonCode () {
return reasonCode;
}
/**
* Sets the new Connection ID
of this LogoutRequestParser
object.
*
* @param newCID The new Connection ID
.
*/
public final void setConnectionID (final short newCID) {
connectionID = newCID;
}
/**
* Sets the Reason Code
of this LogoutRequestParser
object.
*
* @param newReasonCode The new Reason Code
.
*/
public final void setReasonCode (final LogoutReasonCode newReasonCode) {
reasonCode = newReasonCode;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final void deserializeBytes1to3 (final int line) throws InternetSCSIException {
reasonCode = LogoutReasonCode.valueOf((byte) ((line & Constants.SECOND_BYTE_MASK) >>> Constants.TWO_BYTES_SHIFT));
Utils.isReserved(line & Constants.LAST_TWO_BYTES_MASK);
}
/** {@inheritDoc} */
@Override
protected final void deserializeBytes20to23 (final int line) throws InternetSCSIException {
connectionID = (short) ((line & Constants.FIRST_TWO_BYTES_MASK) >>> Constants.TWO_BYTES_SHIFT);
Utils.isReserved(line & Constants.LAST_TWO_BYTES_MASK);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final void checkIntegrity () throws InternetSCSIException {
String exceptionMessage;
do {
Utils.isReserved(logicalUnitNumber);
if (reasonCode == LogoutReasonCode.CLOSE_SESSION && connectionID != 0) {
exceptionMessage = "The CID field must be zero, if close session is requested.";
break;
}
if (protocolDataUnit.getBasicHeaderSegment().getTotalAHSLength() != 0) {
exceptionMessage = "TotalAHSLength must be 0!";
break;
}
if (protocolDataUnit.getBasicHeaderSegment().getDataSegmentLength() != 0) {
exceptionMessage = "DataSegmentLength must be 0!";
break;
}
// message is checked correctly
return;
} while (false);
throw new InternetSCSIException(exceptionMessage);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final int serializeBytes1to3 () {
return reasonCode.value() << Constants.TWO_BYTES_SHIFT;
}
/** {@inheritDoc} */
@Override
protected final int serializeBytes20to23 () {
return connectionID << Constants.TWO_BYTES_SHIFT;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
}