/** * 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 <COPYRIGHT HOLDER> 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.reject; import org.jscsi.exception.InternetSCSIException; import org.jscsi.parser.Constants; import org.jscsi.parser.ProtocolDataUnit; import org.jscsi.parser.TargetMessageParser; import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat; import org.jscsi.utils.Utils; import com.carrotsearch.hppc.ByteObjectOpenHashMap; /** * <h1>RejectParser</h1> * <p> * This class parses a Reject message defined in the iSCSI Standard (RFC3720). * * @author Volker Wildi */ public final class RejectParser extends TargetMessageParser { // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * This enumeration defines all valid reasonCode code, which are defined in * the iSCSI Standard (RFC 3720). * <p> * <table border="1"> * <tr> * <th>Code (hex)</th> * <th>Explanation</th> * <th>Can the original PDU be re-sent?</th> * </tr> * <tr> * <td>0x01</td> * <td>Reserved</td> * <td>no</td> * </tr> * <tr> * <td>0x02</td> * <td>Data (payload) Digest Error</td> * <td>yes (Note 1)</td> * </tr> * <tr> * <td>0x03</td> * <td>SNACK Reject</td> * <td>yes</td> * </tr> * <tr> * <td>0x04</td> * <td>Protocol Error (e.g., SNACK request for a status that was already acknowledged)</td> * <td>no</td> * </tr> * <tr> * <td>0x05</td> * <td>Command not supported</td> * <td>no</td> * </tr> * <tr> * <td>0x06</td> * <td>Immediate Command Reject - too many immediate commands</td> * <td>yes</td> * </tr> * <tr> * <td>0x07</td> * <td>Task in progress</td> * <td>no</td> * </tr> * <tr> * <td>0x08</td> * <td>Invalid Data ACK</td> * <td>no</td> * </tr> * <tr> * <td>0x09</td> * <td>Invalid PDU field</td> * <td>no (Note 2)</td> * </tr> * <tr> * <td>0x0a</td> * <td>Long Operation Reject - Can't generate Target Transfer Tag - out of resources</td> * <td>yes</td> * </tr> * <tr> * <td>0x0b</td> * <td>Negotiation Reset</td> * <td>no</td> * </tr> * <tr> * <td>0x0c</td> * <td>Waiting for Logout</td> * <td>no</td> * </tr> * </table> * <p> * Note 1: For iSCSI, Data-Out PDU retransmission is only done if the target requests retransmission with * a recovery R2T. However, if this is the data digest error on immediate data, the initiator may choose * to retransmit the whole PDU including the immediate data. * <p> * Note 2: A target should use this reasonCode code for all invalid values of PDU fields that are meant to * describe a task, a response, or a data transfer. Some examples are invalid TTT/ITT, buffer offset, LUN * qualifying a TTT, and an invalid sequence number in a SNACK. * <p> * All other values for Reason are reserved. */ public static enum ReasonCode { /** Reserved. */ RESERVED((byte)0x01), /** Data (payload) Digest Error. */ DATA_DIGEST_ERROR((byte)0x02), /** SNACK Reject. */ SNACK_REJECT((byte)0x03), /** * Protocol Error (e.g., SNACK request for a status that was already * acknowledged). */ PROTOCOL_ERROR((byte)0x04), /** Command not supported. */ COMMAND_NOT_SUPPORTED((byte)0x05), /** Immediate Command Reject - too many immediate commands. */ IMMEDIATE_COMMAND_REJECT((byte)0x06), /** Task in progress. */ TASK_IN_PROGRESS((byte)0x07), /** Invalid Data ACK. */ INVALID_DATA_ACK((byte)0x08), /** Invalid PDU field. */ INVALID_PDU_FIELD((byte)0x09), /** * Long Operation Reject - Can't generate Target Transfer Tag - out of * resources. */ LONG_OPERATION_REJECT((byte)0x0A), /** Negotiation Reset. */ NEGOTIATION_RESET((byte)0x0B), /** Waiting for Logout. */ WAITING_FOR_LOGOUT((byte)0x0C); private final byte value; private static ByteObjectOpenHashMap<ReasonCode> mapping; static { ReasonCode.mapping = new ByteObjectOpenHashMap<ReasonCode>(values().length); for (ReasonCode s : values()) { ReasonCode.mapping.put(s.value, s); } } private ReasonCode(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 <code>value</code>. * * @param value * The value to search for. * @return The constant defined for the given <code>value</code>. Or <code>null</code>, if this value * is not defined by this * enumeration. */ public static final ReasonCode valueOf(final byte value) { return ReasonCode.mapping.get(value); } } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** The Reason Code. */ private ReasonCode reasonCode; /** The Data Sequence Number. */ private int dataSequenceNumber; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Default constructor, creates a new, empty <code>RejectParser</code> object. * * @param initProtocolDataUnit * The reference <code>ProtocolDataUnit</code> instance, which * contains this <code>RejectParser</code> subclass object. */ public RejectParser(final ProtocolDataUnit initProtocolDataUnit) { super(initProtocolDataUnit); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * This field is only valid if the rejected PDU is a Data/R2T SNACK and the * Reject reasonCode code is "Protocol error" (see Section 10.16 SNACK * Request). The DataSN/R2TSN is the next Data/R2T sequence number that the * target would send for the task, if any. * * @return The data sequence number of this <code>RejectParser</code> object. */ public final int getDataSequenceNumber() { return dataSequenceNumber; } /** * Returns the reject reasonCode code of this <code>RejectParser</code> object. * * @return The reasonCode code of this <code>RejectParser</code> object. */ public final ReasonCode getReasonCode() { return reasonCode; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override public final String toString() { final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE); Utils.printField(sb, "Reason", reasonCode.value, 1); sb.append(super.toString()); Utils.printField(sb, "Data SN", dataSequenceNumber, 1); return sb.toString(); } /** {@inheritDoc} */ @Override public final DataSegmentFormat getDataSegmentFormat() { return DataSegmentFormat.BINARY; } /** {@inheritDoc} */ @Override public final void clear() { super.clear(); reasonCode = null; dataSequenceNumber = 0x00000000; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override protected final void deserializeBytes1to3(final int line) throws InternetSCSIException { Utils.isReserved(line & Constants.SECOND_BYTE_MASK); reasonCode = ReasonCode.valueOf((byte)((line & Constants.THIRD_BYTE_MASK) >> Constants.ONE_BYTE_SHIFT)); Utils.isReserved(line & Constants.FOURTH_BYTE_MASK); } /** {@inheritDoc} */ @Override protected final void deserializeBytes36to39(final int line) throws InternetSCSIException { dataSequenceNumber = line; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override protected final void checkIntegrity() throws InternetSCSIException { String exceptionMessage; do { if (reasonCode != ReasonCode.PROTOCOL_ERROR && dataSequenceNumber != 0) { exceptionMessage = "The DataSN/R2TSN is only valid, if the reason code is a 'Protocol Error'."; break; } // message is checked correctly return; } while (false); throw new InternetSCSIException(exceptionMessage); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override protected final int serializeBytes1to3() { return reasonCode.value << Constants.ONE_BYTE_SHIFT; } /** {@inheritDoc} */ @Override protected final int serializeBytes36to39() { return dataSequenceNumber; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- }