/**
* 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.login;
import java.util.Random;
import org.jscsi.exception.InternetSCSIException;
import org.jscsi.parser.Constants;
import org.jscsi.utils.Utils;
import com.carrotsearch.hppc.ByteObjectOpenHashMap;
/**
* <h1>ISID</h1>
* <p>
* This is an initiator-defined component of the session identifier and is structured as follows (see
* [RFC3721] and Section 9.1.1 Conservative Reuse of ISIDs for details):
* <p>
* <table border="1">
* <tr>
* <th>Byte</th>
* <th colspan="8">0</th>
* <th colspan="8">1</th>
* <th * colspan="8">2</th>
* <th colspan="8">3</th>
* </tr>
* <tr>
* <th>Bits</th>
* <td>0</td>
* <td>1</td>
* <td>2</td>
* <td>3</td>
* <td>4</td>
* <td>5</td>
* <td>6</td>
* <td>7</td>
* <td>0</td>
* <td>1</td>
* <td>2</td>
* <td>3</td>
* <td>4</td>
* <td>5</td>
* <td>6</td>
* <td>7</td>
* <td>0</td>
* <td>1</td>
* <td>2</td>
* <td>3</td>
* <td>4</td>
* <td>5</td>
* <td>6</td>
* <td>7</td>
* <td>0</td>
* <td>1</td>
* <td>2</td>
* <td>3</td>
* <td>4</td>
* <td>5</td>
* <td>6</td>
* <td>7</td>
* </tr>
* <tr>
* <td>8</td>
* <td colspan="2"><center>T</center></td>
* <td colspan="6"><center>A</center></td>
* <td colspan="16"><center>B</center></td>
* <td colspan="8"><center>C</center></td>
* </tr>
* <tr>
* <td>12</td>
* <td colspan="16"><center>D</center></td>
* <td colspan="16"/>
* </tr>
* </table>
* <p>
* The T field identifies the format and usage of A, B, C, and D as indicated below:
* <p>
* <table border="1">
* <tr>
* <th>T</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>00b</td>
* <td>OUI-Format<br/>
* A&B are a <code>22</code> bit OUI (the I/G & U/L bits are omitted)<br/>
* C&D 24 bit qualifier</td>
* </tr>
* <tr>
* <td>01b</td>
* <td>EN - Format (IANA Enterprise Number)<br/>
* A - Reserved<br/>
* B&C EN (IANA Enterprise Number)<br/>
* D - Qualifier</td>
* </tr>
* <tr>
* <td>10b</td>
* <td>"Random"<br/>
* A - Reserved<br/>
* B&C Random<br/>
* D - Qualifier</td>
* </tr>
* <tr>
* <td>11b</td>
* <td>A,B,C&D Reserved</td>
* </tr>
* </table>
* <p>
* For the <code>T</code> field values <code>00b</code> and <code>01b</code>, a combination of <code>A</code>
* and <code>B</code> (for <code>00b</code>) or <code>B</code> and <code>C</code> (for <code>01b</code>)
* identifies the vendor or organization whose component (software or hardware) generates this ISID. A vendor
* or organization with one or more OUIs, or one or more Enterprise Numbers, MUST use at least one of these
* numbers and select the appropriate value for the <code>T</code> field when its components generate ISIDs.
* An <code>OUI</code> or <code>EN</code> MUST be set in the corresponding fields in network byte order (byte
* big-endian).
* <p>
* If the <code>T</code> field is <code>10b</code>, <code>B</code> and <code>C</code> are set to a random
* <code>24</code>-bit unsigned integer value in network byte order (byte big-endian). See [RFC3721] for how
* this affects the principle of "conservative reuse".
* <p>
* The Qualifier field is a <code>16</code> or <code>24</code>-bit unsigned integer value that provides a
* range of possible values for the ISID within the selected namespace. It may be set to any value within the
* constraints specified in the iSCSI protocol (see Section 3.4.3 Consequences of the Model and Section 9.1.1
* Conservative Reuse of ISIDs).
* <p>
* The <code>T</code> field value of <code>11b</code> is reserved.
* <p>
* If the ISID is derived from something assigned to a hardware adapter or interface by a vendor, as a preset
* default value, it MUST be configurable to a value assigned according to the SCSI port behavior desired by
* the system in which it is installed (see Section 9.1.1 Conservative Reuse of ISIDs and Section 9.1.2 iSCSI
* Name, ISID, and TPGT Use). The resultant ISID MUST also be persistent over power cycles, reboot, card swap,
* etc. For details have a look in the [RFC3721].
*
* @author Volker Wildi
*/
public final class ISID {
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Enumerations of all valid formats defined in the T field.
*/
static enum Format {
/** ISID is in the Organization Unique Identifier Format. */
OUI_FORMAT((byte)0),
/** ISID is in the EN-Format (IANA Enterprise Number). */
IANA_ENTERPRISE_NUMBER((byte)1),
/** ISID is in the "Random" Format. */
RANDOM((byte)2),
/** ISID is in the Reserved. */
RESERVED((byte)3);
private final byte value;
private static ByteObjectOpenHashMap<Format> mapping;
static {
Format.mapping = new ByteObjectOpenHashMap<Format>(values().length);
for (Format s : values()) {
Format.mapping.put(s.value, s);
}
}
private Format(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 Format valueOf(final byte value) {
return Format.mapping.get(value);
}
}
/** Bit mask to extract the first int out from a long. */
private static final long FIRST_LINE_FLAG_MASK = 0xFFFFFFFF00000000L;
/** Bit flag mask to get the field <code>A</code> in this ISID. */
private static final int A_FIELD_FLAG_MASK = 0x3F000000;
/** Number of bits to shift to get the field <code>T</code> in this ISID. */
private static final int T_FIELD_SHIFT = 30;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** The field <code>T</code> defined in the RFC 3720. */
private Format t;
/** The field <code>A</code> defined in the RFC 3720. */
private byte a;
/** The field <code>B</code> defined in the RFC 3720. */
private short b;
/** The field <code>C</code> defined in the RFC 3720. */
private byte c;
/** The field <code>D</code> defined in the RFC 3720. */
private short d;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Default constructor, creates a new, empty ISID object.
*/
public ISID() {
}
/**
* This constructor creates a new ISID object with the given settings.
*
* @param initT
* The new T-Value.
* @param initA
* The new A-Value.
* @param initB
* The new B-Value.
* @param initC
* The new C-Value.
* @param initD
* The new D-Value.
*/
public ISID(final Format initT, final byte initA, final short initB, final byte initC, final short initD) {
t = initT;
a = initA;
b = initB;
c = initC;
d = initD;
}
/**
* This method creates an Initiator Session ID of the <code>Random</code> format defined in the iSCSI
* Standard (RFC3720).
*
* @param seed
* The initialization seed for random generator.
* @return A instance of an <code>ISID</code>.
*/
public static final ISID createRandom(final long seed) {
// TODO: Implement Qualifier
final Random rand = new Random(seed);
final ISID isid =
new ISID(Format.RANDOM, Constants.RESERVED_BYTE, (short)rand.nextInt(), (byte)rand.nextInt(),
(short)0);
return isid;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Serializes this ISID object ot its byte representation.
*
* @return The byte representation of this ISID object.
* @throws InternetSCSIException
* If any violation of the iSCSI-Standard emerge.
*/
public final long serialize() throws InternetSCSIException {
checkIntegrity();
long isid = 0;
int firstLine = c;
firstLine |= b << Constants.ONE_BYTE_SHIFT;
firstLine |= a << Constants.THREE_BYTES_SHIFT;
firstLine |= t.value() << T_FIELD_SHIFT;
isid = Utils.getUnsignedLong(firstLine) << Constants.FOUR_BYTES_SHIFT;
isid |= Utils.getUnsignedLong(d) << Constants.TWO_BYTES_SHIFT;
return isid;
}
/**
* Parses a given ISID in this ISID obejct.
*
* @param isid
* The byte representation of a ISID to parse.
* @throws InternetSCSIException
* If any violation of the iSCSI-Standard emerge.
*/
final void deserialize(final long isid) throws InternetSCSIException {
int line = (int)((isid & FIRST_LINE_FLAG_MASK) >>> Constants.FOUR_BYTES_SHIFT);
t = Format.valueOf((byte)(line >>> T_FIELD_SHIFT));
a = (byte)(line & A_FIELD_FLAG_MASK >>> Constants.THREE_BYTES_SHIFT);
b = (short)((line & Constants.MIDDLE_TWO_BYTES_SHIFT) >>> Constants.ONE_BYTE_SHIFT);
c = (byte)(line & Constants.FOURTH_BYTE_MASK);
line = (int)(isid & Constants.LAST_FOUR_BYTES_MASK);
d = (short)((line & Constants.FIRST_TWO_BYTES_MASK) >>> Constants.TWO_BYTES_SHIFT);
checkIntegrity();
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Creates a string with all fields of this ISID object.
*
* @return The string representation.
*/
public final String toString() {
final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE);
sb.append(Utils.LOG_OUT_INDENT + "ISID:\n");
Utils.printField(sb, "T", t.value(), 2);
Utils.printField(sb, "A", a, 2);
Utils.printField(sb, "B", b, 2);
Utils.printField(sb, "C", c, 2);
Utils.printField(sb, "D", d, 2);
return sb.toString();
}
/**
* This method compares a given ISID object with this object for value
* equality.
*
* @param isid
* The given ISID object to check.
* @return <code>True</code>, if the values of the two ISID objects are
* equal. Else <code>false</code>.
*/
public final boolean equals(final ISID isid) {
do {
if (t != isid.t) {
break;
}
if (a != isid.a) {
break;
}
if (b != isid.b) {
break;
}
if (c != isid.c) {
break;
}
if (d != isid.d) {
break;
}
return true;
} while (false);
return false;
}
/** {@inheritDoc} */
@Override
public final int hashCode() {
return super.hashCode();
}
/**
* This methods resets their attributes to the defaults.
*/
public final void clear() {
t = Format.OUI_FORMAT;
a = 0;
b = 0;
c = 0;
d = 0;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Returns the value of the field <code>A</code>.
*
* @return The value of the field <code>A</code>.
*/
public final byte getA() {
return a;
}
/**
* Returns the value of the field <code>B</code>.
*
* @return The value of the field <code>B</code>.
*/
public final short getB() {
return b;
}
/**
* Returns the value of the field <code>C</code>.
*
* @return The value of the field <code>C</code>.
*/
public final byte getC() {
return c;
}
/**
* Returns the value of the field <code>D</code>.
*
* @return The value of the field <code>D</code>.
*/
public final short getD() {
return d;
}
/**
* Returns the value of the field <code>T</code>.
*
* @return The value of the field <code>T</code>.
*/
public final Format getT() {
return t;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* This method checks, if all fields are valid. In these cases an exception
* will be thrown.
*
* @throws InternetSCSIException
* If the integrity is violated.
*/
protected final void checkIntegrity() throws InternetSCSIException {
String exceptionMessage = "";
switch (t) {
case OUI_FORMAT:
break;
case IANA_ENTERPRISE_NUMBER:
break;
case RANDOM:
if (d != 0) {
exceptionMessage = "The D field is reserved in this ISID Format.";
}
break;
case RESERVED:
if (a != 0 && b != 0 && c != 0 && d != 0) {
exceptionMessage = "This ISID is not valid. All";
}
break;
default:
exceptionMessage = "This format is not supported.";
}
if (exceptionMessage.length() > 0) {
throw new InternetSCSIException(exceptionMessage);
} else {
// no error occured... Nice! :-)
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
}