Updates from Mark Burton:

The enclosed code contains the following improvements:

	1 - Compatible with 08 version of the protocol

	2 - Handles both header and data digests

	3 - Supports desegmentation

	4 - Dissects multiple PDUs per packet

	5 - Stronger heuristics to avoid dissecting non-iSCSI packets

	6 - General rationalisation and de-crufting!

	The old code that attempted to automatically detect the presence
	of a header digest has been removed.  You now have to specify in
	the iSCSI preferences whether digests are enabled and if they
	are, whether they are CRC32 or not.  If not CRC32, you also need
	to specify the size of the digests (in bytes).

	Another new option specifies the iSCSI port number.  This is
	used in the heuristics to filter out packets with silly port
	numbers, set to 0 to disable the port filter.

	One problem that I haven't been able to track down is that if
	desegmentation is enabled and you turn digests on or off
	ethereal throws a SEGV.

svn path=/trunk/; revision=4051
This commit is contained in:
Guy Harris 2001-10-21 17:20:10 +00:00
parent 5521ff2ba5
commit e4dbc04bf0
1 changed files with 350 additions and 215 deletions

View File

@ -4,7 +4,7 @@
*
* Conforms to the protocol described in: draft-ietf-ips-iscsi-08.txt
*
* $Id: packet-iscsi.c,v 1.12 2001/10/20 18:30:50 guy Exp $
* $Id: packet-iscsi.c,v 1.13 2001/10/21 17:20:10 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -50,14 +50,25 @@
#include "packet.h"
#include "prefs.h"
static int enable_bogosity_filter = TRUE;
static guint32 bogus_pdu_data_length_threshold = 1024 * 1024;
static guint32 bogus_pdu_max_digest_padding = 20;
static uint iscsi_desegment = TRUE;
static int enable_force_header_digest_crc32 = FALSE;
static int enable_bogosity_filter = TRUE;
static guint32 bogus_pdu_data_length_threshold = 256 * 1024;
static int enableDataDigests = FALSE;
static int enableHeaderDigests = FALSE;
static int dataDigestIsCRC32 = TRUE;
static int headerDigestIsCRC32 = TRUE;
static int dataDigestSize = 4;
static int headerDigestSize = 4;
static uint iscsi_port = 5003;
/* Initialize the protocol and registered fields */
static int proto_iscsi = -1;
static int hf_iscsi_AHS = -1;
static int hf_iscsi_Padding = -1;
static int hf_iscsi_ping_data = -1;
static int hf_iscsi_immediate_data = -1;
@ -67,7 +78,10 @@ static int hf_iscsi_read_data = -1;
static int hf_iscsi_error_pdu_data = -1;
static int hf_iscsi_Opcode = -1;
static int hf_iscsi_Flags = -1;
static int hf_iscsi_HeaderDigest = -1;
static int hf_iscsi_HeaderDigest32 = -1;
static int hf_iscsi_DataDigest = -1;
static int hf_iscsi_DataDigest32 = -1;
static int hf_iscsi_X = -1;
static int hf_iscsi_I = -1;
static int hf_iscsi_SCSICommand_F = -1;
@ -170,6 +184,8 @@ static gint ett_iscsi_Flags = -1;
#define ISCSI_OPCODE_ASYNC_MESSAGE (0x32 | X_BIT | I_BIT)
#define ISCSI_OPCODE_REJECT (0x3f | X_BIT | I_BIT)
#define CSG_MASK 0x0c
static const value_string iscsi_opcodes[] = {
{ ISCSI_OPCODE_NOP_OUT, "NOP Out" },
{ ISCSI_OPCODE_SCSI_COMMAND, "SCSI Command" },
@ -555,25 +571,97 @@ addTextKeys(proto_tree *tt, tvbuff_t *tvb, gint offset, guint32 text_len) {
static gint
handleHeaderDigest(proto_item *ti, tvbuff_t *tvb, guint offset, int headerLen) {
int available_bytes = tvb_length_remaining(tvb, offset);
if(available_bytes >= (headerLen + 4)) {
guint32 crc = ~calculateCRC32(tvb_get_ptr(tvb, offset, headerLen), headerLen, CRC32C_PRELOAD);
guint32 sent = tvb_get_ntohl(tvb, offset + headerLen);
if(crc == sent || enable_force_header_digest_crc32) {
if(crc == sent) {
proto_tree_add_uint_format(ti, hf_iscsi_HeaderDigest32, tvb, offset + headerLen, 4, sent, "HeaderDigest: 0x%08x (Good CRC32)", sent);
return 4;
}
else {
proto_tree_add_uint_format(ti, hf_iscsi_HeaderDigest32, tvb, offset + headerLen, 4, sent, "HeaderDigest: 0x%08x (Bad CRC32)", sent);
return 4;
if(enableHeaderDigests) {
if(headerDigestIsCRC32) {
if(available_bytes >= (headerLen + 4)) {
guint32 crc = ~calculateCRC32(tvb_get_ptr(tvb, offset, headerLen), headerLen, CRC32C_PRELOAD);
guint32 sent = tvb_get_ntohl(tvb, offset + headerLen);
if(crc == sent) {
proto_tree_add_uint_format(ti, hf_iscsi_HeaderDigest32, tvb, offset + headerLen, 4, sent, "HeaderDigest: 0x%08x (Good CRC32)", sent);
}
else {
proto_tree_add_uint_format(ti, hf_iscsi_HeaderDigest32, tvb, offset + headerLen, 4, sent, "HeaderDigest: 0x%08x (Bad CRC32)", sent);
}
}
return offset + headerLen + 4;
}
if(available_bytes >= (headerLen + headerDigestSize)) {
proto_tree_add_item(ti, hf_iscsi_HeaderDigest, tvb, offset + headerLen, headerDigestSize, FALSE);
}
return offset + headerLen + headerDigestSize;
}
return 0;
return offset + headerLen;
}
static gint
handleDataDigest(proto_item *ti, tvbuff_t *tvb, guint offset, int dataLen) {
int available_bytes = tvb_length_remaining(tvb, offset);
if(enableDataDigests) {
if(dataDigestIsCRC32) {
if(available_bytes >= (dataLen + 4)) {
guint32 crc = ~calculateCRC32(tvb_get_ptr(tvb, offset, dataLen), dataLen, CRC32C_PRELOAD);
guint32 sent = tvb_get_ntohl(tvb, offset + dataLen);
if(crc == sent) {
proto_tree_add_uint_format(ti, hf_iscsi_DataDigest32, tvb, offset + dataLen, 4, sent, "DataDigest: 0x%08x (Good CRC32)", sent);
}
else {
proto_tree_add_uint_format(ti, hf_iscsi_DataDigest32, tvb, offset + dataLen, 4, sent, "DataDigest: 0x%08x (Bad CRC32)", sent);
}
}
return offset + dataLen + 4;
}
if(available_bytes >= (dataLen + dataDigestSize)) {
proto_tree_add_item(ti, hf_iscsi_DataDigest, tvb, offset + dataLen, dataDigestSize, FALSE);
}
return offset + dataLen + dataDigestSize;
}
return offset + dataLen;
}
static int
handleDataSegment(proto_item *ti, tvbuff_t *tvb, guint offset, guint dataSegmentLen, guint endOffset, int hf_id) {
if(endOffset > offset) {
int dataOffset = offset;
int dataLen = iscsi_min(dataSegmentLen, endOffset - offset);
if(dataLen > 0) {
proto_tree_add_item(ti, hf_id, tvb, offset, dataLen, FALSE);
offset += dataLen;
}
if(offset < endOffset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
if(dataSegmentLen > 0 && offset < endOffset)
offset = handleDataDigest(ti, tvb, dataOffset, offset - dataOffset);
}
return offset;
}
static int
handleDataSegmentAsTextKeys(proto_item *ti, tvbuff_t *tvb, guint offset, guint dataSegmentLen, guint endOffset, int digestsActive) {
if(endOffset > offset) {
int dataOffset = offset;
int textLen = iscsi_min(dataSegmentLen, endOffset - offset);
if(textLen > 0) {
proto_item *tf = proto_tree_add_text(ti, tvb, offset, textLen, "Key/Value Pairs");
proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_KeyValues);
offset = addTextKeys(tt, tvb, offset, textLen);
}
if(offset < endOffset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
if(digestsActive && dataSegmentLen > 0 && offset < endOffset)
offset = handleDataDigest(ti, tvb, dataOffset, offset - dataOffset);
}
return offset;
}
/* Code to actually dissect the packets */
static int
static void
dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 opcode, const char *opcode_str, guint32 data_segment_len) {
guint original_offset = offset;
proto_item *ti;
@ -653,19 +741,8 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_TargetTransferTag, tvb, offset + 20, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_CmdSN, tvb, offset + 24, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpStatSN, tvb, offset + 28, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int ping_data_len = iscsi_min(data_segment_len, end_offset - offset);
if(ping_data_len > 0) {
proto_tree_add_item(ti, hf_iscsi_ping_data, tvb, offset, ping_data_len, FALSE);
offset += ping_data_len;
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
offset = handleHeaderDigest(ti, tvb, offset, 48);
offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_ping_data);
}
else if(opcode == ISCSI_OPCODE_NOP_IN) {
/* NOP In */
@ -676,22 +753,12 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_StatSN, tvb, offset + 24, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpCmdSN, tvb, offset + 28, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_MaxCmdSN, tvb, offset + 32, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int ping_data_len = iscsi_min(data_segment_len, end_offset - offset);
if(ping_data_len > 0) {
proto_tree_add_item(ti, hf_iscsi_ping_data, tvb, offset, ping_data_len, FALSE);
offset += ping_data_len;
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
offset = handleHeaderDigest(ti, tvb, offset, 48);
offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_ping_data);
}
else if(opcode == ISCSI_OPCODE_SCSI_COMMAND) {
/* SCSI Command */
guint32 ahsLen = tvb_get_guint8(tvb, offset + 4) * 4;
{
gint b = tvb_get_guint8(tvb, offset + 1);
proto_item *tf = proto_tree_add_uint(ti, hf_iscsi_Flags, tvb, offset + 1, 1, b);
@ -737,20 +804,13 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_CDB);
proto_tree_add_item(tt, hf_iscsi_SCSICommand_CDB, tvb, cdb_offset, cdb_len, FALSE);
}
offset = cdb_offset + cdb_len + handleHeaderDigest(ti, tvb, offset, cdb_offset + cdb_len);
}
if(end_offset > offset) {
int immediate_data_len = iscsi_min(data_segment_len, end_offset - offset);
if(immediate_data_len > 0) {
proto_tree_add_item(ti, hf_iscsi_immediate_data, tvb, offset, immediate_data_len, FALSE);
offset += immediate_data_len;
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
if(ahsLen > 0) {
/* FIXME - disssect AHS? */
proto_tree_add_item(ti, hf_iscsi_AHS, tvb, offset + 48, ahsLen, FALSE);
}
offset = handleHeaderDigest(ti, tvb, offset, 48 + ahsLen);
}
offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_immediate_data);
}
else if(opcode == ISCSI_OPCODE_SCSI_RESPONSE) {
/* SCSI Response */
@ -774,19 +834,8 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_MaxCmdSN, tvb, offset + 32, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpDataSN, tvb, offset + 36, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_SCSIResponse_BidiReadResidualCount, tvb, offset + 44, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int sense_data_len = iscsi_min(data_segment_len, end_offset - offset);
if(sense_data_len > 0) {
proto_tree_add_item(ti, hf_iscsi_sense_data, tvb, offset, sense_data_len, FALSE);
offset += sense_data_len;
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
offset = handleHeaderDigest(ti, tvb, offset, 48);
offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_sense_data);
}
else if(opcode == ISCSI_OPCODE_SCSI_TASK_MANAGEMENT_COMMAND) {
/* SCSI Task Command */
@ -797,7 +846,7 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_CmdSN, tvb, offset + 24, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpStatSN, tvb, offset + 28, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_RefCmdSN, tvb, offset + 32, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
offset = handleHeaderDigest(ti, tvb, offset, 48);
}
else if(opcode == ISCSI_OPCODE_SCSI_TASK_MANAGEMENT_RESPONSE) {
/* SCSI Task Response */
@ -807,12 +856,18 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_StatSN, tvb, offset + 24, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpCmdSN, tvb, offset + 28, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_MaxCmdSN, tvb, offset + 32, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
offset = handleHeaderDigest(ti, tvb, offset, 48);
}
else if(opcode == ISCSI_OPCODE_LOGIN_COMMAND) {
/* Login Command */
int digestsActive = 1;
{
gint b = tvb_get_guint8(tvb, offset + 1);
if((b & CSG_MASK) == 0) {
/* current stage is SecurityNegotiation, digests
* are not yet turned on */
digestsActive = 0;
}
#if 0
proto_item *tf = proto_tree_add_uint(ti, hf_iscsi_Flags, tvb, offset + 1, 1, b);
proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_Flags);
@ -831,25 +886,22 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_CmdSN, tvb, offset + 24, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpStatSN, tvb, offset + 28, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int text_len = iscsi_min(data_segment_len, end_offset - offset);
if(text_len > 0) {
proto_item *tf = proto_tree_add_text(ti, tvb, offset, text_len, "Key/Value Pairs");
proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_KeyValues);
offset = addTextKeys(tt, tvb, offset, text_len);
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
if(digestsActive)
offset = handleHeaderDigest(ti, tvb, offset, 48);
else
offset += 48;
offset = handleDataSegmentAsTextKeys(ti, tvb, offset, data_segment_len, end_offset, digestsActive);
}
else if(opcode == ISCSI_OPCODE_LOGIN_RESPONSE) {
/* Login Response */
int digestsActive = 1;
{
gint b = tvb_get_guint8(tvb, offset + 1);
if((b & CSG_MASK) == 0) {
/* current stage is SecurityNegotiation, digests
* are not yet turned on */
digestsActive = 0;
}
#if 0
proto_item *tf = proto_tree_add_uint(ti, hf_iscsi_Flags, tvb, offset + 1, 1, b);
proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_Flags);
@ -870,20 +922,11 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_ExpCmdSN, tvb, offset + 28, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_MaxCmdSN, tvb, offset + 32, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_Login_Status, tvb, offset + 36, 2, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int text_len = iscsi_min(data_segment_len, end_offset - offset);
if(text_len > 0) {
proto_item *tf = proto_tree_add_text(ti, tvb, offset, text_len, "Key/Value Pairs");
proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_KeyValues);
offset = addTextKeys(tt, tvb, offset, text_len);
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
if(digestsActive)
offset = handleHeaderDigest(ti, tvb, offset, 48);
else
offset += 48;
offset = handleDataSegmentAsTextKeys(ti, tvb, offset, data_segment_len, end_offset, digestsActive);
}
else if(opcode == ISCSI_OPCODE_TEXT_COMMAND) {
/* Text Command */
@ -899,20 +942,8 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_TargetTransferTag, tvb, offset + 20, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_CmdSN, tvb, offset + 24, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpStatSN, tvb, offset + 28, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int text_len = iscsi_min(data_segment_len, end_offset - offset);
if(text_len > 0) {
proto_item *tf = proto_tree_add_text(ti, tvb, offset, text_len, "Key/Value Pairs");
proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_KeyValues);
offset = addTextKeys(tt, tvb, offset, text_len);
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
offset = handleHeaderDigest(ti, tvb, offset, 48);
offset = handleDataSegmentAsTextKeys(ti, tvb, offset, data_segment_len, end_offset, TRUE);
}
else if(opcode == ISCSI_OPCODE_TEXT_RESPONSE) {
/* Text Response */
@ -929,20 +960,8 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_StatSN, tvb, offset + 24, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpCmdSN, tvb, offset + 28, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_MaxCmdSN, tvb, offset + 32, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int text_len = iscsi_min(data_segment_len, end_offset - offset);
if(text_len > 0) {
proto_item *tf = proto_tree_add_text(ti, tvb, offset, text_len, "Key/Value Pairs");
proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_KeyValues);
offset = addTextKeys(tt, tvb, offset, text_len);
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
offset = handleHeaderDigest(ti, tvb, offset, 48);
offset = handleDataSegmentAsTextKeys(ti, tvb, offset, data_segment_len, end_offset, TRUE);
}
else if(opcode == ISCSI_OPCODE_SCSI_DATA_OUT) {
/* SCSI Data Out (write) */
@ -960,19 +979,8 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_ExpStatSN, tvb, offset + 28, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_DataSN, tvb, offset + 36, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_BufferOffset, tvb, offset + 40, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int write_data_len = iscsi_min(data_segment_len, end_offset - offset);
if(write_data_len > 0) {
proto_tree_add_item(ti, hf_iscsi_write_data, tvb, offset, write_data_len, FALSE);
offset += write_data_len;
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
offset = handleHeaderDigest(ti, tvb, offset, 48);
offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_write_data);
}
else if(opcode == ISCSI_OPCODE_SCSI_DATA_IN) {
/* SCSI Data In (read) */
@ -995,19 +1003,8 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_MaxCmdSN, tvb, offset + 32, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_DataSN, tvb, offset + 36, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_BufferOffset, tvb, offset + 40, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int read_data_len = iscsi_min(data_segment_len, end_offset - offset);
if(read_data_len > 0) {
proto_tree_add_item(ti, hf_iscsi_read_data, tvb, offset, read_data_len, FALSE);
offset += read_data_len;
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
offset = handleHeaderDigest(ti, tvb, offset, 48);
offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_read_data);
}
else if(opcode == ISCSI_OPCODE_LOGOUT_COMMAND) {
/* Logout Command */
@ -1015,7 +1012,7 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_Logout_Reason, tvb, offset + 11, 1, FALSE);
proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpStatSN, tvb, offset + 28, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
offset = handleHeaderDigest(ti, tvb, offset, 48);
}
else if(opcode == ISCSI_OPCODE_LOGOUT_RESPONSE) {
/* Logout Response */
@ -1025,7 +1022,7 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_MaxCmdSN, tvb, offset + 32, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_Time2Wait, tvb, offset + 40, 2, FALSE);
proto_tree_add_item(ti, hf_iscsi_Time2Retain, tvb, offset + 42, 2, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
offset = handleHeaderDigest(ti, tvb, offset, 48);
}
else if(opcode == ISCSI_OPCODE_SNACK_REQUEST) {
/* SNACK Request */
@ -1043,7 +1040,7 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_RunLength, tvb, offset + 24, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpStatSN, tvb, offset + 28, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_ExpDataSN, tvb, offset + 36, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
offset = handleHeaderDigest(ti, tvb, offset, 48);
}
else if(opcode == ISCSI_OPCODE_R2T) {
/* R2T */
@ -1055,7 +1052,7 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_R2TSN, tvb, offset + 36, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_BufferOffset, tvb, offset + 40, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_DesiredDataLength, tvb, offset + 44, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
offset = handleHeaderDigest(ti, tvb, offset, 48);
}
else if(opcode == ISCSI_OPCODE_ASYNC_MESSAGE) {
/* Asynchronous Message */
@ -1069,7 +1066,7 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_Parameter1, tvb, offset + 38, 2, FALSE);
proto_tree_add_item(ti, hf_iscsi_Parameter2, tvb, offset + 40, 2, FALSE);
proto_tree_add_item(ti, hf_iscsi_Parameter3, tvb, offset + 42, 2, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
offset = handleHeaderDigest(ti, tvb, offset, 48);
}
else if(opcode == ISCSI_OPCODE_REJECT) {
/* Reject */
@ -1079,50 +1076,38 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
proto_tree_add_item(ti, hf_iscsi_ExpCmdSN, tvb, offset + 28, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_MaxCmdSN, tvb, offset + 32, 4, FALSE);
proto_tree_add_item(ti, hf_iscsi_DataSN, tvb, offset + 36, 4, FALSE);
offset += 48 + handleHeaderDigest(ti, tvb, offset, 48);
if(end_offset > offset) {
int error_pdu_len = iscsi_min(data_segment_len, end_offset - offset);
if(error_pdu_len > 0) {
proto_tree_add_item(ti, hf_iscsi_error_pdu_data, tvb, offset, error_pdu_len, FALSE);
offset += error_pdu_len;
}
if(offset < end_offset && (offset & 3) != 0) {
int padding = 4 - (offset & 3);
proto_tree_add_item(ti, hf_iscsi_Padding, tvb, offset, padding, FALSE);
offset += padding;
}
}
offset = handleHeaderDigest(ti, tvb, offset, 48);
offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_error_pdu_data);
}
proto_item_set_len(ti, offset - original_offset);
}
else {
/* FIXME - this really should gobble up digests and data segment */
offset += 48;
}
return offset - original_offset;
}
static gboolean
dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
/* Set up structures needed to add the protocol subtree and manage it */
guint iSCSIPdusDissected = 0;
guint offset = 0;
guint32 available_bytes = tvb_length_remaining(tvb, offset);
/* quick check to see if the packet is long enough to contain a
* whole iSCSI header segment */
if (available_bytes < 48) {
if (!proto_is_protocol_enabled(proto_iscsi))
return FALSE; /* iSCSI has been disabled */
/* quick check to see if the packet is long enough to contain the
* minimum amount of information we need */
if (available_bytes < 48 && (!iscsi_desegment || available_bytes < 8)) {
/* no, so give up */
return FALSE;
}
/* (potentially) process multiple iSCSI PDUs per packet */
while(available_bytes >= 48) {
/* process multiple iSCSI PDUs per packet */
while(available_bytes >= 48 || (iscsi_desegment && available_bytes >= 8)) {
const char *opcode_str = NULL;
guint32 data_segment_len;
guint8 opcode = tvb_get_guint8(tvb, offset + 0);
guint8 secondPduByte = tvb_get_guint8(tvb, offset + 1);
int badPdu = FALSE;
if((opcode & TARGET_OPCODE_BIT) == 0) {
/* initiator -> target */
@ -1130,46 +1115,133 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
opcode &= ~(X_BIT | I_BIT);
}
opcode_str = match_strval(opcode, iscsi_opcodes);
data_segment_len = tvb_get_ntohl(tvb, offset + 4) & 0x00ffffff;
if(opcode == ISCSI_OPCODE_SCSI_TASK_MANAGEMENT_COMMAND ||
opcode == ISCSI_OPCODE_SCSI_TASK_MANAGEMENT_RESPONSE ||
opcode == ISCSI_OPCODE_R2T ||
opcode == ISCSI_OPCODE_LOGOUT_COMMAND ||
opcode == ISCSI_OPCODE_LOGOUT_RESPONSE ||
opcode == ISCSI_OPCODE_SNACK_REQUEST)
data_segment_len = 0;
else
data_segment_len = tvb_get_ntohl(tvb, offset + 4) & 0x00ffffff;
/* try and distinguish between data and real headers */
if(opcode_str == NULL ||
(enable_bogosity_filter &&
(data_segment_len > bogus_pdu_data_length_threshold ||
available_bytes > (data_segment_len + 48 + bogus_pdu_max_digest_padding)))) {
#if 1
/* scanning a packet for a valid header is very slow so
* for the moment we just give up */
return FALSE;
#else
/* see if the next word starts a header */
int inc = 4;
offset += inc;
available_bytes -= inc;
#endif
if(opcode_str == NULL) {
badPdu = TRUE;
}
else if(iscsi_port != 0 &&
(((opcode & TARGET_OPCODE_BIT) && pinfo->srcport != iscsi_port) ||
(!(opcode & TARGET_OPCODE_BIT) && pinfo->destport != iscsi_port))) {
badPdu = TRUE;
}
else if(enable_bogosity_filter) {
/* try and distinguish between data and real headers */
if(data_segment_len > bogus_pdu_data_length_threshold) {
badPdu = TRUE;
}
else if(!(secondPduByte & 0x80) &&
(opcode == ISCSI_OPCODE_NOP_OUT ||
opcode == ISCSI_OPCODE_NOP_IN ||
opcode == ISCSI_OPCODE_LOGOUT_COMMAND ||
opcode == ISCSI_OPCODE_LOGOUT_RESPONSE ||
opcode == ISCSI_OPCODE_SCSI_RESPONSE ||
opcode == ISCSI_OPCODE_SCSI_TASK_MANAGEMENT_RESPONSE ||
opcode == ISCSI_OPCODE_R2T ||
opcode == ISCSI_OPCODE_ASYNC_MESSAGE ||
opcode == ISCSI_OPCODE_SNACK_REQUEST ||
opcode == ISCSI_OPCODE_REJECT)) {
badPdu = TRUE;
}
}
if(badPdu) {
return iSCSIPdusDissected > 0;
}
else {
guint32 pduLen = 48;
int digestsActive = 1;
if(opcode == ISCSI_OPCODE_LOGIN_COMMAND ||
opcode == ISCSI_OPCODE_LOGIN_RESPONSE) {
if((secondPduByte & CSG_MASK) == 0) {
/* current stage is SecurityNegotiation, digests
* are not yet turned on */
digestsActive = 0;
}
}
if(opcode == ISCSI_OPCODE_SCSI_COMMAND) {
/* ahsLen */
pduLen += tvb_get_guint8(tvb, offset + 4) * 4;
}
pduLen += data_segment_len;
if((pduLen & 3) != 0)
pduLen += 4 - (pduLen & 3);
if(digestsActive && enableHeaderDigests) {
if(headerDigestIsCRC32)
pduLen += 4;
else
pduLen += headerDigestSize;
}
if(digestsActive && data_segment_len > 0 && enableDataDigests) {
if(dataDigestIsCRC32)
pduLen += 4;
else
pduLen += dataDigestSize;
}
/*
* Desegmentation check.
*/
if(iscsi_desegment && pinfo->can_desegment) {
if(pduLen > available_bytes) {
/*
* This frame doesn't have all of the data for
* this message, but we can do reassembly on it.
*
* Tell the TCP dissector where the data for this
* message starts in the data it handed us, and
* how many more bytes we need, and return.
*/
pinfo->desegment_offset = offset;
pinfo->desegment_len = pduLen - available_bytes;
return TRUE;
}
}
dissect_iscsi_pdu(tvb, pinfo, tree, offset, opcode, opcode_str, data_segment_len);
return TRUE;
if(pduLen > available_bytes)
pduLen = available_bytes;
offset += pduLen;
available_bytes -= pduLen;
++iSCSIPdusDissected;
}
}
return FALSE;
return iSCSIPdusDissected > 0;
}
/* Register the protocol with Ethereal */
/* this format is require because a script is used to build the C function
that calls all the protocol registration.
/*
* this format is require because a script is used to build the C
* function that calls all the protocol registration.
*/
void
proto_register_iscsi(void)
{
/* Setup list of header fields See Section 1.6.1 for details*/
/* Setup list of header fields See Section 1.6.1 for details*/
static hf_register_info hf[] = {
{ &hf_iscsi_AHS,
{ "AHS", "iscsi.ahs",
FT_BYTES, BASE_HEX, NULL, 0,
"Additional header segment", HFILL }
},
{ &hf_iscsi_Padding,
{ "Padding", "iscsi.padding",
FT_BYTES, BASE_HEX, NULL, 0,
@ -1205,11 +1277,26 @@ proto_register_iscsi(void)
FT_BYTES, BASE_HEX, NULL, 0,
"Error PDU Data", HFILL }
},
{ &hf_iscsi_HeaderDigest32,
{ &hf_iscsi_HeaderDigest,
{ "HeaderDigest", "iscsi.headerdigest",
FT_BYTES, BASE_HEX, NULL, 0,
"Header Digest", HFILL }
},
{ &hf_iscsi_HeaderDigest32,
{ "HeaderDigest", "iscsi.headerdigest32",
FT_UINT32, BASE_HEX, NULL, 0,
"Header Digest", HFILL }
},
{ &hf_iscsi_DataDigest,
{ "DataDigest", "iscsi.datadigest",
FT_BYTES, BASE_HEX, NULL, 0,
"Data Digest", HFILL }
},
{ &hf_iscsi_DataDigest32,
{ "DataDigest", "iscsi.datadigest32",
FT_UINT32, BASE_HEX, NULL, 0,
"Data Digest", HFILL }
},
{ &hf_iscsi_Opcode,
{ "Opcode", "iscsi.opcode",
FT_UINT8, BASE_HEX, VALS(iscsi_opcodes), 0,
@ -1447,7 +1534,7 @@ proto_register_iscsi(void)
},
{ &hf_iscsi_Login_CSG,
{ "CSG", "iscsi.login.csg",
FT_UINT8, BASE_HEX, VALS(iscsi_login_stage), 0x0c,
FT_UINT8, BASE_HEX, VALS(iscsi_login_stage), CSG_MASK,
"Current stage", HFILL }
},
{ &hf_iscsi_Login_NSG,
@ -1582,13 +1669,20 @@ proto_register_iscsi(void)
/* Register the protocol name and description */
proto_iscsi = proto_register_protocol("iSCSI", "ISCSI", "iscsi");
/* Required function calls to register the header fields and subtrees used */
/* Required function calls to register the header fields and
* subtrees used */
proto_register_field_array(proto_iscsi, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
{
module_t *iscsi_module = prefs_register_protocol(proto_iscsi, NULL);
prefs_register_bool_preference(iscsi_module,
"desegment_iscsi_messages",
"Desegment iSCSI messages",
"When enabled, iSCSI messages that span multiple TCP segments are desegmented",
&iscsi_desegment);
prefs_register_bool_preference(iscsi_module,
"bogus_pdu_filter",
"Enable bogus pdu filter",
@ -1601,20 +1695,61 @@ proto_register_iscsi(void)
"Treat packets whose data segment length is greater than this value as bogus",
10,
&bogus_pdu_data_length_threshold);
prefs_register_uint_preference(iscsi_module,
"bogus_pdu_max_digest_padding",
"Bogus pdu max digest padding",
"Treat packets whose apparent total digest size is greater than this value as bogus",
"iscsi_port",
"Target port",
"Port number of iSCSI target",
10,
&bogus_pdu_max_digest_padding);
&iscsi_port);
prefs_register_bool_preference(iscsi_module,
"enable_header_digests",
"Enable header digests",
"When enabled, pdus are assumed to contain a header digest",
&enableHeaderDigests);
prefs_register_bool_preference(iscsi_module,
"enable_data_digests",
"Enable data digests",
"When enabled, pdus are assumed to contain a data digest",
&enableDataDigests);
prefs_register_bool_preference(iscsi_module,
"header_digest_is_crc32c",
"Header digest is CRC32C",
"When enabled, header digests are assumed to be CRC32C",
&headerDigestIsCRC32);
prefs_register_bool_preference(iscsi_module,
"data_digest_is_crc32c",
"Data digest is CRC32C",
"When enabled, data digests are assumed to be CRC32C",
&dataDigestIsCRC32);
prefs_register_uint_preference(iscsi_module,
"header_digest_size",
"Header digest size",
"The size of a header digest (bytes)",
10,
&headerDigestSize);
prefs_register_uint_preference(iscsi_module,
"data_digest_size",
"Data digest size",
"The size of a data digest (bytes)",
10,
&dataDigestSize);
}
}
/* If this dissector uses sub-dissector registration add a registration routine.
This format is required because a script is used to find these routines and
create the code that calls these routines.
*/
/*
* If this dissector uses sub-dissector registration add a
* registration routine.
*/
/*
* This format is required because a script is used to find these
* routines and create the code that calls these routines.
*/
void
proto_reg_handoff_iscsi(void)
{