diff options
Diffstat (limited to 'epan/dissectors/packet-iscsi.c')
-rw-r--r-- | epan/dissectors/packet-iscsi.c | 2511 |
1 files changed, 2511 insertions, 0 deletions
diff --git a/epan/dissectors/packet-iscsi.c b/epan/dissectors/packet-iscsi.c new file mode 100644 index 0000000000..c141535e6c --- /dev/null +++ b/epan/dissectors/packet-iscsi.c @@ -0,0 +1,2511 @@ +/* packet-iscsi.c + * Routines for iSCSI dissection + * Copyright 2001, Eurologic and Mark Burton <markb@ordern.com> + * 2004 Request/Response matching and Service Response Time: ronnie sahlberg + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> + +#include <epan/packet.h> +#include "prefs.h" +#include <epan/conversation.h> +#include "packet-scsi.h" +#include "epan/nstime.h" + +/* the absolute values of these constants don't matter as long as + * latter revisions of the protocol are assigned a larger number */ +#define ISCSI_PROTOCOL_DRAFT08 1 +#define ISCSI_PROTOCOL_DRAFT09 2 +#define ISCSI_PROTOCOL_DRAFT11 3 +#define ISCSI_PROTOCOL_DRAFT12 4 +#define ISCSI_PROTOCOL_DRAFT13 5 + +static enum_val_t iscsi_protocol_versions[] = { + { "draft-08", "Draft 08", ISCSI_PROTOCOL_DRAFT08 }, + { "draft-09", "Draft 09", ISCSI_PROTOCOL_DRAFT09 }, + { "draft-11", "Draft 11", ISCSI_PROTOCOL_DRAFT11 }, + { "draft-12", "Draft 12", ISCSI_PROTOCOL_DRAFT12 }, + { "draft-13", "Draft 13", ISCSI_PROTOCOL_DRAFT13 }, + { NULL, NULL, 0 } +}; + +static gint iscsi_protocol_version = ISCSI_PROTOCOL_DRAFT13; + +static gboolean iscsi_desegment = TRUE; + +static int demand_good_f_bit = 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 guint iscsi_port = 3260; + +/* Initialize the protocol and registered fields */ +static int proto_iscsi = -1; +static int hf_iscsi_time = -1; +static int hf_iscsi_request_frame = -1; +static int hf_iscsi_data_in_frame = -1; +static int hf_iscsi_data_out_frame = -1; +static int hf_iscsi_response_frame = -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; +static int hf_iscsi_write_data = -1; +static int hf_iscsi_read_data = -1; +static int hf_iscsi_error_pdu_data = -1; +static int hf_iscsi_async_message_data = -1; +static int hf_iscsi_vendor_specific_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; +/* #ifdef DRAFT08 */ +static int hf_iscsi_X = -1; +/* #endif */ +static int hf_iscsi_I = -1; +static int hf_iscsi_SCSICommand_F = -1; +static int hf_iscsi_SCSICommand_R = -1; +static int hf_iscsi_SCSICommand_W = -1; +static int hf_iscsi_SCSICommand_Attr = -1; +static int hf_iscsi_SCSICommand_CRN = -1; +static int hf_iscsi_SCSICommand_AddCDB = -1; +static int hf_iscsi_DataSegmentLength = -1; +static int hf_iscsi_TotalAHSLength = -1; +static int hf_iscsi_LUN = -1; +static int hf_iscsi_InitiatorTaskTag = -1; +static int hf_iscsi_ExpectedDataTransferLength = -1; +static int hf_iscsi_CmdSN = -1; +static int hf_iscsi_ExpStatSN = -1; +static int hf_iscsi_StatSN = -1; +static int hf_iscsi_ExpCmdSN = -1; +static int hf_iscsi_MaxCmdSN = -1; +static int hf_iscsi_SCSIResponse_o = -1; +static int hf_iscsi_SCSIResponse_u = -1; +static int hf_iscsi_SCSIResponse_O = -1; +static int hf_iscsi_SCSIResponse_U = -1; +static int hf_iscsi_SCSIResponse_BidiReadResidualCount = -1; +static int hf_iscsi_SCSIResponse_ResidualCount = -1; +static int hf_iscsi_SCSIResponse_Response = -1; +static int hf_iscsi_SCSIResponse_Status = -1; +static int hf_iscsi_SenseLength = -1; +static int hf_iscsi_SCSIData_F = -1; +static int hf_iscsi_SCSIData_A = -1; +static int hf_iscsi_SCSIData_S = -1; +static int hf_iscsi_SCSIData_O = -1; +static int hf_iscsi_SCSIData_U = -1; +static int hf_iscsi_TargetTransferTag = -1; +static int hf_iscsi_DataSN = -1; +static int hf_iscsi_BufferOffset = -1; +static int hf_iscsi_SCSIData_ResidualCount = -1; +static int hf_iscsi_VersionMin = -1; +static int hf_iscsi_VersionMax = -1; +static int hf_iscsi_VersionActive = -1; +static int hf_iscsi_CID = -1; +static int hf_iscsi_ISID8 = -1; +static int hf_iscsi_ISID = -1; +/* #if defined(DRAFT09) */ +static int hf_iscsi_ISID_Type = -1; +static int hf_iscsi_ISID_NamingAuthority = -1; +static int hf_iscsi_ISID_Qualifier = -1; +/* #elif !defined(DRAFT08) */ +static int hf_iscsi_ISID_t = -1; +static int hf_iscsi_ISID_a = -1; +static int hf_iscsi_ISID_b = -1; +static int hf_iscsi_ISID_c = -1; +static int hf_iscsi_ISID_d = -1; +/* #endif */ +static int hf_iscsi_TSID = -1; +static int hf_iscsi_TSIH = -1; +static int hf_iscsi_InitStatSN = -1; +static int hf_iscsi_InitCmdSN = -1; +/* #ifdef DRAFT09 */ +static int hf_iscsi_Login_X = -1; +/* #endif */ +static int hf_iscsi_Login_C = -1; +static int hf_iscsi_Login_T = -1; +static int hf_iscsi_Login_CSG = -1; +static int hf_iscsi_Login_NSG = -1; +static int hf_iscsi_Login_Status = -1; +static int hf_iscsi_KeyValue = -1; +static int hf_iscsi_Text_C = -1; +static int hf_iscsi_Text_F = -1; +static int hf_iscsi_ExpDataSN = -1; +static int hf_iscsi_R2TSN = -1; +static int hf_iscsi_TaskManagementFunction_ReferencedTaskTag = -1; +static int hf_iscsi_RefCmdSN = -1; +static int hf_iscsi_TaskManagementFunction_Function = -1; +static int hf_iscsi_TaskManagementFunction_Response = -1; +static int hf_iscsi_Logout_Reason = -1; +static int hf_iscsi_Logout_Response = -1; +static int hf_iscsi_Time2Wait = -1; +static int hf_iscsi_Time2Retain = -1; +static int hf_iscsi_DesiredDataLength = -1; +static int hf_iscsi_AsyncEvent = -1; +static int hf_iscsi_EventVendorCode = -1; +static int hf_iscsi_Parameter1 = -1; +static int hf_iscsi_Parameter2 = -1; +static int hf_iscsi_Parameter3 = -1; +static int hf_iscsi_Reject_Reason = -1; +static int hf_iscsi_snack_type = -1; +static int hf_iscsi_BegRun = -1; +static int hf_iscsi_RunLength = -1; + +/* Initialize the subtree pointers */ +static gint ett_iscsi = -1; +static gint ett_iscsi_KeyValues = -1; +static gint ett_iscsi_CDB = -1; +static gint ett_iscsi_Flags = -1; +/* #ifndef DRAFT08 */ +static gint ett_iscsi_ISID = -1; +/* #endif */ + + +/* #ifdef DRAFT08 */ +#define X_BIT 0x80 +/* #endif */ + +#define I_BIT 0x40 + +#define OPCODE_MASK 0x3f + +#define TARGET_OPCODE_BIT 0x20 + +#define ISCSI_OPCODE_NOP_OUT 0x00 +#define ISCSI_OPCODE_SCSI_COMMAND 0x01 +#define ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION 0x02 +#define ISCSI_OPCODE_LOGIN_COMMAND 0x03 +#define ISCSI_OPCODE_TEXT_COMMAND 0x04 +#define ISCSI_OPCODE_SCSI_DATA_OUT 0x05 +#define ISCSI_OPCODE_LOGOUT_COMMAND 0x06 +#define ISCSI_OPCODE_SNACK_REQUEST 0x10 +#define ISCSI_OPCODE_VENDOR_SPECIFIC_I0 0x1c +#define ISCSI_OPCODE_VENDOR_SPECIFIC_I1 0x1d +#define ISCSI_OPCODE_VENDOR_SPECIFIC_I2 0x1e + +#define ISCSI_OPCODE_NOP_IN 0x20 +#define ISCSI_OPCODE_SCSI_RESPONSE 0x21 +#define ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION_RESPONSE 0x22 +#define ISCSI_OPCODE_LOGIN_RESPONSE 0x23 +#define ISCSI_OPCODE_TEXT_RESPONSE 0x24 +#define ISCSI_OPCODE_SCSI_DATA_IN 0x25 +#define ISCSI_OPCODE_LOGOUT_RESPONSE 0x26 +#define ISCSI_OPCODE_R2T 0x31 +#define ISCSI_OPCODE_ASYNC_MESSAGE 0x32 +#define ISCSI_OPCODE_REJECT 0x3f +#define ISCSI_OPCODE_VENDOR_SPECIFIC_T0 0x3c +#define ISCSI_OPCODE_VENDOR_SPECIFIC_T1 0x3d +#define ISCSI_OPCODE_VENDOR_SPECIFIC_T2 0x3e + +#define CSG_SHIFT 2 +#define CSG_MASK (0x03 << CSG_SHIFT) +#define NSG_MASK 0x03 + +#define ISCSI_CSG_SECURITY_NEGOTIATION (0 << CSG_SHIFT) +#define ISCSI_CSG_OPERATIONAL_NEGOTIATION (1 << CSG_SHIFT) +#define ISCSI_CSG_FULL_FEATURE_PHASE (3 << CSG_SHIFT) + +#define ISCSI_SCSI_DATA_FLAG_S 0x01 +#define ISCSI_SCSI_DATA_FLAG_U 0x02 +#define ISCSI_SCSI_DATA_FLAG_O 0x04 +#define ISCSI_SCSI_DATA_FLAG_A 0x40 +#define ISCSI_SCSI_DATA_FLAG_F 0x80 + +static const value_string iscsi_opcodes[] = { + { ISCSI_OPCODE_NOP_OUT, "NOP Out" }, + { ISCSI_OPCODE_SCSI_COMMAND, "SCSI Command" }, + { ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION, "Task Management Function" }, + { ISCSI_OPCODE_LOGIN_COMMAND, "Login Command" }, + { ISCSI_OPCODE_TEXT_COMMAND, "Text Command" }, + { ISCSI_OPCODE_SCSI_DATA_OUT, "SCSI Data Out" }, + { ISCSI_OPCODE_LOGOUT_COMMAND, "Logout Command" }, + { ISCSI_OPCODE_SNACK_REQUEST, "SNACK Request" }, + { ISCSI_OPCODE_VENDOR_SPECIFIC_I0, "Vendor Specific I0" }, + { ISCSI_OPCODE_VENDOR_SPECIFIC_I1, "Vendor Specific I1" }, + { ISCSI_OPCODE_VENDOR_SPECIFIC_I2, "Vendor Specific I2" }, + + { ISCSI_OPCODE_NOP_IN, "NOP In" }, + { ISCSI_OPCODE_SCSI_RESPONSE, "SCSI Response" }, + { ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION_RESPONSE, "Task Management Function Response" }, + { ISCSI_OPCODE_LOGIN_RESPONSE, "Login Response" }, + { ISCSI_OPCODE_TEXT_RESPONSE, "Text Response" }, + { ISCSI_OPCODE_SCSI_DATA_IN, "SCSI Data In" }, + { ISCSI_OPCODE_LOGOUT_RESPONSE, "Logout Response" }, + { ISCSI_OPCODE_R2T, "Ready To Transfer" }, + { ISCSI_OPCODE_ASYNC_MESSAGE, "Asynchronous Message" }, + { ISCSI_OPCODE_REJECT, "Reject"}, + { ISCSI_OPCODE_VENDOR_SPECIFIC_T0, "Vendor Specific T0" }, + { ISCSI_OPCODE_VENDOR_SPECIFIC_T1, "Vendor Specific T1" }, + { ISCSI_OPCODE_VENDOR_SPECIFIC_T2, "Vendor Specific T2" }, + {0, NULL}, +}; + +/* #ifdef DRAFT08 */ +static const true_false_string iscsi_meaning_X = { + "Retry", + "Not retry" +}; +/* #endif */ + +/* #ifdef DRAFT09 */ +static const true_false_string iscsi_meaning_login_X = { + "Reinstate failed connection", + "New connection" +}; +/* #endif */ + +static const true_false_string iscsi_meaning_I = { + "Immediate delivery", + "Queued delivery" +}; + +static const true_false_string iscsi_meaning_F = { + "Final PDU in sequence", + "Not final PDU in sequence" +}; + +static const true_false_string iscsi_meaning_A = { + "Acknowledge requested", + "Acknowledge not requested" +}; + +static const true_false_string iscsi_meaning_T = { + "Transit to next login stage", + "Stay in current login stage" +}; + +static const true_false_string iscsi_meaning_C = { + "Text is incomplete", + "Text is complete" +}; + +static const true_false_string iscsi_meaning_S = { + "Response contains SCSI status", + "Response does not contain SCSI status" +}; + +static const true_false_string iscsi_meaning_R = { + "Data will be read from target", + "No data will be read from target" +}; + +static const true_false_string iscsi_meaning_W = { + "Data will be written to target", + "No data will be written to target" +}; + +static const true_false_string iscsi_meaning_o = { + "Read part of bi-directional command overflowed", + "No overflow of read part of bi-directional command", +}; + +static const true_false_string iscsi_meaning_u = { + "Read part of bi-directional command underflowed", + "No underflow of read part of bi-directional command", +}; + +static const true_false_string iscsi_meaning_O = { + "Residual overflow occurred", + "No residual overflow occurred", +}; + +static const true_false_string iscsi_meaning_U = { + "Residual underflow occurred", + "No residual underflow occurred", +}; + +static const value_string iscsi_scsi_responses[] = { + { 0, "Command completed at target" }, + { 1, "Response does not contain SCSI status"}, + { 0, NULL } +}; + +static const value_string iscsi_scsicommand_taskattrs[] = { + {0, "Untagged"}, + {1, "Simple"}, + {2, "Ordered"}, + {3, "Head of Queue"}, + {4, "ACA"}, + {0, NULL}, +}; + +static const value_string iscsi_task_management_responses[] = { + {0, "Function complete"}, + {1, "Task not in task set"}, + {2, "LUN does not exist"}, + {3, "Task still allegiant"}, + {4, "Task failover not supported"}, + {5, "Task management function not supported"}, + {6, "Authorisation failed"}, + {255, "Function rejected"}, + {0, NULL}, +}; + +static const value_string iscsi_task_management_functions[] = { + {1, "Abort Task"}, + {2, "Abort Task Set"}, + {3, "Clear ACA"}, + {4, "Clear Task Set"}, + {5, "Logical Unit Reset"}, + {6, "Target Warm Reset"}, + {7, "Target Cold Reset"}, + {0, NULL}, +}; + +static const value_string iscsi_login_status[] = { + {0x0000, "Success"}, + {0x0101, "Target moved temporarily"}, + {0x0102, "Target moved permanently"}, + {0x0200, "Initiator error (miscellaneous error)"}, + {0x0201, "Authentication failed"}, + {0x0202, "Authorisation failure"}, + {0x0203, "Target not found"}, + {0x0204, "Target removed"}, + {0x0205, "Unsupported version"}, + {0x0206, "Too many connections"}, + {0x0207, "Missing parameter"}, + {0x0208, "Can't include in session"}, + {0x0209, "Session type not supported"}, + {0x020a, "Session does not exist"}, + {0x020b, "Invalid request during login"}, + {0x0300, "Target error (miscellaneous error)"}, + {0x0301, "Service unavailable"}, + {0x0302, "Out of resources"}, + {0, NULL}, +}; + +static const value_string iscsi_login_stage[] = { + {0, "Security negotiation"}, + {1, "Operational negotiation"}, + {3, "Full feature phase"}, + {0, NULL}, +}; + +/* #ifndef DRAFT08 */ +static const value_string iscsi_isid_type[] = { + {0x00, "IEEE OUI"}, + {0x01, "IANA Enterprise Number"}, + {0x02, "Random"}, + {0, NULL}, +}; +/* #endif */ + +static const value_string iscsi_logout_reasons[] = { + {0, "Close session"}, + {1, "Close connection"}, + {2, "Remove connection for recovery"}, + {0, NULL}, +}; + +static const value_string iscsi_logout_response[] = { + {0, "Connection closed successfully"}, + {1, "CID not found"}, + {2, "Connection recovery not supported"}, + {3, "Cleanup failed for various reasons"}, + {0, NULL}, +}; + +static const value_string iscsi_asyncevents[] = { + {0, "A SCSI asynchronous event is reported in the sense data"}, + {1, "Target requests logout"}, + {2, "Target will/has dropped connection"}, + {3, "Target will/has dropped all connections"}, + {4, "Target requests parameter negotiation"}, + {0, NULL}, +}; + +static const value_string iscsi_snack_types[] = { + {0, "Data/R2T"}, + {1, "Status"}, +/* #ifndef DRAFT08 */ + {2, "Data ACK"}, +/* #endif */ + {3, "R-Data"}, + {0, NULL} +}; + +static const value_string iscsi_reject_reasons[] = { +/* #ifdef DRAFT08 */ + {0x01, "Full feature phase command before login"}, +/* #endif */ + {0x02, "Data (payload) digest error"}, + {0x03, "Data SNACK reject"}, + {0x04, "Protocol error"}, + {0x05, "Command not supported in this session type"}, + {0x06, "Immediate command reject (too many immediate commands)"}, + {0x07, "Task in progress"}, + {0x08, "Invalid Data Ack"}, + {0x09, "Invalid PDU field"}, + {0x0a, "Long operation reject"}, + {0x0b, "Negotiation reset"}, + {0x0c, "Waiting for logout"}, + {0, NULL}, +}; + +/*****************************************************************/ +/* */ +/* CRC LOOKUP TABLE */ +/* ================ */ +/* The following CRC lookup table was generated automagically */ +/* by the Rocksoft^tm Model CRC Algorithm Table Generation */ +/* Program V1.0 using the following model parameters: */ +/* */ +/* Width : 4 bytes. */ +/* Poly : 0x1EDC6F41L */ +/* Reverse : TRUE. */ +/* */ +/* For more information on the Rocksoft^tm Model CRC Algorithm, */ +/* see the document titled "A Painless Guide to CRC Error */ +/* Detection Algorithms" by Ross Williams */ +/* (ross@guest.adelaide.edu.au.). This document is likely to be */ +/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ +/* */ +/*****************************************************************/ + +static guint32 crc32Table[256] = { + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L +}; + +#define CRC32C_PRELOAD 0xffffffff + +/* + * Byte swap fix contributed by Dave Wysochanski <davidw@netapp.com> + */ +#define CRC32C_SWAP(crc32c_value) \ + (((crc32c_value & 0xff000000) >> 24) | \ + ((crc32c_value & 0x00ff0000) >> 8) | \ + ((crc32c_value & 0x0000ff00) << 8) | \ + ((crc32c_value & 0x000000ff) << 24)) + +static guint32 +calculateCRC32(const void *buf, int len, guint32 crc) { + const guint8 *p = (const guint8 *)buf; + crc = CRC32C_SWAP(crc); + while(len-- > 0) + crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8); + return CRC32C_SWAP(crc); +} + + + + + +/* structure and functions to keep track of + * COMMAND/DATA_IN/DATA_OUT/RESPONSE matching + */ +typedef struct _iscsi_conv_data { + guint32 conv_idx; + guint32 itt; + guint32 request_frame; + guint32 data_in_frame; + guint32 data_out_frame; + guint32 response_frame; + guint32 iscsi_dl; + nstime_t req_time; +} iscsi_conv_data_t; +static GHashTable *iscsi_req_unmatched = NULL; +static GHashTable *iscsi_req_matched = NULL; +static GMemChunk *iscsi_req_vals = NULL; +static guint32 iscsi_init_count = 200; + +static gint +iscsi_equal_unmatched(gconstpointer v, gconstpointer w) +{ + const iscsi_conv_data_t *v1 = (const iscsi_conv_data_t *)v; + const iscsi_conv_data_t *v2 = (const iscsi_conv_data_t *)w; + + return (v1->conv_idx == v2->conv_idx)&&(v1->itt == v2->itt); +} + +static guint +iscsi_hash_unmatched (gconstpointer v) +{ + const iscsi_conv_data_t *key = (const iscsi_conv_data_t *)v; + guint val; + + val = key->conv_idx + key->itt; + + return val; +} + +static gint +iscsi_equal_matched(gconstpointer v, gconstpointer w) +{ + const iscsi_conv_data_t *v1 = (const iscsi_conv_data_t *)v; + const iscsi_conv_data_t *v2 = (const iscsi_conv_data_t *)w; + int check_frame; + + check_frame=0; + if (v1->request_frame && (v1->request_frame==v2->request_frame)) + check_frame=1; + if (v1->data_in_frame && (v1->data_in_frame==v2->data_in_frame)) + check_frame=1; + if (v1->data_out_frame && (v1->data_out_frame==v2->data_out_frame)) + check_frame=1; + if (v1->response_frame && (v1->response_frame==v2->response_frame)) + check_frame=1; + + return check_frame&&(v1->conv_idx == v2->conv_idx)&&(v1->itt == v2->itt); +} + +static guint +iscsi_hash_matched (gconstpointer v) +{ + const iscsi_conv_data_t *key = (const iscsi_conv_data_t *)v; + guint val; + + val = key->conv_idx + key->itt; + + return val; +} + + + + + + +/* + * Protocol initialization + */ +static void +iscsi_init_protocol(void) +{ + if (iscsi_req_vals) + g_mem_chunk_destroy(iscsi_req_vals); + if (iscsi_req_unmatched) + g_hash_table_destroy(iscsi_req_unmatched); + if (iscsi_req_matched) + g_hash_table_destroy(iscsi_req_matched); + + iscsi_req_unmatched = g_hash_table_new(iscsi_hash_unmatched, iscsi_equal_unmatched); + iscsi_req_matched = g_hash_table_new(iscsi_hash_matched, iscsi_equal_matched); + iscsi_req_vals = g_mem_chunk_new("iscsi_req_vals", + sizeof(iscsi_conv_data_t), + iscsi_init_count * sizeof(iscsi_conv_data_t), + G_ALLOC_AND_FREE); +} + +static int +iscsi_min(int a, int b) { + return (a < b)? a : b; +} + +static gint +addTextKeys(proto_tree *tt, tvbuff_t *tvb, gint offset, guint32 text_len) { + const gint limit = offset + text_len; + while(offset < limit) { + gint len = tvb_strnlen(tvb, offset, limit - offset); + if(len == -1) + len = limit - offset; + else + len = len + 1; + proto_tree_add_item(tt, hf_iscsi_KeyValue, tvb, offset, len, FALSE); + offset += len; + } + return offset; +} + +static gint +handleHeaderDigest(proto_item *ti, tvbuff_t *tvb, guint offset, int headerLen) { + int available_bytes = tvb_length_remaining(tvb, offset); + 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, should be 0x%08x)", sent, crc); + } + } + 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 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, should be 0x%08x)", sent, crc); + } + } + 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 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_tree *ti = NULL; + guint8 scsi_status = 0; + gboolean S_bit=FALSE; + guint cdb_offset = offset + 32; /* offset of CDB from start of PDU */ + guint end_offset = offset + tvb_length_remaining(tvb, offset); + conversation_t *conversation = NULL; + iscsi_conv_data_t *cdata = NULL; + scsi_task_id_t task_key; + int paddedDataSegmentLength = data_segment_len; + if(paddedDataSegmentLength & 3) + paddedDataSegmentLength += 4 - (paddedDataSegmentLength & 3); + + /* Make entries in Protocol column and Info column on summary display */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "iSCSI"); + + if (opcode == ISCSI_OPCODE_SCSI_RESPONSE || + opcode == ISCSI_OPCODE_SCSI_DATA_IN) { + scsi_status = tvb_get_guint8 (tvb, offset+3); + } + + if ((opcode == ISCSI_OPCODE_SCSI_RESPONSE) || + (opcode == ISCSI_OPCODE_SCSI_DATA_IN) || + (opcode == ISCSI_OPCODE_SCSI_DATA_OUT)) { + conversation = find_conversation (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, + pinfo->destport, 0); + if (conversation) { + if (!pinfo->fd->flags.visited){ + iscsi_conv_data_t ckey; + ckey.conv_idx = conversation->index; + ckey.itt = tvb_get_ntohl (tvb, offset+16); + + /* first time we see this packet. check if we can find the request */ + cdata = (iscsi_conv_data_t *)g_hash_table_lookup (iscsi_req_unmatched, &ckey); + if (cdata){ + if (cdata->data_in_frame+cdata->data_out_frame+cdata->response_frame==0){ + /* this is the first response to the request, add it to the matched table */ + g_hash_table_insert (iscsi_req_matched, cdata, cdata); + } + switch(opcode){ + case ISCSI_OPCODE_SCSI_RESPONSE: + cdata->response_frame=pinfo->fd->num; + break; + case ISCSI_OPCODE_SCSI_DATA_IN: + /* a bit ugly but we need to check the S bit here */ + if(tvb_get_guint8(tvb, offset+1)&ISCSI_SCSI_DATA_FLAG_S){ + cdata->response_frame=pinfo->fd->num; + } + cdata->data_in_frame=pinfo->fd->num; + break; + case ISCSI_OPCODE_SCSI_DATA_OUT: + cdata->data_out_frame=pinfo->fd->num; + break; + } + } + } else { + iscsi_conv_data_t ckey; + ckey.conv_idx = conversation->index; + ckey.itt = tvb_get_ntohl (tvb, offset+16); + ckey.request_frame=0; + ckey.data_in_frame=0; + ckey.data_out_frame=0; + ckey.response_frame=0; + switch(opcode){ + case ISCSI_OPCODE_SCSI_RESPONSE: + ckey.response_frame=pinfo->fd->num; + break; + case ISCSI_OPCODE_SCSI_DATA_IN: + ckey.data_in_frame=pinfo->fd->num; + break; + case ISCSI_OPCODE_SCSI_DATA_OUT: + ckey.data_out_frame=pinfo->fd->num; + break; + } + + /* we have seen this one before, pick it up from the matched table */ + cdata = (iscsi_conv_data_t *)g_hash_table_lookup (iscsi_req_matched, &ckey); + } + + if (cdata){ + task_key.conv_id = cdata->conv_idx; + task_key.task_id = cdata->itt; + pinfo->private_data = &task_key; + } else { + pinfo->private_data = NULL; + } + } else { + /* no conversation, meaning we didn't see the request */ + pinfo->private_data = NULL; + } + + } else if (opcode == ISCSI_OPCODE_SCSI_COMMAND) { + conversation = find_conversation (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, + pinfo->destport, 0); + if (!conversation) { + conversation = conversation_new (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, + pinfo->destport, 0); + } + + if (!pinfo->fd->flags.visited){ + iscsi_conv_data_t ckey; + + /* first time we see this packet. */ + /*check if we have seen this request before and delete it in that case */ + ckey.conv_idx = conversation->index; + ckey.itt = tvb_get_ntohl (tvb, offset+16); + cdata = (iscsi_conv_data_t *)g_hash_table_lookup (iscsi_req_unmatched, &ckey); + if (cdata){ + g_hash_table_remove(iscsi_req_unmatched, &ckey); + } + + /* add this new transaction to the unmatched table */ + cdata = g_mem_chunk_alloc (iscsi_req_vals); + cdata->conv_idx = conversation->index; + cdata->itt = tvb_get_ntohl (tvb, offset+16); + cdata->request_frame=pinfo->fd->num; + cdata->data_in_frame=0; + cdata->data_out_frame=0; + cdata->response_frame=0; + cdata->req_time.nsecs = pinfo->fd->abs_usecs*1000; + cdata->req_time.secs = pinfo->fd->abs_secs; + + g_hash_table_insert (iscsi_req_unmatched, cdata, cdata); + } else { + iscsi_conv_data_t ckey; + ckey.conv_idx = conversation->index; + ckey.itt = tvb_get_ntohl (tvb, offset+16); + ckey.request_frame=pinfo->fd->num; + ckey.data_in_frame=0; + ckey.data_out_frame=0; + ckey.response_frame=0; + + /* we have seen this one before, pick it up from the matched table */ + cdata = (iscsi_conv_data_t *)g_hash_table_lookup (iscsi_req_matched, &ckey); + } + + + if (cdata){ + /* The SCSI protocol uses this as the key to detect a + * SCSI-level conversation. */ + task_key.conv_id = cdata->conv_idx; + task_key.task_id = cdata->itt; + pinfo->private_data = &task_key; + } else { + pinfo->private_data=NULL; + } + } + else { + pinfo->private_data = NULL; + } + + if (check_col(pinfo->cinfo, COL_INFO)) { + + if (opcode != ISCSI_OPCODE_SCSI_COMMAND) { + + col_append_str(pinfo->cinfo, COL_INFO, opcode_str); + + if (opcode == ISCSI_OPCODE_SCSI_RESPONSE || + (opcode == ISCSI_OPCODE_SCSI_DATA_IN && + (tvb_get_guint8(tvb, offset + 1) & ISCSI_SCSI_DATA_FLAG_S))) { + col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)", + val_to_str (scsi_status, scsi_status_val, "0x%x")); + } + else if (opcode == ISCSI_OPCODE_LOGIN_RESPONSE) { + guint16 login_status = tvb_get_ntohs(tvb, offset+36); + col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)", + val_to_str (login_status, iscsi_login_status, "0x%x")); + } + else if (opcode == ISCSI_OPCODE_LOGOUT_COMMAND) { + guint8 logoutReason; + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08) { + logoutReason = tvb_get_guint8(tvb, offset+11); + } else if(iscsi_protocol_version >= ISCSI_PROTOCOL_DRAFT13) { + logoutReason = tvb_get_guint8(tvb, offset+1) & 0x7f; + } + else { + logoutReason = tvb_get_guint8(tvb, offset+23); + } + col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)", + val_to_str (logoutReason, iscsi_logout_reasons, "0x%x")); + } + else if (opcode == ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION) { + guint8 tmf = tvb_get_guint8(tvb, offset + 1); + col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)", + val_to_str (tmf, iscsi_task_management_functions, "0x%x")); + } + else if (opcode == ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION_RESPONSE) { + guint8 resp = tvb_get_guint8(tvb, offset + 2); + col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)", + val_to_str (resp, iscsi_task_management_responses, "0x%x")); + } + else if (opcode == ISCSI_OPCODE_REJECT) { + guint8 reason = tvb_get_guint8(tvb, offset + 2); + col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)", + val_to_str (reason, iscsi_reject_reasons, "0x%x")); + } + else if (opcode == ISCSI_OPCODE_ASYNC_MESSAGE) { + guint8 asyncEvent = tvb_get_guint8(tvb, offset + 36); + col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)", + val_to_str (asyncEvent, iscsi_asyncevents, "0x%x")); + } + } + } + + /* In the interest of speed, if "tree" is NULL, don't do any + work not necessary to generate protocol tree items. */ + if (tree) { + proto_item *tp; + /* create display subtree for the protocol */ + tp = proto_tree_add_protocol_format(tree, proto_iscsi, tvb, + offset, -1, "iSCSI (%s)", + opcode_str); + ti = proto_item_add_subtree(tp, ett_iscsi); + } + proto_tree_add_uint(ti, hf_iscsi_Opcode, tvb, + offset + 0, 1, opcode); + if((opcode & TARGET_OPCODE_BIT) == 0) { + /* initiator -> target */ + gint b = tvb_get_guint8(tvb, offset + 0); + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08) { + if(opcode != ISCSI_OPCODE_SCSI_DATA_OUT && + opcode != ISCSI_OPCODE_LOGOUT_COMMAND && + opcode != ISCSI_OPCODE_SNACK_REQUEST) + proto_tree_add_boolean(ti, hf_iscsi_X, tvb, offset + 0, 1, b); + } + if(opcode != ISCSI_OPCODE_SCSI_DATA_OUT && + opcode != ISCSI_OPCODE_LOGIN_COMMAND && + opcode != ISCSI_OPCODE_SNACK_REQUEST) + proto_tree_add_boolean(ti, hf_iscsi_I, tvb, offset + 0, 1, b); + } + + + if(opcode == ISCSI_OPCODE_NOP_OUT) { + /* NOP Out */ + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + 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 = 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 */ + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_TargetTransferTag, tvb, offset + 20, 4, FALSE); + 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 = 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); + proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_Flags); + + proto_tree_add_boolean(tt, hf_iscsi_SCSICommand_F, tvb, offset + 1, 1, b); + proto_tree_add_boolean(tt, hf_iscsi_SCSICommand_R, tvb, offset + 1, 1, b); + proto_tree_add_boolean(tt, hf_iscsi_SCSICommand_W, tvb, offset + 1, 1, b); + proto_tree_add_uint(tt, hf_iscsi_SCSICommand_Attr, tvb, offset + 1, 1, b); + } + if(iscsi_protocol_version < ISCSI_PROTOCOL_DRAFT12) { + proto_tree_add_item(ti, hf_iscsi_SCSICommand_CRN, tvb, offset + 3, 1, FALSE); + } + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_ExpectedDataTransferLength, 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); + { + 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 */ + { + gint b = tvb_get_guint8(tvb, offset + 1); + 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); + + proto_tree_add_boolean(tt, hf_iscsi_SCSIResponse_o, tvb, offset + 1, 1, b); + proto_tree_add_boolean(tt, hf_iscsi_SCSIResponse_u, tvb, offset + 1, 1, b); + proto_tree_add_boolean(tt, hf_iscsi_SCSIResponse_O, tvb, offset + 1, 1, b); + proto_tree_add_boolean(tt, hf_iscsi_SCSIResponse_U, tvb, offset + 1, 1, b); + } + proto_tree_add_item(ti, hf_iscsi_SCSIResponse_Response, tvb, offset + 2, 1, FALSE); + proto_tree_add_item(ti, hf_iscsi_SCSIResponse_Status, tvb, offset + 3, 1, FALSE); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + if(iscsi_protocol_version <= ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_SCSIResponse_ResidualCount, tvb, offset + 20, 4, FALSE); + } + 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); + proto_tree_add_item(ti, hf_iscsi_ExpDataSN, tvb, offset + 36, 4, FALSE); + if(iscsi_protocol_version <= ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_SCSIResponse_BidiReadResidualCount, tvb, offset + 44, 4, FALSE); + } + else { + proto_tree_add_item(ti, hf_iscsi_SCSIResponse_BidiReadResidualCount, tvb, offset + 40, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_SCSIResponse_ResidualCount, tvb, offset + 44, 4, FALSE); + } + offset = handleHeaderDigest(ti, tvb, offset, 48); + /* do not update offset here because the data segment is + * dissected below */ + handleDataDigest(ti, tvb, offset, paddedDataSegmentLength); + } else if(opcode == ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION) { + /* Task Management Function */ + proto_tree_add_item(ti, hf_iscsi_TaskManagementFunction_Function, tvb, offset + 1, 1, FALSE); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + } + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_TaskManagementFunction_ReferencedTaskTag, 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); + proto_tree_add_item(ti, hf_iscsi_RefCmdSN, tvb, offset + 32, 4, FALSE); + offset = handleHeaderDigest(ti, tvb, offset, 48); + } else if(opcode == ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION_RESPONSE) { + /* Task Management Function Response */ + proto_tree_add_item(ti, hf_iscsi_TaskManagementFunction_Response, tvb, offset + 2, 1, FALSE); + if(iscsi_protocol_version <= ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + } + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + if(iscsi_protocol_version < ISCSI_PROTOCOL_DRAFT12) { + proto_tree_add_item(ti, hf_iscsi_TaskManagementFunction_ReferencedTaskTag, tvb, offset + 20, 4, FALSE); + } + 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 = handleHeaderDigest(ti, tvb, offset, 48); + } else if(opcode == ISCSI_OPCODE_LOGIN_COMMAND) { + /* Login Command */ + int digestsActive = 0; + { + gint b = tvb_get_guint8(tvb, offset + 1); + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08) { + if((b & CSG_MASK) >= ISCSI_CSG_OPERATIONAL_NEGOTIATION) + digestsActive = 1; + } +#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); +#endif + + proto_tree_add_boolean(ti, hf_iscsi_Login_T, tvb, offset + 1, 1, b); + if(iscsi_protocol_version >= ISCSI_PROTOCOL_DRAFT13) { + proto_tree_add_boolean(ti, hf_iscsi_Login_C, tvb, offset + 1, 1, b); + } + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08) { + proto_tree_add_boolean(ti, hf_iscsi_Login_X, tvb, offset + 1, 1, b); + } + proto_tree_add_item(ti, hf_iscsi_Login_CSG, tvb, offset + 1, 1, FALSE); + + /* NSG is undefined unless T is set */ + if(b&0x80){ + proto_tree_add_item(ti, hf_iscsi_Login_NSG, tvb, offset + 1, 1, FALSE); + } + } + proto_tree_add_item(ti, hf_iscsi_VersionMax, tvb, offset + 2, 1, FALSE); + proto_tree_add_item(ti, hf_iscsi_VersionMin, tvb, offset + 3, 1, FALSE); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08) { + proto_tree_add_item(ti, hf_iscsi_CID, tvb, offset + 8, 2, FALSE); + proto_tree_add_item(ti, hf_iscsi_ISID8, tvb, offset + 12, 2, FALSE); + } + else { + proto_item *tf = proto_tree_add_item(ti, hf_iscsi_ISID, tvb, offset + 8, 6, FALSE); + proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_ISID); + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(tt, hf_iscsi_ISID_Type, tvb, offset + 8, 1, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_NamingAuthority, tvb, offset + 9, 3, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_Qualifier, tvb, offset + 12, 2, FALSE); + } + else { + proto_tree_add_item(tt, hf_iscsi_ISID_t, tvb, offset + 8, 1, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_a, tvb, offset + 8, 1, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_b, tvb, offset + 9, 2, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_c, tvb, offset + 11, 1, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_d, tvb, offset + 12, 2, FALSE); + } + } + if(iscsi_protocol_version < ISCSI_PROTOCOL_DRAFT12) { + proto_tree_add_item(ti, hf_iscsi_TSID, tvb, offset + 14, 2, FALSE); + } + else { + proto_tree_add_item(ti, hf_iscsi_TSIH, tvb, offset + 14, 2, FALSE); + } + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT08) { + proto_tree_add_item(ti, hf_iscsi_CID, tvb, offset + 20, 2, 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); + 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 = 0; + { + gint b = tvb_get_guint8(tvb, offset + 1); + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08) { + if((b & CSG_MASK) >= ISCSI_CSG_OPERATIONAL_NEGOTIATION) + digestsActive = 1; + } +#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); +#endif + + proto_tree_add_boolean(ti, hf_iscsi_Login_T, tvb, offset + 1, 1, b); + if(iscsi_protocol_version >= ISCSI_PROTOCOL_DRAFT13) { + proto_tree_add_boolean(ti, hf_iscsi_Login_C, tvb, offset + 1, 1, b); + } + proto_tree_add_item(ti, hf_iscsi_Login_CSG, tvb, offset + 1, 1, FALSE); + /* NSG is undefined unless T is set */ + if(b&0x80){ + proto_tree_add_item(ti, hf_iscsi_Login_NSG, tvb, offset + 1, 1, FALSE); + } + } + + proto_tree_add_item(ti, hf_iscsi_VersionMax, tvb, offset + 2, 1, FALSE); + proto_tree_add_item(ti, hf_iscsi_VersionActive, tvb, offset + 3, 1, FALSE); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08) { + proto_tree_add_item(ti, hf_iscsi_ISID8, tvb, offset + 12, 2, FALSE); + } + else { + proto_item *tf = proto_tree_add_item(ti, hf_iscsi_ISID, tvb, offset + 8, 6, FALSE); + proto_tree *tt = proto_item_add_subtree(tf, ett_iscsi_ISID); + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(tt, hf_iscsi_ISID_Type, tvb, offset + 8, 1, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_NamingAuthority, tvb, offset + 9, 3, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_Qualifier, tvb, offset + 12, 2, FALSE); + } + else { + proto_tree_add_item(tt, hf_iscsi_ISID_t, tvb, offset + 8, 1, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_a, tvb, offset + 8, 1, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_b, tvb, offset + 9, 2, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_c, tvb, offset + 11, 1, FALSE); + proto_tree_add_item(tt, hf_iscsi_ISID_d, tvb, offset + 12, 2, FALSE); + } + } + if(iscsi_protocol_version < ISCSI_PROTOCOL_DRAFT12) { + proto_tree_add_item(ti, hf_iscsi_TSID, tvb, offset + 14, 2, FALSE); + } + else { + proto_tree_add_item(ti, hf_iscsi_TSIH, tvb, offset + 14, 2, FALSE); + } + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + 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); + proto_tree_add_item(ti, hf_iscsi_Login_Status, tvb, offset + 36, 2, FALSE); + 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 */ + { + gint b = tvb_get_guint8(tvb, offset + 1); + 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); + + proto_tree_add_boolean(tt, hf_iscsi_Text_F, tvb, offset + 1, 1, b); + if(iscsi_protocol_version >= ISCSI_PROTOCOL_DRAFT13) { + proto_tree_add_boolean(tt, hf_iscsi_Text_C, tvb, offset + 1, 1, b); + } + } + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + } + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + 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 = 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 */ + { + gint b = tvb_get_guint8(tvb, offset + 1); + 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); + + proto_tree_add_boolean(tt, hf_iscsi_Text_F, tvb, offset + 1, 1, b); + if(iscsi_protocol_version >= ISCSI_PROTOCOL_DRAFT13) { + proto_tree_add_boolean(tt, hf_iscsi_Text_C, tvb, offset + 1, 1, b); + } + } + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + } + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_TargetTransferTag, tvb, offset + 20, 4, FALSE); + 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 = 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) */ + { + gint b = tvb_get_guint8(tvb, offset + 1); + 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); + + proto_tree_add_boolean(tt, hf_iscsi_SCSIData_F, tvb, offset + 1, 1, b); + } + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_TargetTransferTag, tvb, offset + 20, 4, FALSE); + 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 = handleHeaderDigest(ti, tvb, offset, 48); + /* do not update offset here because the data segment is + * dissected below */ + handleDataDigest(ti, tvb, offset, paddedDataSegmentLength); + } else if(opcode == ISCSI_OPCODE_SCSI_DATA_IN) { + /* SCSI Data In (read) */ + { + gint b = tvb_get_guint8(tvb, offset + 1); + 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); + + if(b&ISCSI_SCSI_DATA_FLAG_S){ + S_bit=TRUE; + } + proto_tree_add_boolean(tt, hf_iscsi_SCSIData_F, tvb, offset + 1, 1, b); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT08) { + proto_tree_add_boolean(tt, hf_iscsi_SCSIData_A, tvb, offset + 1, 1, b); + } + proto_tree_add_boolean(tt, hf_iscsi_SCSIData_O, tvb, offset + 1, 1, b); + proto_tree_add_boolean(tt, hf_iscsi_SCSIData_U, tvb, offset + 1, 1, b); + proto_tree_add_boolean(tt, hf_iscsi_SCSIData_S, tvb, offset + 1, 1, b); + } + proto_tree_add_item(ti, hf_iscsi_SCSIResponse_Status, tvb, offset + 3, 1, FALSE); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + } + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + if(iscsi_protocol_version <= ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_SCSIData_ResidualCount, tvb, offset + 20, 4, FALSE); + } + else { + proto_tree_add_item(ti, hf_iscsi_TargetTransferTag, tvb, offset + 20, 4, FALSE); + } + 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); + 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); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_SCSIData_ResidualCount, tvb, offset + 44, 4, FALSE); + } + offset = handleHeaderDigest(ti, tvb, offset, 48); + /* do not update offset here because the data segment is + * dissected below */ + handleDataDigest(ti, tvb, offset, paddedDataSegmentLength); + } else if(opcode == ISCSI_OPCODE_LOGOUT_COMMAND) { + /* Logout Command */ + if(iscsi_protocol_version >= ISCSI_PROTOCOL_DRAFT13) { + proto_tree_add_item(ti, hf_iscsi_Logout_Reason, tvb, offset + 1, 1, FALSE); + } + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + } + if(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08) { + proto_tree_add_item(ti, hf_iscsi_CID, tvb, offset + 8, 2, FALSE); + 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); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT08) { + proto_tree_add_item(ti, hf_iscsi_CID, tvb, offset + 20, 2, FALSE); + if(iscsi_protocol_version < ISCSI_PROTOCOL_DRAFT13) { + proto_tree_add_item(ti, hf_iscsi_Logout_Reason, tvb, offset + 23, 1, 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 = handleHeaderDigest(ti, tvb, offset, 48); + } else if(opcode == ISCSI_OPCODE_LOGOUT_RESPONSE) { + /* Logout Response */ + proto_tree_add_item(ti, hf_iscsi_Logout_Response, tvb, offset + 2, 1, FALSE); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + } + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + 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); + 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 = handleHeaderDigest(ti, tvb, offset, 48); + } else if(opcode == ISCSI_OPCODE_SNACK_REQUEST) { + /* SNACK Request */ + { + gint b = tvb_get_guint8(tvb, offset + 1); +#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); +#endif + + proto_tree_add_item(ti, hf_iscsi_snack_type, tvb, offset + 1, 1, b); + } + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + } + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + if(iscsi_protocol_version <= ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_BegRun, tvb, offset + 20, 4, FALSE); + 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); + } + else { + proto_tree_add_item(ti, hf_iscsi_TargetTransferTag, tvb, offset + 20, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_ExpStatSN, tvb, offset + 28, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_BegRun, tvb, offset + 40, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_RunLength, tvb, offset + 44, 4, FALSE); + } + offset = handleHeaderDigest(ti, tvb, offset, 48); + } else if(opcode == ISCSI_OPCODE_R2T) { + /* R2T */ + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + } + proto_tree_add_item(ti, hf_iscsi_InitiatorTaskTag, tvb, offset + 16, 4, FALSE); + proto_tree_add_item(ti, hf_iscsi_TargetTransferTag, tvb, offset + 20, 4, FALSE); + 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); + 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 = handleHeaderDigest(ti, tvb, offset, 48); + } else if(opcode == ISCSI_OPCODE_ASYNC_MESSAGE) { + /* Asynchronous Message */ + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + proto_tree_add_item(ti, hf_iscsi_LUN, tvb, offset + 8, 8, FALSE); + 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); + proto_tree_add_item(ti, hf_iscsi_AsyncEvent, tvb, offset + 36, 1, FALSE); + proto_tree_add_item(ti, hf_iscsi_EventVendorCode, tvb, offset + 37, 1, FALSE); + 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 = handleHeaderDigest(ti, tvb, offset, 48); + offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_async_message_data); + } else if(opcode == ISCSI_OPCODE_REJECT) { + /* Reject */ + proto_tree_add_item(ti, hf_iscsi_Reject_Reason, tvb, offset + 2, 1, FALSE); + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + 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); + proto_tree_add_item(ti, hf_iscsi_DataSN, tvb, offset + 36, 4, FALSE); + offset = handleHeaderDigest(ti, tvb, offset, 48); + offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_error_pdu_data); + } else if(opcode == ISCSI_OPCODE_VENDOR_SPECIFIC_I0 || + opcode == ISCSI_OPCODE_VENDOR_SPECIFIC_I1 || + opcode == ISCSI_OPCODE_VENDOR_SPECIFIC_I2 || + opcode == ISCSI_OPCODE_VENDOR_SPECIFIC_T0 || + opcode == ISCSI_OPCODE_VENDOR_SPECIFIC_T1 || + opcode == ISCSI_OPCODE_VENDOR_SPECIFIC_T2) { + /* Vendor specific opcodes */ + if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) { + proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE); + } + proto_tree_add_uint(ti, hf_iscsi_DataSegmentLength, tvb, offset + 5, 3, data_segment_len); + offset = handleHeaderDigest(ti, tvb, offset, 48); + offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_vendor_specific_data); + } + + + + /* handle request/response matching */ + if (cdata){ + switch(opcode){ + case ISCSI_OPCODE_SCSI_RESPONSE: + if (cdata->request_frame){ + nstime_t delta_time; + proto_tree_add_uint(ti, hf_iscsi_request_frame, tvb, 0, 0, cdata->request_frame); + delta_time.secs = pinfo->fd->abs_secs - cdata->req_time.secs; + delta_time.nsecs = pinfo->fd->abs_usecs*1000 - cdata->req_time.nsecs; + if (delta_time.nsecs<0){ + delta_time.nsecs+=1000000000; + delta_time.secs--; + } + proto_tree_add_time(ti, hf_iscsi_time, tvb, 0, 0, &delta_time); + + } + if (cdata->data_in_frame) + proto_tree_add_uint(ti, hf_iscsi_data_in_frame, tvb, 0, 0, cdata->data_in_frame); + if (cdata->data_out_frame) + proto_tree_add_uint(ti, hf_iscsi_data_out_frame, tvb, 0, 0, cdata->data_out_frame); + break; + case ISCSI_OPCODE_SCSI_DATA_IN: + /* if we have phase collaps then we might have the + response embedded in the last DataIn segment */ + if(!S_bit){ + if (cdata->request_frame) + proto_tree_add_uint(ti, hf_iscsi_request_frame, tvb, 0, 0, cdata->request_frame); + if (cdata->response_frame) + proto_tree_add_uint(ti, hf_iscsi_response_frame, tvb, 0, 0, cdata->response_frame); + } else { + if (cdata->request_frame){ + nstime_t delta_time; + proto_tree_add_uint(ti, hf_iscsi_request_frame, tvb, 0, 0, cdata->request_frame); + delta_time.secs = pinfo->fd->abs_secs - cdata->req_time.secs; + delta_time.nsecs = pinfo->fd->abs_usecs*1000 - cdata->req_time.nsecs; + if (delta_time.nsecs<0){ + delta_time.nsecs+=1000000000; + delta_time.secs--; + } + proto_tree_add_time(ti, hf_iscsi_time, tvb, 0, 0, &delta_time); + + } + } + if (cdata->data_out_frame) + proto_tree_add_uint(ti, hf_iscsi_data_out_frame, tvb, 0, 0, cdata->data_out_frame); + break; + case ISCSI_OPCODE_SCSI_DATA_OUT: + if (cdata->request_frame) + proto_tree_add_uint(ti, hf_iscsi_request_frame, tvb, 0, 0, cdata->request_frame); + if (cdata->data_in_frame) + proto_tree_add_uint(ti, hf_iscsi_data_in_frame, tvb, 0, 0, cdata->data_in_frame); + if (cdata->response_frame) + proto_tree_add_uint(ti, hf_iscsi_response_frame, tvb, 0, 0, cdata->response_frame); + break; + case ISCSI_OPCODE_SCSI_COMMAND: + if (cdata->data_in_frame) + proto_tree_add_uint(ti, hf_iscsi_data_in_frame, tvb, 0, 0, cdata->data_in_frame); + if (cdata->data_out_frame) + proto_tree_add_uint(ti, hf_iscsi_data_out_frame, tvb, 0, 0, cdata->data_out_frame); + if (cdata->response_frame) + proto_tree_add_uint(ti, hf_iscsi_response_frame, tvb, 0, 0, cdata->response_frame); + break; + } + } + + + + proto_item_set_len(ti, offset - original_offset); + + if((opcode & ((iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08)? + ~(X_BIT | I_BIT) : + ~I_BIT)) == ISCSI_OPCODE_SCSI_COMMAND) { + /* SCSI Command */ + dissect_scsi_cdb (tvb, pinfo, tree, cdb_offset, 16, SCSI_DEV_UNKNOWN); + } + else if (opcode == ISCSI_OPCODE_SCSI_RESPONSE) { + if (scsi_status == 0x2) { + /* A SCSI response with Check Condition contains sense data */ + /* offset is setup correctly by the iscsi code for response above */ + if((end_offset - offset) >= 2) { + int senseLen = tvb_get_ntohs(tvb, offset); + if(ti != NULL) + proto_tree_add_item(ti, hf_iscsi_SenseLength, tvb, offset, 2, FALSE); + offset += 2; + if(senseLen > 0) + dissect_scsi_snsinfo (tvb, pinfo, tree, offset, + iscsi_min (senseLen, + end_offset-offset)); + } + } + else { + dissect_scsi_rsp (tvb, pinfo, tree); + } + } + else if ((opcode == ISCSI_OPCODE_SCSI_DATA_IN) || + (opcode == ISCSI_OPCODE_SCSI_DATA_OUT)) { + /* offset is setup correctly by the iscsi code for response above */ + dissect_scsi_payload (tvb, pinfo, tree, offset, FALSE, + iscsi_min (data_segment_len, end_offset-offset)); + } +} + +static gboolean +dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean check_port) { + /* 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 the + * minimum amount of information we need */ + if (available_bytes < 48 && (!iscsi_desegment || available_bytes < 8)) { + /* no, so give up */ + return FALSE; + } + + /* 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; + + /* mask out any extra bits in the opcode byte */ + opcode &= OPCODE_MASK; + + opcode_str = match_strval(opcode, iscsi_opcodes); + if(opcode == ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION || + opcode == ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION_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; + + if(opcode_str == NULL) { + badPdu = TRUE; + } + else if(check_port && 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(demand_good_f_bit && + !(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_TASK_MANAGEMENT_FUNCTION_RESPONSE || + opcode == ISCSI_OPCODE_R2T || + opcode == ISCSI_OPCODE_ASYNC_MESSAGE || + opcode == ISCSI_OPCODE_SNACK_REQUEST || + opcode == ISCSI_OPCODE_REJECT)) { + badPdu = TRUE; + } else if(opcode==ISCSI_OPCODE_NOP_OUT) { + /* TransferTag for NOP-Out should either be -1 or + the tag value we want for a response. + Assume 0 means we are just inside a big all zero + datablock. + */ + if(tvb_get_ntohl(tvb, offset+20)==0){ + 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(iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08) { + if((secondPduByte & CSG_MASK) < ISCSI_CSG_OPERATIONAL_NEGOTIATION) { + /* digests are not yet turned on */ + digestsActive = 0; + } + } + else { + 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; + } + } + + /* This is to help TCP keep track of PDU boundaries + and allows it to find PDUs that are not aligned to + the start of a TCP segments. + Since it also allows TCP to know what is in the middle + of a large PDU, it reduces the probability of a segment + in the middle of a large PDU transfer being misdissected as + a PDU. + */ + if(!pinfo->fd->flags.visited){ + if(pduLen>(guint32)tvb_reported_length_remaining(tvb, offset)){ + pinfo->want_pdu_tracking=2; + pinfo->bytes_until_next_pdu=pduLen-tvb_reported_length_remaining(tvb, offset); + } + } + + if(check_col(pinfo->cinfo, COL_INFO)) { + if(iSCSIPdusDissected == 0) + col_set_str(pinfo->cinfo, COL_INFO, ""); + else + col_append_str(pinfo->cinfo, COL_INFO, ", "); + } + + dissect_iscsi_pdu(tvb, pinfo, tree, offset, opcode, opcode_str, data_segment_len); + if(pduLen > available_bytes) + pduLen = available_bytes; + offset += pduLen; + available_bytes -= pduLen; + ++iSCSIPdusDissected; + } + } + + return iSCSIPdusDissected > 0; +} + +/* This is called for those sessions where we have explicitely said + this to be iSCSI using "Decode As..." + In this case we will not check the port number for sanity and just + do as the user said. +*/ +static void +dissect_iscsi_handle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + dissect_iscsi(tvb, pinfo, tree, FALSE); +} + +/* This is called through the heuristic handler. + In this case we also want to check that the port matches the preference + setting for iSCSI in order to reduce the number of + false positives. +*/ +static gboolean +dissect_iscsi_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + return dissect_iscsi(tvb, pinfo, tree, TRUE); +} + + +/* 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. +*/ + +void +proto_register_iscsi(void) +{ + + /* Setup list of header fields See Section 1.6.1 for details*/ + static hf_register_info hf[] = { + { &hf_iscsi_request_frame, + { "Request in", "iscsi.request_frame", + FT_FRAMENUM, BASE_NONE, NULL, 0, + "The request to this transaction is in this frame", HFILL }}, + + { &hf_iscsi_time, + { "Time from request", "iscsi.time", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0, + "Time between the Command and the Response", HFILL }}, + + { &hf_iscsi_data_in_frame, + { "Data In in", "iscsi.data_in_frame", + FT_FRAMENUM, BASE_NONE, NULL, 0, + "The Data In for this transaction is in this frame", HFILL }}, + + { &hf_iscsi_data_out_frame, + { "Data Out in", "iscsi.data_out_frame", + FT_FRAMENUM, BASE_NONE, NULL, 0, + "The Data Out for this transaction is in this frame", HFILL }}, + + { &hf_iscsi_response_frame, + { "Response in", "iscsi.response_frame", + FT_FRAMENUM, BASE_NONE, NULL, 0, + "The response to this transaction is in this frame", HFILL }}, + + { &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, + "Padding to 4 byte boundary", HFILL } + }, + { &hf_iscsi_ping_data, + { "PingData", "iscsi.pingdata", + FT_BYTES, BASE_HEX, NULL, 0, + "Ping Data", HFILL } + }, + { &hf_iscsi_immediate_data, + { "ImmediateData", "iscsi.immediatedata", + FT_BYTES, BASE_HEX, NULL, 0, + "Immediate Data", HFILL } + }, + { &hf_iscsi_write_data, + { "WriteData", "iscsi.writedata", + FT_BYTES, BASE_HEX, NULL, 0, + "Write Data", HFILL } + }, + { &hf_iscsi_read_data, + { "ReadData", "iscsi.readdata", + FT_BYTES, BASE_HEX, NULL, 0, + "Read Data", HFILL } + }, + { &hf_iscsi_error_pdu_data, + { "ErrorPDUData", "iscsi.errorpdudata", + FT_BYTES, BASE_HEX, NULL, 0, + "Error PDU Data", HFILL } + }, + { &hf_iscsi_async_message_data, + { "AsyncMessageData", "iscsi.asyncmessagedata", + FT_BYTES, BASE_HEX, NULL, 0, + "Async Message Data", HFILL } + }, + { &hf_iscsi_vendor_specific_data, + { "VendorSpecificData", "iscsi.vendorspecificdata", + FT_BYTES, BASE_HEX, NULL, 0, + "Vendor Specific Data", HFILL } + }, + { &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, + "Opcode", HFILL } + }, +/* #ifdef DRAFT08 */ + { &hf_iscsi_X, + { "X", "iscsi.X", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_X), 0x80, + "Command Retry", HFILL } + }, +/* #endif */ + { &hf_iscsi_I, + { "I", "iscsi.I", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_I), 0x40, + "Immediate delivery", HFILL } + }, + { &hf_iscsi_Flags, + { "Flags", "iscsi.flags", + FT_UINT8, BASE_HEX, NULL, 0, + "Opcode specific flags", HFILL } + }, + { &hf_iscsi_SCSICommand_F, + { "F", "iscsi.scsicommand.F", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_F), 0x80, + "PDU completes command", HFILL } + }, + { &hf_iscsi_SCSICommand_R, + { "R", "iscsi.scsicommand.R", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_R), 0x40, + "Command reads from SCSI target", HFILL } + }, + { &hf_iscsi_SCSICommand_W, + { "W", "iscsi.scsicommand.W", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_W), 0x20, + "Command writes to SCSI target", HFILL } + }, + { &hf_iscsi_SCSICommand_Attr, + { "Attr", "iscsi.scsicommand.attr", + FT_UINT8, BASE_HEX, VALS(iscsi_scsicommand_taskattrs), 0x07, + "SCSI task attributes", HFILL } + }, + { &hf_iscsi_SCSICommand_CRN, + { "CRN", "iscsi.scsicommand.crn", + FT_UINT8, BASE_HEX, NULL, 0, + "SCSI command reference number", HFILL } + }, + { &hf_iscsi_SCSICommand_AddCDB, + { "AddCDB", "iscsi.scsicommand.addcdb", + FT_UINT8, BASE_HEX, NULL, 0, + "Additional CDB length (in 4 byte units)", HFILL } + }, + { &hf_iscsi_DataSegmentLength, + { "DataSegmentLength", "iscsi.datasegmentlength", + FT_UINT32, BASE_HEX, NULL, 0, + "Data segment length (bytes)", HFILL } + }, + { &hf_iscsi_TotalAHSLength, + { "TotalAHSLength", "iscsi.totalahslength", + FT_UINT8, BASE_HEX, NULL, 0, + "Total additional header segment length (4 byte words)", HFILL } + }, + { &hf_iscsi_LUN, + { "LUN", "iscsi.lun", + FT_BYTES, BASE_HEX, NULL, 0, + "Logical Unit Number", HFILL } + }, + { &hf_iscsi_InitiatorTaskTag, + { "InitiatorTaskTag", "iscsi.initiatortasktag", + FT_UINT32, BASE_HEX, NULL, 0, + "Initiator's task tag", HFILL } + }, + { &hf_iscsi_ExpectedDataTransferLength, + { "ExpectedDataTransferLength", "iscsi.scsicommand.expecteddatatransferlength", + FT_UINT32, BASE_HEX, NULL, 0, + "Expected length of data transfer", HFILL } + }, + { &hf_iscsi_CmdSN, + { "CmdSN", "iscsi.cmdsn", + FT_UINT32, BASE_HEX, NULL, 0, + "Sequence number for this command", HFILL } + }, + { &hf_iscsi_ExpStatSN, + { "ExpStatSN", "iscsi.expstatsn", + FT_UINT32, BASE_HEX, NULL, 0, + "Next expected status sequence number", HFILL } + }, + { &hf_iscsi_SCSIResponse_ResidualCount, + { "ResidualCount", "iscsi.scsiresponse.residualcount", + FT_UINT32, BASE_HEX, NULL, 0, + "Residual count", HFILL } + }, + { &hf_iscsi_StatSN, + { "StatSN", "iscsi.statsn", + FT_UINT32, BASE_HEX, NULL, 0, + "Status sequence number", HFILL } + }, + { &hf_iscsi_ExpCmdSN, + { "ExpCmdSN", "iscsi.expcmdsn", + FT_UINT32, BASE_HEX, NULL, 0, + "Next expected command sequence number", HFILL } + }, + { &hf_iscsi_MaxCmdSN, + { "MaxCmdSN", "iscsi.maxcmdsn", + FT_UINT32, BASE_HEX, NULL, 0, + "Maximum acceptable command sequence number", HFILL } + }, + { &hf_iscsi_SCSIResponse_o, + { "o", "iscsi.scsiresponse.o", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_o), 0x10, + "Bi-directional read residual overflow", HFILL } + }, + { &hf_iscsi_SCSIResponse_u, + { "u", "iscsi.scsiresponse.u", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_u), 0x08, + "Bi-directional read residual underflow", HFILL } + }, + { &hf_iscsi_SCSIResponse_O, + { "O", "iscsi.scsiresponse.O", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_O), 0x04, + "Residual overflow", HFILL } + }, + { &hf_iscsi_SCSIResponse_U, + { "U", "iscsi.scsiresponse.U", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_U), 0x02, + "Residual underflow", HFILL } + }, + { &hf_iscsi_SCSIResponse_Status, + { "Status", "iscsi.scsiresponse.status", + FT_UINT8, BASE_HEX, VALS(scsi_status_val), 0, + "SCSI command status value", HFILL } + }, + { &hf_iscsi_SCSIResponse_Response, + { "Response", "iscsi.scsiresponse.response", + FT_UINT8, BASE_HEX, VALS(iscsi_scsi_responses), 0, + "SCSI command response value", HFILL } + }, + { &hf_iscsi_SCSIResponse_BidiReadResidualCount, + { "BidiReadResidualCount", "iscsi.scsiresponse.bidireadresidualcount", + FT_UINT32, BASE_HEX, NULL, 0, + "Bi-directional read residual count", HFILL } + }, + { &hf_iscsi_SenseLength, + { "SenseLength", "iscsi.scsiresponse.senselength", + FT_UINT16, BASE_HEX, NULL, 0, + "Sense data length", HFILL } + }, + { &hf_iscsi_SCSIData_F, + { "F", "iscsi.scsidata.F", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_F), ISCSI_SCSI_DATA_FLAG_F, + "Final PDU", HFILL } + }, + { &hf_iscsi_SCSIData_A, + { "A", "iscsi.scsidata.A", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_A), ISCSI_SCSI_DATA_FLAG_A, + "Acknowledge Requested", HFILL } + }, + { &hf_iscsi_SCSIData_S, + { "S", "iscsi.scsidata.S", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_S), ISCSI_SCSI_DATA_FLAG_S, + "PDU Contains SCSI command status", HFILL } + }, + { &hf_iscsi_SCSIData_U, + { "U", "iscsi.scsidata.U", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_U), ISCSI_SCSI_DATA_FLAG_U, + "Residual underflow", HFILL } + }, + { &hf_iscsi_SCSIData_O, + { "O", "iscsi.scsidata.O", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_O), ISCSI_SCSI_DATA_FLAG_O, + "Residual overflow", HFILL } + }, + { &hf_iscsi_TargetTransferTag, + { "TargetTransferTag", "iscsi.targettransfertag", + FT_UINT32, BASE_HEX, NULL, 0, + "Target transfer tag", HFILL } + }, + { &hf_iscsi_BufferOffset, + { "BufferOffset", "iscsi.bufferOffset", + FT_UINT32, BASE_HEX, NULL, 0, + "Buffer offset", HFILL } + }, + { &hf_iscsi_SCSIData_ResidualCount, + { "ResidualCount", "iscsi.scsidata.readresidualcount", + FT_UINT32, BASE_HEX, NULL, 0, + "Residual count", HFILL } + }, + { &hf_iscsi_DataSN, + { "DataSN", "iscsi.datasn", + FT_UINT32, BASE_HEX, NULL, 0, + "Data sequence number", HFILL } + }, + { &hf_iscsi_VersionMax, + { "VersionMax", "iscsi.versionmax", + FT_UINT8, BASE_HEX, NULL, 0, + "Maximum supported protocol version", HFILL } + }, + { &hf_iscsi_VersionMin, + { "VersionMin", "iscsi.versionmin", + FT_UINT8, BASE_HEX, NULL, 0, + "Minimum supported protocol version", HFILL } + }, + { &hf_iscsi_VersionActive, + { "VersionActive", "iscsi.versionactive", + FT_UINT8, BASE_HEX, NULL, 0, + "Negotiated protocol version", HFILL } + }, + { &hf_iscsi_CID, + { "CID", "iscsi.cid", + FT_UINT16, BASE_HEX, NULL, 0, + "Connection identifier", HFILL } + }, +/* #ifdef DRAFT08 */ + { &hf_iscsi_ISID8, + { "ISID", "iscsi.isid", + FT_UINT16, BASE_HEX, NULL, 0, + "Initiator part of session identifier", HFILL } + }, +/* #else */ + { &hf_iscsi_ISID, + { "ISID", "iscsi.isid", + FT_BYTES, BASE_HEX, NULL, 0, + "Initiator part of session identifier", HFILL } + }, +/* #ifdef DRAFT09 */ + { &hf_iscsi_ISID_Type, + { "ISID_Type", "iscsi.isid.type", + FT_UINT8, BASE_HEX, VALS(iscsi_isid_type), 0, + "Initiator part of session identifier - type", HFILL } + }, + { &hf_iscsi_ISID_NamingAuthority, + { "ISID_NamingAuthority", "iscsi.isid.namingauthority", + FT_UINT24, BASE_HEX, NULL, 0, + "Initiator part of session identifier - naming authority", HFILL } + }, + { &hf_iscsi_ISID_Qualifier, + { "ISID_Qualifier", "iscsi.isid.qualifier", + FT_UINT8, BASE_HEX, NULL, 0, + "Initiator part of session identifier - qualifier", HFILL } + }, +/* #else */ + { &hf_iscsi_ISID_t, + { "ISID_t", "iscsi.isid.t", + FT_UINT8, BASE_HEX, VALS(iscsi_isid_type), 0xc0, + "Initiator part of session identifier - t", HFILL } + }, + { &hf_iscsi_ISID_a, + { "ISID_a", "iscsi.isid.a", + FT_UINT8, BASE_HEX, NULL, 0x3f, + "Initiator part of session identifier - a", HFILL } + }, + { &hf_iscsi_ISID_b, + { "ISID_b", "iscsi.isid.b", + FT_UINT16, BASE_HEX, NULL, 0, + "Initiator part of session identifier - b", HFILL } + }, + { &hf_iscsi_ISID_c, + { "ISID_c", "iscsi.isid.c", + FT_UINT8, BASE_HEX, NULL, 0, + "Initiator part of session identifier - c", HFILL } + }, + { &hf_iscsi_ISID_d, + { "ISID_d", "iscsi.isid.d", + FT_UINT16, BASE_HEX, NULL, 0, + "Initiator part of session identifier - d", HFILL } + }, +/* #endif */ +/* #endif */ + { &hf_iscsi_TSID, + { "TSID", "iscsi.tsid", + FT_UINT16, BASE_HEX, NULL, 0, + "Target part of session identifier", HFILL } + }, + { &hf_iscsi_TSIH, + { "TSIH", "iscsi.tsih", + FT_UINT16, BASE_HEX, NULL, 0, + "Target session identifying handle", HFILL } + }, + { &hf_iscsi_InitStatSN, + { "InitStatSN", "iscsi.initstatsn", + FT_UINT32, BASE_HEX, NULL, 0, + "Initial status sequence number", HFILL } + }, + { &hf_iscsi_InitCmdSN, + { "InitCmdSN", "iscsi.initcmdsn", + FT_UINT32, BASE_HEX, NULL, 0, + "Initial command sequence number", HFILL } + }, + { &hf_iscsi_Login_T, + { "T", "iscsi.login.T", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_T), 0x80, + "Transit to next login stage", HFILL } + }, + { &hf_iscsi_Login_C, + { "C", "iscsi.login.C", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_C), 0x40, + "Text incomplete", HFILL } + }, +/* #ifdef DRAFT09 */ + { &hf_iscsi_Login_X, + { "X", "iscsi.login.X", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_login_X), 0x40, + "Restart Connection", HFILL } + }, +/* #endif */ + { &hf_iscsi_Login_CSG, + { "CSG", "iscsi.login.csg", + FT_UINT8, BASE_HEX, VALS(iscsi_login_stage), CSG_MASK, + "Current stage", HFILL } + }, + { &hf_iscsi_Login_NSG, + { "NSG", "iscsi.login.nsg", + FT_UINT8, BASE_HEX, VALS(iscsi_login_stage), NSG_MASK, + "Next stage", HFILL } + }, + { &hf_iscsi_Login_Status, + { "Status", "iscsi.login.status", + FT_UINT16, BASE_HEX, VALS(iscsi_login_status), 0, + "Status class and detail", HFILL } + }, + { &hf_iscsi_KeyValue, + { "KeyValue", "iscsi.keyvalue", + FT_STRING, 0, NULL, 0, + "Key/value pair", HFILL } + }, + { &hf_iscsi_Text_F, + { "F", "iscsi.text.F", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_F), 0x80, + "Final PDU in text sequence", HFILL } + }, + { &hf_iscsi_Text_C, + { "C", "iscsi.text.C", + FT_BOOLEAN, 8, TFS(&iscsi_meaning_C), 0x40, + "Text incomplete", HFILL } + }, + { &hf_iscsi_ExpDataSN, + { "ExpDataSN", "iscsi.expdatasn", + FT_UINT32, BASE_HEX, NULL, 0, + "Next expected data sequence number", HFILL } + }, + { &hf_iscsi_R2TSN, + { "R2TSN", "iscsi.r2tsn", + FT_UINT32, BASE_HEX, NULL, 0, + "R2T PDU Number", HFILL } + }, + { &hf_iscsi_TaskManagementFunction_Response, + { "Response", "iscsi.taskmanfun.response", + FT_UINT8, BASE_HEX, VALS(iscsi_task_management_responses), 0, + "Response", HFILL } + }, + { &hf_iscsi_TaskManagementFunction_ReferencedTaskTag, + { "ReferencedTaskTag", "iscsi.taskmanfun.referencedtasktag", + FT_UINT32, BASE_HEX, NULL, 0, + "Referenced task tag", HFILL } + }, + { &hf_iscsi_RefCmdSN, + { "RefCmdSN", "iscsi.refcmdsn", + FT_UINT32, BASE_HEX, NULL, 0, + "Command sequence number for command to be aborted", HFILL } + }, + { &hf_iscsi_TaskManagementFunction_Function, + { "Function", "iscsi.taskmanfun.function", + FT_UINT8, BASE_HEX, VALS(iscsi_task_management_functions), 0x7F, + "Requested task function", HFILL } + }, + { &hf_iscsi_Logout_Reason, + { "Reason", "iscsi.logout.reason", + FT_UINT8, BASE_HEX, VALS(iscsi_logout_reasons), 0x7F, + "Reason for logout", HFILL } + }, + { &hf_iscsi_Logout_Response, + { "Response", "iscsi.logout.response", + FT_UINT8, BASE_HEX, VALS(iscsi_logout_response), 0, + "Logout response", HFILL } + }, + { &hf_iscsi_Time2Wait, + { "Time2Wait", "iscsi.time2wait", + FT_UINT16, BASE_HEX, NULL, 0, + "Time2Wait", HFILL } + }, + { &hf_iscsi_Time2Retain, + { "Time2Retain", "iscsi.time2retain", + FT_UINT16, BASE_HEX, NULL, 0, + "Time2Retain", HFILL } + }, + { &hf_iscsi_DesiredDataLength, + { "DesiredDataLength", "iscsi.desireddatalength", + FT_UINT32, BASE_HEX, NULL, 0, + "Desired data length (bytes)", HFILL } + }, + { &hf_iscsi_AsyncEvent, + { "AsyncEvent", "iscsi.asyncevent", + FT_UINT8, BASE_HEX, VALS(iscsi_asyncevents), 0, + "Async event type", HFILL } + }, + { &hf_iscsi_EventVendorCode, + { "EventVendorCode", "iscsi.eventvendorcode", + FT_UINT8, BASE_HEX, NULL, 0, + "Event vendor code", HFILL } + }, + { &hf_iscsi_Parameter1, + { "Parameter1", "iscsi.parameter1", + FT_UINT16, BASE_HEX, NULL, 0, + "Parameter 1", HFILL } + }, + { &hf_iscsi_Parameter2, + { "Parameter2", "iscsi.parameter2", + FT_UINT16, BASE_HEX, NULL, 0, + "Parameter 2", HFILL } + }, + { &hf_iscsi_Parameter3, + { "Parameter3", "iscsi.parameter3", + FT_UINT16, BASE_HEX, NULL, 0, + "Parameter 3", HFILL } + }, + { &hf_iscsi_Reject_Reason, + { "Reason", "iscsi.reject.reason", + FT_UINT8, BASE_HEX, VALS(iscsi_reject_reasons), 0, + "Reason for command rejection", HFILL } + }, + { &hf_iscsi_snack_type, + { "S", "iscsi.snack.type", + FT_UINT8, BASE_DEC, VALS(iscsi_snack_types), 0x0f, + "Type of SNACK requested", HFILL } + }, + { &hf_iscsi_BegRun, + { "BegRun", "iscsi.snack.begrun", + FT_UINT32, BASE_HEX, NULL, 0, + "First missed DataSN or StatSN", HFILL } + }, + { &hf_iscsi_RunLength, + { "RunLength", "iscsi.snack.runlength", + FT_UINT32, BASE_HEX, NULL, 0, + "Number of additional missing status PDUs in this run", HFILL } + }, + }; + + /* Setup protocol subtree array */ + static gint *ett[] = { + &ett_iscsi, + &ett_iscsi_KeyValues, + &ett_iscsi_CDB, + &ett_iscsi_Flags, +/* #ifndef DRAFT08 */ + &ett_iscsi_ISID, +/* #endif */ + }; + + /* 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 */ + proto_register_field_array(proto_iscsi, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + register_init_routine (&iscsi_init_protocol); + + { + module_t *iscsi_module = prefs_register_protocol(proto_iscsi, NULL); + + prefs_register_enum_preference(iscsi_module, + "protocol_version", + "Protocol version", + "The iSCSI protocol version", + &iscsi_protocol_version, + iscsi_protocol_versions, + FALSE); + + 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", + "When enabled, packets that appear bogus are ignored", + &enable_bogosity_filter); + + prefs_register_bool_preference(iscsi_module, + "demand_good_f_bit", + "Ignore packets with bad F bit", + "Ignore packets that haven't set the F bit when they should have", + &demand_good_f_bit); + + prefs_register_uint_preference(iscsi_module, + "bogus_pdu_max_data_len", + "Bogus pdu max data length threshold", + "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, + "target_port", + "Target port", + "Port number of iSCSI target", + 10, + &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); + + /* Preference supported in older versions. + Register them as obsolete. */ + prefs_register_obsolete_preference(iscsi_module, + "version_03_compatible"); + prefs_register_obsolete_preference(iscsi_module, + "bogus_pdu_max_digest_padding"); + } +} + + +/* + * 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) +{ + dissector_handle_t iscsi_handle; + + heur_dissector_add("tcp", dissect_iscsi_heur, proto_iscsi); + + iscsi_handle = create_dissector_handle(dissect_iscsi_handle, proto_iscsi); + dissector_add_handle("tcp.port", iscsi_handle); +} |