#include <string.h> and/or #include <stdio.h> not needed.
[obnox/wireshark/wip.git] / epan / dissectors / packet-iscsi.c
index 5a3d224387672de7bbe2d836c07ba77885ac5774..738bb21a0fef191623a014f10e925e2bd9a2bb56 100644 (file)
@@ -1,3 +1,11 @@
+/* TODO for the cases where one just can not autodetect whether header digest
+   is used or not we might need a new preference
+   HeaderDigest :
+       Automatic (default)
+       None
+       CRC32
+*/
+
 /* packet-iscsi.c
  * Routines for iSCSI dissection
  * Copyright 2001, Eurologic and Mark Burton <markb@ordern.com>
@@ -5,8 +13,8 @@
  *
  * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
  * This program is free software; you can redistribute it and/or
@@ -28,7 +36,6 @@
 # include "config.h"
 #endif
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -38,7 +45,9 @@
 #include <epan/prefs.h>
 #include <epan/conversation.h>
 #include "packet-scsi.h"
-#include "epan/nstime.h"
+#include <epan/nstime.h>
+#include <epan/emem.h>
+#include <epan/crc32.h>
 
 /* the absolute values of these constants don't matter as long as
  * latter revisions of the protocol are assigned a larger number */
@@ -57,6 +66,14 @@ static enum_val_t iscsi_protocol_versions[] = {
     { NULL, NULL, 0 }
 };
 
+static const value_string ahs_type_vals[] = {
+    {1, "Extended CDB"},
+    {2, "Expected Bidirection Read Data Length"},
+    {0, NULL}
+};
+
+static dissector_handle_t iscsi_handle=NULL;
+
 static gint iscsi_protocol_version = ISCSI_PROTOCOL_DRAFT13;
 
 static gboolean iscsi_desegment = TRUE;
@@ -69,7 +86,7 @@ static int enableDataDigests = FALSE;
 
 static int dataDigestIsCRC32 = TRUE;
 
-static int dataDigestSize = 4;
+static guint dataDigestSize = 4;
 
 static guint iscsi_port = 3260;
 
@@ -81,13 +98,18 @@ 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_AHS_length = -1;
+static int hf_iscsi_AHS_type = -1;
+static int hf_iscsi_AHS_blob = -1;
+static int hf_iscsi_AHS_read_data_length = -1;
+static int hf_iscsi_AHS_extended_cdb = -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_async_event_data = -1;
 static int hf_iscsi_vendor_specific_data = -1;
 static int hf_iscsi_Opcode = -1;
 static int hf_iscsi_Flags = -1;
@@ -197,29 +219,12 @@ static gint ett_iscsi_ISID = -1;
 #define ISCSI_HEADER_DIGEST_AUTO       0
 #define ISCSI_HEADER_DIGEST_NONE       1
 #define ISCSI_HEADER_DIGEST_CRC32      2
-/* this structure contains session wide state for all iscsi sessions */
+/* this structure contains session wide state for a specific tcp conversation */
 typedef struct _iscsi_session_t {
-       guint32 conv_idx;       /* unique ID for conversation */
        guint32 header_digest;
+       emem_tree_t *itlq;      /* indexed by ITT */
+       emem_tree_t *itl;               /* indexed by LUN */
 } iscsi_session_t;
-static GHashTable *iscsi_session_table = NULL;
-static GMemChunk *iscsi_sessions = NULL;
-static gint
-iscsi_session_equal(gconstpointer v, gconstpointer w)
-{
-  const iscsi_session_t *v1 = (const iscsi_session_t *)v;
-  const iscsi_session_t *v2 = (const iscsi_session_t *)w;
-
-  return (v1->conv_idx == v2->conv_idx);
-}
-
-static guint
-iscsi_session_hash (gconstpointer v)
-{
-       const iscsi_session_t *key = (const iscsi_session_t *)v;
-
-       return key->conv_idx;
-}
 
 
 
@@ -411,6 +416,7 @@ static const value_string iscsi_task_management_functions[] = {
     {5, "Logical Unit Reset"},
     {6, "Target Warm Reset"},
     {7, "Target Cold Reset"},
+    {8, "Target Reassign"},
     {0, NULL},
 };
 
@@ -504,230 +510,14 @@ static const value_string iscsi_reject_reasons[] = {
     {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 
+/* 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;
+    itlq_nexus_t itlq;
 } 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_sessions) {
-        g_mem_chunk_destroy(iscsi_sessions);
-       iscsi_sessions=NULL;
-    }
-    if (iscsi_req_vals) {
-        g_mem_chunk_destroy(iscsi_req_vals);
-       iscsi_req_vals=NULL;
-    }
-    if (iscsi_req_unmatched) {
-        g_hash_table_destroy(iscsi_req_unmatched);
-       iscsi_req_unmatched=NULL;
-    }
-    if (iscsi_req_matched) {
-        g_hash_table_destroy(iscsi_req_matched);
-       iscsi_req_matched=NULL;
-    }
-    if (iscsi_session_table) {
-        g_hash_table_destroy(iscsi_session_table);
-       iscsi_session_table=NULL;
-    }
-
-    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_session_table = g_hash_table_new(iscsi_session_hash, iscsi_session_equal);
-    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);
-    iscsi_sessions = g_mem_chunk_new("iscsi_sessions",
-                                   sizeof(iscsi_session_t),
-                                   iscsi_init_count * sizeof(iscsi_session_t),
-                                   G_ALLOC_AND_FREE);
-}
 
 static int
 iscsi_min(int a, int b) {
@@ -756,17 +546,15 @@ handleHeaderDigest(iscsi_session_t *iscsi_session, proto_item *ti, tvbuff_t *tvb
     switch(iscsi_session->header_digest){
     case ISCSI_HEADER_DIGEST_CRC32:
        if(available_bytes >= (headerLen + 4)) {
-           guint32 crc = ~calculateCRC32(tvb_get_ptr(tvb, offset, headerLen), headerLen, CRC32C_PRELOAD);
+           guint32 crc = ~crc32c_calculate(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);
-printf("bad digest\n");
            }
        }
        return offset + headerLen + 4;
-        break;
     }
     return offset + headerLen;
 }
@@ -777,7 +565,7 @@ handleDataDigest(proto_item *ti, tvbuff_t *tvb, guint offset, int dataLen) {
     if(enableDataDigests) {
        if(dataDigestIsCRC32) {
            if(available_bytes >= (dataLen + 4)) {
-               guint32 crc = ~calculateCRC32(tvb_get_ptr(tvb, offset, dataLen), dataLen, CRC32C_PRELOAD);
+               guint32 crc = ~crc32c_calculate(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);
@@ -788,7 +576,7 @@ handleDataDigest(proto_item *ti, tvbuff_t *tvb, guint offset, int dataLen) {
            }
            return offset + dataLen + 4;
        }
-       if(available_bytes >= (dataLen + dataDigestSize)) {
+       if((unsigned)available_bytes >= (dataLen + dataDigestSize)) {
            proto_tree_add_item(ti, hf_iscsi_DataDigest, tvb, offset + dataLen, dataDigestSize, FALSE);
        }
        return offset + dataLen + dataDigestSize;
@@ -840,7 +628,7 @@ handleDataSegmentAsTextKeys(proto_item *ti, tvbuff_t *tvb, guint offset, guint d
 
 /* 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, iscsi_session_t *iscsi_session) {
+dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 opcode, const char *opcode_str, guint32 data_segment_len, iscsi_session_t *iscsi_session, conversation_t *conversation) {
 
     guint original_offset = offset;
     proto_tree *ti = NULL;
@@ -849,14 +637,41 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
     guint cdb_offset = offset + 32; /* offset of CDB from start of PDU */
     guint end_offset = offset + tvb_length_remaining(tvb, offset);
     iscsi_conv_data_t *cdata = NULL;
-    scsi_task_id_t task_key;
     int paddedDataSegmentLength = data_segment_len;
+    guint16 lun=0xffff;
+    guint immediate_data_length=0;
+    guint immediate_data_offset=0;
+    itl_nexus_t *itl=NULL;
+    guint ahs_cdb_length=0;
+    guint ahs_cdb_offset=0;
+    guint32 data_offset=0;
+
     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");
+    col_set_str(pinfo->cinfo, COL_PROTOCOL, "iSCSI");
+
+    /* XXX we need a way to handle replayed iscsi itt here */
+    cdata=(iscsi_conv_data_t *)se_tree_lookup32(iscsi_session->itlq, tvb_get_ntohl(tvb, offset+16));
+    if(!cdata){
+        cdata = se_alloc (sizeof(iscsi_conv_data_t));
+        cdata->itlq.lun=0xffff;
+        cdata->itlq.scsi_opcode=0xffff;
+       cdata->itlq.task_flags=0;
+       cdata->itlq.data_length=0;
+       cdata->itlq.bidir_data_length=0;
+        cdata->itlq.fc_time = pinfo->fd->abs_ts;
+        cdata->itlq.first_exchange_frame=0;
+        cdata->itlq.last_exchange_frame=0;
+        cdata->itlq.flags=0;
+        cdata->itlq.alloc_len=0;
+        cdata->itlq.extra_data=NULL;
+        cdata->data_in_frame=0;
+        cdata->data_out_frame=0;
+
+        se_tree_insert32(iscsi_session->itlq, tvb_get_ntohl(tvb, offset+16), cdata);
+    }
 
     if (opcode == ISCSI_OPCODE_SCSI_RESPONSE ||
        opcode == ISCSI_OPCODE_SCSI_DATA_IN) {
@@ -866,119 +681,63 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
     if ((opcode == ISCSI_OPCODE_SCSI_RESPONSE) ||
         (opcode == ISCSI_OPCODE_SCSI_DATA_IN) ||
         (opcode == ISCSI_OPCODE_SCSI_DATA_OUT)) {
-        if (!pinfo->fd->flags.visited){
-            iscsi_conv_data_t ckey;
-            ckey.conv_idx = iscsi_session->conv_idx;
-            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 = iscsi_session->conv_idx;
-            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;
+        /* first time we see this packet. check if we can find the request */
+        switch(opcode){
+        case ISCSI_OPCODE_SCSI_RESPONSE:
+            cdata->itlq.last_exchange_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->itlq.last_exchange_frame=pinfo->fd->num;
             }
-
-            /* 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;
+            cdata->data_in_frame=pinfo->fd->num;
+            break;
+        case ISCSI_OPCODE_SCSI_DATA_OUT:
+            cdata->data_out_frame=pinfo->fd->num;
+            break;
         }
 
     } else if (opcode == ISCSI_OPCODE_SCSI_COMMAND) {
-        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 = iscsi_session->conv_idx;
-            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 = iscsi_session->conv_idx;
-            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);
+        /*we need the LUN value for some of the commands so we can pass it
+          across to the SCSI dissector.
+          Not correct but simple  and probably accurate enough :
+          If bit 6 of first bit is 0   then just take second byte as the LUN
+          If bit 6 of first bit is 1, then take 6 bits from first byte
+          and all of second byte and pretend it is the lun value
+         people that care can add host specific dissection of vsa later.
+
+          We need to keep track of this on a per transaction basis since
+          for error recoverylevel 0 and when the A bit is clear in a
+          Data-In PDU, there will not be a LUN field in teh iscsi layer.
+        */
+        if(tvb_get_guint8(tvb, offset+8)&0x40){
+          /* volume set addressing */
+          lun=tvb_get_guint8(tvb,offset+8)&0x3f;
+          lun<<=8;
+          lun|=tvb_get_guint8(tvb,offset+9);
         } else {
-                iscsi_conv_data_t ckey;
-                ckey.conv_idx = iscsi_session->conv_idx;
-                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);
+          lun=tvb_get_guint8(tvb,offset+9);
         }
 
+        cdata->itlq.lun=lun;
+        cdata->itlq.first_exchange_frame=pinfo->fd->num;
 
-        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;
+        itl=(itl_nexus_t *)se_tree_lookup32(iscsi_session->itl, lun);
+        if(!itl){
+            itl=se_alloc(sizeof(itl_nexus_t));
+            itl->cmdset=0xff;
+            itl->conversation=conversation;
+            se_tree_insert32(iscsi_session->itl, lun, itl);
         }
+
     }
-    else {
-        pinfo->private_data = NULL;
+
+    if(!itl){
+        itl=(itl_nexus_t *)se_tree_lookup32(iscsi_session->itl, cdata->itlq.lun);
     }
 
+
     if (check_col(pinfo->cinfo, COL_INFO)) {
 
         if (opcode != ISCSI_OPCODE_SCSI_COMMAND) {
@@ -1059,7 +818,6 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
                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) {
@@ -1092,12 +850,19 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
            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);
+               if(b&0x40){
+                   cdata->itlq.task_flags|=SCSI_DATA_READ;
+               }
                proto_tree_add_boolean(tt, hf_iscsi_SCSICommand_W, tvb, offset + 1, 1, b);
+               if(b&0x20){
+                   cdata->itlq.task_flags|=SCSI_DATA_WRITE;
+               }
                proto_tree_add_uint(tt, hf_iscsi_SCSICommand_Attr, tvb, offset + 1, 1, b);
            }
            if(iscsi_protocol_version < ISCSI_PROTOCOL_DRAFT12) {
@@ -1108,16 +873,58 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
            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);
+           cdata->itlq.data_length=tvb_get_ntohl(tvb, offset+20);
            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);
+           if(ahsLen > 0) {
+               guint ahs_offset=offset+48;
+               guint16 ahs_length=0;
+               guint8 ahs_type=0;
+
+               while(ahs_offset<(offset+48+ahsLen)){
+
+                   ahs_length=tvb_get_ntohs(tvb, ahs_offset);
+                   proto_tree_add_item(ti, hf_iscsi_AHS_length, tvb, ahs_offset, 2, FALSE);
+                   ahs_offset+=2;
+
+                   ahs_type=tvb_get_guint8(tvb, ahs_offset);
+                   proto_tree_add_item(ti, hf_iscsi_AHS_type, tvb, ahs_offset, 1, FALSE);
+                   ahs_offset++;
+
+                   switch(ahs_type){
+                   case 0x01: /* extended CDB */
+                       /* additional cdb */
+                       ahs_cdb_offset=ahs_offset+1;
+                       ahs_cdb_length=ahs_length-1;
+                       proto_tree_add_item(ti, hf_iscsi_AHS_extended_cdb, tvb, ahs_cdb_offset, ahs_cdb_length, FALSE);
+                       ahs_offset+=ahs_length;
+                       break;
+                   case 0x02: /* bidirectional read data length */
+                       /* skip reserved byte */
+                       ahs_offset++;
+                       /* read data length */
+                       proto_tree_add_item(ti, hf_iscsi_AHS_read_data_length, tvb, ahs_offset, 4, FALSE);
+                       cdata->itlq.bidir_data_length=tvb_get_ntohl(tvb, ahs_offset);
+                       ahs_offset+=4;
+                       break;
+                   default:
+                       proto_tree_add_item(ti, hf_iscsi_AHS_blob, tvb, ahs_offset, ahs_length, FALSE);
+                       ahs_offset+=ahs_length;
+                   }
+
+                   /* strip off padding bytes */
+                   if(ahs_offset&0x0003){
+                       ahs_offset=(ahs_offset+3)&0xfffc;
+                   }
+
                }
-               offset = handleHeaderDigest(iscsi_session, ti, tvb, offset, 48 + ahsLen);
+
            }
+           offset = handleHeaderDigest(iscsi_session, ti, tvb, offset, 48 + ahsLen);
+
+            immediate_data_offset=offset;
            offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_immediate_data);
+           immediate_data_length=offset-immediate_data_offset;
     } else if(opcode == ISCSI_OPCODE_SCSI_RESPONSE) {
            /* SCSI Response */
            {
@@ -1393,6 +1200,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);
+           data_offset=tvb_get_ntohl(tvb, offset+40);
+
            offset = handleHeaderDigest(iscsi_session, ti, tvb, offset, 48);
            /* do not update offset here because the data segment is
             * dissected below */
@@ -1415,7 +1224,9 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
                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(S_bit){
+               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);
            }
@@ -1435,6 +1246,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);
+           data_offset=tvb_get_ntohl(tvb, offset+40);
+
            if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) {
                proto_tree_add_item(ti, hf_iscsi_SCSIData_ResidualCount, tvb, offset + 44, 4, FALSE);
            }
@@ -1526,10 +1339,13 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
            proto_tree_add_item(ti, hf_iscsi_DesiredDataLength, tvb, offset + 44, 4, FALSE);
            offset = handleHeaderDigest(iscsi_session, ti, tvb, offset, 48);
     } else if(opcode == ISCSI_OPCODE_ASYNC_MESSAGE) {
+           int dsl, snsl;
+
            /* Asynchronous Message */
            if(iscsi_protocol_version > ISCSI_PROTOCOL_DRAFT09) {
                proto_tree_add_item(ti, hf_iscsi_TotalAHSLength, tvb, offset + 4, 1, FALSE);
            }
+           dsl=tvb_get_ntoh24(tvb, offset+5);
            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);
@@ -1541,7 +1357,35 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
            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(iscsi_session, ti, tvb, offset, 48);
-           offset = handleDataSegment(ti, tvb, offset, data_segment_len, end_offset, hf_iscsi_async_message_data);
+
+           /* If we have a datasegment this contains scsi sense info followed
+            * by iscsi event data. (rfc3720 10.9.4)
+            */
+           if(dsl){
+               snsl=tvb_get_ntohs(tvb, offset);
+               offset+=2;
+               if(snsl){
+                   tvbuff_t *data_tvb;
+                   int tvb_len, tvb_rlen;
+
+                   tvb_len=tvb_length_remaining(tvb, offset);
+                   if(tvb_len>snsl)
+                       tvb_len=snsl;
+                   tvb_rlen=tvb_reported_length_remaining(tvb, offset);
+                   if(tvb_rlen>snsl)
+                       tvb_rlen=snsl;
+                   data_tvb=tvb_new_subset(tvb, offset, tvb_len, tvb_rlen);
+                   dissect_scsi_snsinfo (data_tvb, pinfo, tree, 0,
+                                         tvb_len,
+                                         &cdata->itlq, itl);
+
+                   offset+=snsl;
+               }
+               if((end_offset-offset)>0){
+                   proto_tree_add_item(ti, hf_iscsi_async_event_data, tvb, offset, end_offset-offset, FALSE);
+               }
+           }
+           offset=end_offset;
     } else if(opcode == ISCSI_OPCODE_REJECT) {
            /* Reject */
            proto_tree_add_item(ti, hf_iscsi_Reject_Reason, tvb, offset + 2, 1, FALSE);
@@ -1573,68 +1417,54 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
 
 
     /* 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);
-                
-                }
+    switch(opcode){
+    case ISCSI_OPCODE_SCSI_RESPONSE:
+        if (cdata->itlq.first_exchange_frame){
+            nstime_t delta_time;
+            proto_tree_add_uint(ti, hf_iscsi_request_frame, tvb, 0, 0, cdata->itlq.first_exchange_frame);
+            nstime_delta(&delta_time, &pinfo->fd->abs_ts, &cdata->itlq.fc_time);
+            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->itlq.first_exchange_frame)
+                proto_tree_add_uint(ti, hf_iscsi_request_frame, tvb, 0, 0, cdata->itlq.first_exchange_frame);
+            if (cdata->itlq.last_exchange_frame)
+                proto_tree_add_uint(ti, hf_iscsi_response_frame, tvb, 0, 0, cdata->itlq.last_exchange_frame);
+        } else {
+            if (cdata->itlq.first_exchange_frame){
+                 nstime_t delta_time;
+                 proto_tree_add_uint(ti, hf_iscsi_request_frame, tvb, 0, 0, cdata->itlq.first_exchange_frame);
+                nstime_delta(&delta_time, &pinfo->fd->abs_ts, &cdata->itlq.fc_time);
+                 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;
         }
+        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->itlq.first_exchange_frame)
+            proto_tree_add_uint(ti, hf_iscsi_request_frame, tvb, 0, 0, cdata->itlq.first_exchange_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->itlq.last_exchange_frame)
+            proto_tree_add_uint(ti, hf_iscsi_response_frame, tvb, 0, 0, cdata->itlq.last_exchange_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->itlq.last_exchange_frame)
+            proto_tree_add_uint(ti, hf_iscsi_response_frame, tvb, 0, 0, cdata->itlq.last_exchange_frame);
+        break;
     }
 
 
@@ -1644,8 +1474,61 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
     if((opcode & ((iscsi_protocol_version == ISCSI_PROTOCOL_DRAFT08)?
                  ~(X_BIT | I_BIT) :
                  ~I_BIT)) == ISCSI_OPCODE_SCSI_COMMAND) {
+       tvbuff_t *cdb_tvb, *data_tvb;
+       int tvb_len, tvb_rlen;
+       guint8 scsi_opcode;
+
         /* SCSI Command */
-        dissect_scsi_cdb (tvb, pinfo, tree, cdb_offset, 16, SCSI_DEV_UNKNOWN);
+       tvb_len=tvb_length_remaining(tvb, cdb_offset);
+       tvb_rlen=tvb_reported_length_remaining(tvb, cdb_offset);
+       scsi_opcode=tvb_get_guint8(tvb, cdb_offset);
+       if(ahs_cdb_length && ahs_cdb_length<1024){
+               char *cdb_buf;
+
+               /* We have a variable length CDB where bytes >16 is transported
+                * in the AHS.
+                */
+               cdb_buf=g_malloc(16+ahs_cdb_length);
+               /* the 16 first bytes of the cdb */
+               tvb_memcpy(tvb, cdb_buf, cdb_offset, 16);
+               /* the remainder of the cdb from the ahs */
+               tvb_memcpy(tvb, cdb_buf+16, ahs_cdb_offset, ahs_cdb_length);
+
+               cdb_tvb = tvb_new_child_real_data(tvb, cdb_buf,
+                                         ahs_cdb_length+16,
+                                         ahs_cdb_length+16);
+                tvb_set_free_cb(cdb_tvb, g_free);
+
+               add_new_data_source(pinfo, cdb_tvb, "CDB+AHS");
+       } else {
+               if(tvb_len>16){
+                   tvb_len=16;
+               }
+               if(tvb_rlen>16){
+                   tvb_rlen=16;
+               }
+               cdb_tvb=tvb_new_subset(tvb, cdb_offset, tvb_len, tvb_rlen);
+       }
+        dissect_scsi_cdb(cdb_tvb, pinfo, tree, SCSI_DEV_UNKNOWN, &cdata->itlq, itl);
+       /* we dont want the immediata below to overwrite our CDB info */
+       if (check_col(pinfo->cinfo, COL_INFO)) {
+           col_set_fence(pinfo->cinfo, COL_INFO);
+       }
+       /* where there any ImmediateData ? */
+       if(immediate_data_length){
+            /* Immediate Data TVB */
+           tvb_len=tvb_length_remaining(tvb, immediate_data_offset);
+           if(tvb_len>(int)immediate_data_length)
+               tvb_len=immediate_data_length;
+           tvb_rlen=tvb_reported_length_remaining(tvb, immediate_data_offset);
+           if(tvb_rlen>(int)immediate_data_length)
+               tvb_rlen=immediate_data_length;
+           data_tvb=tvb_new_subset(tvb, immediate_data_offset, tvb_len, tvb_rlen);
+            dissect_scsi_payload (data_tvb, pinfo, tree,
+                                 TRUE,
+                                 &cdata->itlq, itl,
+                                 0);
+       }
     }
     else if (opcode == ISCSI_OPCODE_SCSI_RESPONSE) {
         if (scsi_status == 0x2) {
@@ -1656,21 +1539,48 @@ dissect_iscsi_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint off
                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));
+               if(senseLen > 0){
+                   tvbuff_t *data_tvb;
+                   int tvb_len, tvb_rlen;
+
+                   tvb_len=tvb_length_remaining(tvb, offset);
+                   if(tvb_len>senseLen)
+                       tvb_len=senseLen;
+                   tvb_rlen=tvb_reported_length_remaining(tvb, offset);
+                   if(tvb_rlen>senseLen)
+                       tvb_rlen=senseLen;
+                   data_tvb=tvb_new_subset(tvb, offset, tvb_len, tvb_rlen);
+                   dissect_scsi_snsinfo (data_tvb, pinfo, tree, 0,
+                                         tvb_len,
+                                         &cdata->itlq, itl);
+               }
            }
         }
         else {
-            dissect_scsi_rsp (tvb, pinfo, tree);
+            dissect_scsi_rsp(tvb, pinfo, tree, &cdata->itlq, itl, scsi_status);
         }
     }
     else if ((opcode == ISCSI_OPCODE_SCSI_DATA_IN) ||
              (opcode == ISCSI_OPCODE_SCSI_DATA_OUT)) {
+       tvbuff_t *data_tvb;
+       int tvb_len, tvb_rlen;
+
         /* 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));
+       tvb_len=tvb_length_remaining(tvb, offset);
+       if(tvb_len>(int)data_segment_len)
+           tvb_len=data_segment_len;
+       tvb_rlen=tvb_reported_length_remaining(tvb, offset);
+       if(tvb_rlen>(int)data_segment_len)
+           tvb_rlen=data_segment_len;
+       data_tvb=tvb_new_subset(tvb, offset, tvb_len, tvb_rlen);
+        dissect_scsi_payload (data_tvb, pinfo, tree,
+                             (opcode==ISCSI_OPCODE_SCSI_DATA_OUT),
+                             &cdata->itlq, itl,
+                             data_offset);
+    }
+
+    if(S_bit){
+        dissect_scsi_rsp(tvb, pinfo, tree, &cdata->itlq, itl, scsi_status);
     }
 }
 
@@ -1679,28 +1589,553 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean chec
     /* 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);
-    guint32 pduLen = 48;
+    guint32 available_bytes = tvb_length(tvb);
     int digestsActive = 1;
     conversation_t *conversation = NULL;
     iscsi_session_t *iscsi_session=NULL;
+    guint8 opcode, tmpbyte;
+
+    if (available_bytes < 48 ){
+       /* heuristic already rejected the packet if size < 48,
+         assume it's an iscsi packet with a segmented header */
+        pinfo->desegment_offset = offset;
+        pinfo->desegment_len = 48 - available_bytes;
+        return TRUE;
+    }
 
-    /* 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 */
+    opcode = tvb_get_guint8(tvb, offset + 0);
+    opcode &= OPCODE_MASK;
+
+    /* heuristics to verify that the packet looks sane.   the heuristics
+     * are based on the RFC version of iscsi.
+     * (we should retire support for older iscsi versions in wireshark)
+     *      -- ronnie s
+     */
+    /* opcode must be any of the ones from the standard
+     * also check the header that it looks "sane"
+     * all reserved or undefined bits in iscsi must be set to zero.
+     */
+    switch(opcode){
+    case ISCSI_OPCODE_NOP_IN:
+       /* top two bits of byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* byte 1 must be 0x80 */
+       if(tvb_get_guint8(tvb, offset+1)!=0x80){
+           return FALSE;
+       }
+       /* bytes 2 and 3 must be 0 */
+       if(tvb_get_guint8(tvb, offset+2)||tvb_get_guint8(tvb, offset+3)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_NOP_OUT:
+       /* top bit of byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0x80){
+           return FALSE;
+       }
+       /* byte 1 must be 0x80 */
+       if(tvb_get_guint8(tvb, offset+1)!=0x80){
+           return FALSE;
+       }
+       /* bytes 2 and 3 must be 0 */
+       if(tvb_get_guint8(tvb, offset+2)||tvb_get_guint8(tvb, offset+3)){
+           return FALSE;
+       }
+       /* assume ITT and TTT must always be non NULL (ok they can be NULL
+        * from time to time but it usually means we are in the middle
+        * of a zeroed datablock).
+        */
+       if(!tvb_get_letohl(tvb,offset+16) || !tvb_get_letohl(tvb,offset+20)){
+           return FALSE;
+       }
+       /* all reserved bytes between 32 - 47 must be null */
+       if(tvb_get_letohl(tvb,offset+32)
+       || tvb_get_letohl(tvb,offset+36)
+       || tvb_get_letohl(tvb,offset+40)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_LOGIN_COMMAND:
+       /* top two bits in byte 0 must be 0x40 */
+       if((tvb_get_guint8(tvb, offset+0)&0xc0)!=0x40){
+
+           return FALSE;
+       }
+       /* both the T and C bits can not be set
+        * and the two reserved bits in byte 1 must be 0
+        */
+       tmpbyte=tvb_get_guint8(tvb, offset+1);
+       switch(tmpbyte&0xf0){
+       case 0x80:
+       case 0x40:
+       case 0x00:
+           break;
+       default:
+           return FALSE;
+       }
+       /* CSG and NSG must not be 2 */
+       if(((tmpbyte&0x03)==0x02)
+       || ((tmpbyte&0xc0)==0x08)){
+           return FALSE;
+       }
+       /* if T bit is set NSG must not be 0 */
+       if(tmpbyte&0x80){
+           if(!(tmpbyte&0x03)){
+               return FALSE;
+           }
+       }
+       /* should we test that datasegmentlen is non zero? */
+       break;
+    case ISCSI_OPCODE_LOGIN_RESPONSE:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+
+           return FALSE;
+       }
+       /* both the T and C bits can not be set
+        * and the two reserved bits in byte 1 must be 0
+        */
+       tmpbyte=tvb_get_guint8(tvb, offset+1);
+       switch(tmpbyte&0xf0){
+       case 0x80:
+       case 0x40:
+       case 0x00:
+           break;
+       default:
+           return FALSE;
+       }
+       /* CSG and NSG must not be 2 */
+       if(((tmpbyte&0x03)==0x02)
+       || ((tmpbyte&0xc0)==0x08)){
+           return FALSE;
+       }
+       /* if T bit is set NSG must not be 0 */
+       if(tmpbyte&0x80){
+           if(!(tmpbyte&0x03)){
+               return FALSE;
+           }
+       }
+       /* the 32bit words at offsets 20, 40, 44 must be zero */
+       if(tvb_get_letohl(tvb,offset+20)
+       || tvb_get_letohl(tvb,offset+40)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       /* the two bytes at offset 38 must be zero */
+       if(tvb_get_letohs(tvb,offset+38)){
+           return FALSE;
+       }
+       /* should we test that datasegmentlen is non zero unless we just
+        * entered full featured phase?
+        */
+       break;
+    case ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION:
+       /* top bit in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0x80){
+           return FALSE;
+       }
+       /* top bit in byte 1 must be set */
+       tmpbyte=tvb_get_guint8(tvb, offset+1);
+       if(!(tmpbyte&0x80)){
+           return FALSE;
+       }
+       /* Function must be known */
+       if(!match_strval(tmpbyte&0x7f, iscsi_task_management_functions)){
+           return FALSE;
+       }
+       /* bytes 2,3 must be null */
+       if(tvb_get_letohs(tvb,offset+2)){
+           return FALSE;
+       }
+       /* ahs and dsl must be null */
+       if(tvb_get_letohl(tvb,offset+4)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_TASK_MANAGEMENT_FUNCTION_RESPONSE:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* byte 1 must be 0x80 */
+       if(tvb_get_guint8(tvb, offset+1)!=0x80){
+           return FALSE;
+       }
+       /* response must be 0-6 or 255 */
+       tmpbyte=tvb_get_guint8(tvb,offset+2);
+       if(tmpbyte>6 && tmpbyte<255){
+           return FALSE;
+       }
+       /* byte 3 must be 0 */
+       if(tvb_get_guint8(tvb,offset+3)){
+           return FALSE;
+       }
+       /* ahs and dsl  as well as the 32bit words at offsets 8, 12, 20, 36
+        * 40, 44 must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+4)
+       || tvb_get_letohl(tvb,offset+8)
+       || tvb_get_letohl(tvb,offset+12)
+       || tvb_get_letohl(tvb,offset+20)
+       || tvb_get_letohl(tvb,offset+36)
+       || tvb_get_letohl(tvb,offset+40)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_LOGOUT_COMMAND:
+       /* top bit in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0x80){
+           return FALSE;
+       }
+       /* top bit in byte 1 must be set */
+       tmpbyte=tvb_get_guint8(tvb, offset+1);
+       if(!(tmpbyte&0x80)){
+           return FALSE;
+       }
+       /* Reason code must be known */
+       if(!match_strval(tmpbyte&0x7f, iscsi_logout_reasons)){
+           return FALSE;
+       }
+       /* bytes 2,3 must be null */
+       if(tvb_get_letohs(tvb,offset+2)){
+           return FALSE;
+       }
+       /* ahs and dsl  as well as the 32bit words at offsets 8, 12, 32, 36
+        * 40, 44 must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+4)
+       || tvb_get_letohl(tvb,offset+8)
+       || tvb_get_letohl(tvb,offset+12)
+       || tvb_get_letohl(tvb,offset+32)
+       || tvb_get_letohl(tvb,offset+36)
+       || tvb_get_letohl(tvb,offset+40)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_SNACK_REQUEST:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* top 4 bits in byte 1 must be 0x80 */
+       tmpbyte=tvb_get_guint8(tvb, offset+1);
+       if((tmpbyte&0xf0)!=0x80){
+           return FALSE;
+       }
+       /* type must be known */
+       if(!match_strval(tmpbyte&0x0f, iscsi_snack_types)){
+           return FALSE;
+       }
+       /* for status/snack and datack itt must be 0xffffffff
+        * for rdata/snack ttt must not be 0 or 0xffffffff
+        */
+       switch(tmpbyte&0x0f){
+       case 1:
+       case 2:
+           if(tvb_get_letohl(tvb,offset+16)!=0xffffffff){
+               return FALSE;
+           }
+           break;
+       case 3:
+           if(tvb_get_letohl(tvb,offset+20)==0xffffffff){
+               return FALSE;
+           }
+           if(tvb_get_letohl(tvb,offset+20)==0){
+               return FALSE;
+           }
+           break;
+       }
+       /* bytes 2,3 must be null */
+       if(tvb_get_letohs(tvb,offset+2)){
+           return FALSE;
+       }
+       /* the 32bit words at offsets 24, 32, 36
+        * must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+24)
+       || tvb_get_letohl(tvb,offset+32)
+       || tvb_get_letohl(tvb,offset+36)){
+           return FALSE;
+       }
+
+       break;
+    case ISCSI_OPCODE_R2T:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* byte 1 must be 0x80 */
+       if(tvb_get_guint8(tvb, offset+1)!=0x80){
+           return FALSE;
+       }
+       /* bytes 2,3 must be null */
+       if(tvb_get_letohs(tvb,offset+2)){
+           return FALSE;
+       }
+       /* ahs and dsl must be null */
+       if(tvb_get_letohl(tvb,offset+4)){
+           return FALSE;
+       }
+       /* desired data transfer length must not be null */
+       if(!tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_REJECT:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* byte 1 must be 0x80 */
+       if(tvb_get_guint8(tvb, offset+1)!=0x80){
+           return FALSE;
+       }
+       /* reason must be known */
+       if(!match_strval(tvb_get_guint8(tvb,offset+2), iscsi_reject_reasons)){
+           return FALSE;
+       }
+       /* byte 3 must be 0 */
+       if(tvb_get_guint8(tvb, offset+3)){
+           return FALSE;
+       }
+       /* the 32bit words at offsets 8, 12, 20, 40, 44
+        * must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+8)
+       || tvb_get_letohl(tvb,offset+12)
+       || tvb_get_letohl(tvb,offset+20)
+       || tvb_get_letohl(tvb,offset+40)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       /* the 32bit word at 16 must be 0xffffffff */
+       if(tvb_get_letohl(tvb,offset+16)!=0xffffffff){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_TEXT_COMMAND:
+       /* top bit in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0x80){
+           return FALSE;
+       }
+       /* one of the F and C bits must be set but not both
+        * low 6 bits in byte 1 must be 0
+        */
+       switch(tvb_get_guint8(tvb,offset+1)){
+       case 0x80:
+       case 0x40:
+           break;
+       default:
+           return FALSE;
+       }
+       /* bytes 2,3 must be null */
+       if(tvb_get_letohs(tvb,offset+2)){
+           return FALSE;
+       }
+       /* the 32bit words at offsets 32, 36, 40, 44
+        * must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+32)
+       || tvb_get_letohl(tvb,offset+36)
+       || tvb_get_letohl(tvb,offset+40)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_TEXT_RESPONSE:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* one of the F and C bits must be set but not both
+        * low 6 bits in byte 1 must be 0
+        */
+       switch(tvb_get_guint8(tvb,offset+1)){
+       case 0x80:
+       case 0x40:
+           break;
+       default:
+           return FALSE;
+       }
+       /* bytes 2,3 must be null */
+       if(tvb_get_letohs(tvb,offset+2)){
+           return FALSE;
+       }
+       /* the 32bit words at offsets 36, 40, 44
+        * must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+36)
+       || tvb_get_letohl(tvb,offset+40)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_SCSI_COMMAND:
+       /* top bit in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0x80){
+           return FALSE;
+       }
+       /* reserved bits in byte 1 must be 0 */
+       if(tvb_get_guint8(tvb, offset+1)&0x18){
+           return FALSE;
+       }
+       /* bytes 2,3 must be null */
+       if(tvb_get_letohs(tvb,offset+2)){
+           return FALSE;
+       }
+       /* last 6 bytes of LUN are always 0 */
+       if(tvb_get_ntohs(tvb, offset+10) || tvb_get_ntohl(tvb, offset+12)){
+           return FALSE;
+       }
+       /* expected data transfer length is never >16MByte ? */
+       if(tvb_get_guint8(tvb,offset+20)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_SCSI_RESPONSE:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* top bit in byte 1 must be 1 */
+       tmpbyte=tvb_get_guint8(tvb,offset+1);
+       if(!(tmpbyte&0x80)){
+           return FALSE;
+       }
+       /* the reserved bits in byte 1 must be 0 */
+       if(tmpbyte&0x61){
+           return FALSE;
+       }
+       /* status must be known */
+       if(!match_strval(tvb_get_guint8(tvb,offset+3), scsi_status_val)){
+           return FALSE;
+       }
+       /* the 32bit words at offsets 8, 12
+        * must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+8)
+       || tvb_get_letohl(tvb,offset+12)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_ASYNC_MESSAGE:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* byte 1 must be 0x80 */
+       if(tvb_get_guint8(tvb, offset+1)!=0x80){
+           return FALSE;
+       }
+       /* bytes 2,3 must be null */
+       if(tvb_get_letohs(tvb,offset+2)){
+           return FALSE;
+       }
+       /* the 32bit words at offsets 20, 44
+        * must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+20)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       /* the 32bit word at 16 must be 0xffffffff */
+       if(tvb_get_letohl(tvb,offset+16)!=0xffffffff){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_LOGOUT_RESPONSE:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* byte 1 must be 0x80 */
+       if(tvb_get_guint8(tvb, offset+1)!=0x80){
+           return FALSE;
+       }
+       /* response must be known */
+       if(!match_strval(tvb_get_guint8(tvb,offset+2), iscsi_logout_response)){
+           return FALSE;
+       }
+       /* byte 3 must be 0 */
+       if(tvb_get_guint8(tvb,offset+3)){
+           return FALSE;
+       }
+       /* ahs and dsl  as well as the 32bit words at offsets 8, 12, 20, 36
+        * 44 must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+4)
+       || tvb_get_letohl(tvb,offset+8)
+       || tvb_get_letohl(tvb,offset+12)
+       || tvb_get_letohl(tvb,offset+20)
+       || tvb_get_letohl(tvb,offset+36)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_SCSI_DATA_OUT:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* low 7 bits in byte 1 must be 0 */
+       if(tvb_get_guint8(tvb,offset+1)&0x7f){
+           return FALSE;
+       }
+       /* bytes 2,3 must be null */
+       if(tvb_get_letohs(tvb,offset+2)){
+           return FALSE;
+       }
+       /* the 32bit words at offsets 24, 32, 44
+        * must all be 0
+        */
+       if(tvb_get_letohl(tvb,offset+24)
+       || tvb_get_letohl(tvb,offset+32)
+       || tvb_get_letohl(tvb,offset+44)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_SCSI_DATA_IN:
+       /* top two bits in byte 0 must be 0 */
+       if(tvb_get_guint8(tvb, offset+0)&0xc0){
+           return FALSE;
+       }
+       /* reserved bits in byte 1 must be 0 */
+       if(tvb_get_guint8(tvb,offset+1)&0x38){
+           return FALSE;
+       }
+       /* byte 2 must be reserved */
+       if(tvb_get_guint8(tvb,offset+2)){
+           return FALSE;
+       }
+       break;
+    case ISCSI_OPCODE_VENDOR_SPECIFIC_I0:
+    case ISCSI_OPCODE_VENDOR_SPECIFIC_I1:
+    case ISCSI_OPCODE_VENDOR_SPECIFIC_I2:
+    case ISCSI_OPCODE_VENDOR_SPECIFIC_T0:
+    case ISCSI_OPCODE_VENDOR_SPECIFIC_T1:
+    case ISCSI_OPCODE_VENDOR_SPECIFIC_T2:
+       break;
+    default:
        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);
+       guint32 pduLen = 48;
        guint8 secondPduByte = tvb_get_guint8(tvb, offset + 1);
        int badPdu = FALSE;
+       guint8 ahsLen=0;
 
        /* mask out any extra bits in the opcode byte */
+       opcode = tvb_get_guint8(tvb, offset + 0);
        opcode &= OPCODE_MASK;
 
        opcode_str = match_strval(opcode, iscsi_opcodes);
@@ -1742,7 +2177,7 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean chec
                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. 
+                  the tag value we want for a response.
                   Assume 0 means we are just inside a big all zero
                   datablock.
                */
@@ -1770,16 +2205,14 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean chec
 
        if(opcode == ISCSI_OPCODE_SCSI_COMMAND) {
            /* ahsLen */
-           pduLen += tvb_get_guint8(tvb, offset + 4) * 4;
+           ahsLen = tvb_get_guint8(tvb, offset + 4);
+           pduLen += ahsLen * 4;
        }
 
        pduLen += data_segment_len;
        if((pduLen & 3) != 0)
            pduLen += 4 - (pduLen & 3);
 
-       if(digestsActive) {
-               pduLen += 4;
-       }
 
        if(digestsActive && data_segment_len > 0 && enableDataDigests) {
            if(dataDigestIsCRC32)
@@ -1789,36 +2222,35 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean chec
        }
 
        /* make sure we have a conversation for this session */
-        conversation = find_conversation (&pinfo->src, &pinfo->dst,
+        conversation = find_conversation (pinfo->fd->num, &pinfo->src, &pinfo->dst,
                                           pinfo->ptype, pinfo->srcport,
                                           pinfo->destport, 0);
         if (!conversation) {
-            conversation = conversation_new (&pinfo->src, &pinfo->dst,
+            conversation = conversation_new (pinfo->fd->num, &pinfo->src, &pinfo->dst,
                                              pinfo->ptype, pinfo->srcport,
                                              pinfo->destport, 0);
-            iscsi_session=g_mem_chunk_alloc(iscsi_sessions);
-            iscsi_session->conv_idx=conversation->index;
-            iscsi_session->header_digest=ISCSI_HEADER_DIGEST_AUTO;
-            g_hash_table_insert(iscsi_session_table, iscsi_session, iscsi_session);
         }
+        iscsi_session=conversation_get_proto_data(conversation, proto_iscsi);
         if(!iscsi_session){
-            iscsi_session_t key;
-            key.conv_idx=conversation->index;
-            /* this should never return NULL */
-            iscsi_session = (iscsi_session_t *)g_hash_table_lookup (iscsi_session_table, &key);
-            if(!iscsi_session){
-                iscsi_session=g_mem_chunk_alloc(iscsi_sessions);
-                iscsi_session->conv_idx=conversation->index;
-                iscsi_session->header_digest=ISCSI_HEADER_DIGEST_AUTO;
-                g_hash_table_insert(iscsi_session_table, iscsi_session, iscsi_session);
-            }
+            iscsi_session=se_alloc(sizeof(iscsi_session_t));
+            iscsi_session->header_digest=ISCSI_HEADER_DIGEST_AUTO;
+            iscsi_session->itlq=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "iSCSI ITLQ");
+            iscsi_session->itl=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "iSCSI ITL");
+            conversation_add_proto_data(conversation, proto_iscsi, iscsi_session);
+
+            /* DataOut PDUs are often mistaken by DCERPC heuristics to be
+             * that protocol. Now that we know this is iscsi, set a
+             * dissector for this conversation to block other heuristic
+             * dissectors.
+             */
+            conversation_set_dissector(conversation, iscsi_handle);
         }
         /* try to autodetect if header digest is used or not */
-       if(digestsActive && (available_bytes>=52) && (iscsi_session->header_digest==ISCSI_HEADER_DIGEST_AUTO) ){
+       if(digestsActive && (available_bytes>=(guint32) (48+4+ahsLen*4)) && (iscsi_session->header_digest==ISCSI_HEADER_DIGEST_AUTO) ){
             guint32 crc;
                /* we have enough data to test if HeaderDigest is enabled */
-            crc= ~calculateCRC32(tvb_get_ptr(tvb, offset, 48), 48, CRC32C_PRELOAD);
-            if(crc==tvb_get_ntohl(tvb,48)){
+            crc= ~crc32c_calculate(tvb_get_ptr(tvb, offset, 48+ahsLen*4), 48+ahsLen*4, CRC32C_PRELOAD);
+            if(crc==tvb_get_ntohl(tvb,48+ahsLen*4)){
                 iscsi_session->header_digest=ISCSI_HEADER_DIGEST_CRC32;
             } else {
                 iscsi_session->header_digest=ISCSI_HEADER_DIGEST_NONE;
@@ -1826,6 +2258,23 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean chec
        }
 
 
+       /* Add header digest length to pdulen */
+       if(digestsActive){
+               switch(iscsi_session->header_digest){
+               case ISCSI_HEADER_DIGEST_CRC32:
+                       pduLen += 4;
+                       break;
+               case ISCSI_HEADER_DIGEST_NONE:
+                       break;
+               case ISCSI_HEADER_DIGEST_AUTO:
+                       /* oops we didnt know what digest is used yet */
+                       /* here we should use some default */
+                       break;
+               default:
+                       DISSECTOR_ASSERT_NOT_REACHED();
+               }
+       }
+
        /*
         * Desegmentation check.
         */
@@ -1846,7 +2295,7 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean chec
        }
 
        /* This is to help TCP keep track of PDU boundaries
-          and allows it to find PDUs that are not aligned to 
+          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
@@ -1867,7 +2316,7 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean chec
                col_append_str(pinfo->cinfo, COL_INFO, ", ");
        }
 
-       dissect_iscsi_pdu(tvb, pinfo, tree, offset, opcode, opcode_str, data_segment_len, iscsi_session);
+       dissect_iscsi_pdu(tvb, pinfo, tree, offset, opcode, opcode_str, data_segment_len, iscsi_session, conversation);
        if(pduLen > available_bytes)
            pduLen = available_bytes;
        offset += pduLen;
@@ -1882,10 +2331,11 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean chec
    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.
+   We still check that the PDU header looks sane though.
 */
-static void
+static int
 dissect_iscsi_handle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
-    dissect_iscsi(tvb, pinfo, tree, FALSE);
+    return dissect_iscsi(tvb, pinfo, tree, FALSE);
 }
 
 /* This is called through the heuristic handler.
@@ -1895,11 +2345,20 @@ dissect_iscsi_handle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
 */
 static gboolean
 dissect_iscsi_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
+    guint32 available_bytes = tvb_length(tvb);
+
+    /* quick check to see if the packet is long enough to contain the
+     * minimum amount of information we need */
+    if (available_bytes < 48 ){
+       /* no, so give up */
+       return FALSE;
+    }
+
     return dissect_iscsi(tvb, pinfo, tree, TRUE);
 }
 
 
-/* Register the protocol with Ethereal */
+/* Register the protocol with Wireshark */
 
 /*
  * this format is require because a script is used to build the C
@@ -1939,47 +2398,72 @@ proto_register_iscsi(void)
 
        { &hf_iscsi_AHS,
          { "AHS", "iscsi.ahs",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Additional header segment", HFILL }
        },
+       { &hf_iscsi_AHS_length,
+         { "AHS Length", "iscsi.ahs.length",
+           FT_UINT16, BASE_DEC, NULL, 0,
+           "Length of Additional header segment", HFILL }
+       },
+       { &hf_iscsi_AHS_read_data_length,
+         { "Bidirectional Read Data Length", "iscsi.ahs.bidir.length",
+           FT_UINT32, BASE_DEC, NULL, 0,
+           NULL, HFILL }
+       },
+       { &hf_iscsi_AHS_type,
+         { "AHS Type", "iscsi.ahs.type",
+           FT_UINT8, BASE_DEC, VALS(ahs_type_vals), 0,
+           "Type of Additional header segment", HFILL }
+       },
+       { &hf_iscsi_AHS_extended_cdb,
+         { "AHS Extended CDB", "iscsi.ahs.extended_cdb",
+           FT_BYTES, BASE_NONE, NULL, 0,
+           NULL, HFILL }
+       },
+       { &hf_iscsi_AHS_blob,
+         { "Unknown AHS blob", "iscsi.ahs.unknown_blob",
+           FT_BYTES, BASE_NONE, NULL, 0,
+           NULL, HFILL }
+       },
        { &hf_iscsi_Padding,
          { "Padding", "iscsi.padding",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Padding to 4 byte boundary", HFILL }
        },
        { &hf_iscsi_ping_data,
          { "PingData", "iscsi.pingdata",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Ping Data", HFILL }
        },
        { &hf_iscsi_immediate_data,
          { "ImmediateData", "iscsi.immediatedata",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Immediate Data", HFILL }
        },
        { &hf_iscsi_write_data,
          { "WriteData", "iscsi.writedata",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Write Data", HFILL }
        },
        { &hf_iscsi_read_data,
          { "ReadData", "iscsi.readdata",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Read Data", HFILL }
        },
        { &hf_iscsi_error_pdu_data,
          { "ErrorPDUData", "iscsi.errorpdudata",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, 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_async_event_data,
+         { "AsyncEventData", "iscsi.asynceventdata",
+           FT_BYTES, BASE_NONE, NULL, 0,
+           "Async Event Data", HFILL }
        },
        { &hf_iscsi_vendor_specific_data,
          { "VendorSpecificData", "iscsi.vendorspecificdata",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Vendor Specific Data", HFILL }
        },
        { &hf_iscsi_HeaderDigest32,
@@ -1989,7 +2473,7 @@ proto_register_iscsi(void)
        },
        { &hf_iscsi_DataDigest,
          { "DataDigest", "iscsi.datadigest",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Data Digest", HFILL }
        },
        { &hf_iscsi_DataDigest32,
@@ -2000,7 +2484,7 @@ proto_register_iscsi(void)
        { &hf_iscsi_Opcode,
          { "Opcode", "iscsi.opcode",
            FT_UINT8, BASE_HEX, VALS(iscsi_opcodes), 0,
-           "Opcode", HFILL }
+           NULL, HFILL }
        },
 /* #ifdef DRAFT08 */
        { &hf_iscsi_X,
@@ -2061,7 +2545,7 @@ proto_register_iscsi(void)
        },
        { &hf_iscsi_LUN,
          { "LUN", "iscsi.lun",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Logical Unit Number", HFILL }
        },
        { &hf_iscsi_InitiatorTaskTag,
@@ -2218,7 +2702,7 @@ proto_register_iscsi(void)
 /* #else */
        { &hf_iscsi_ISID,
          { "ISID", "iscsi.isid",
-           FT_BYTES, BASE_HEX, NULL, 0,
+           FT_BYTES, BASE_NONE, NULL, 0,
            "Initiator part of session identifier", HFILL }
        },
 /* #ifdef DRAFT09 */
@@ -2319,7 +2803,7 @@ proto_register_iscsi(void)
        },
        { &hf_iscsi_KeyValue,
          { "KeyValue", "iscsi.keyvalue",
-           FT_STRING, 0, NULL, 0,
+           FT_STRING, BASE_NONE, NULL, 0,
            "Key/value pair", HFILL }
        },
        { &hf_iscsi_Text_F,
@@ -2345,7 +2829,7 @@ proto_register_iscsi(void)
        { &hf_iscsi_TaskManagementFunction_Response,
          { "Response", "iscsi.taskmanfun.response",
            FT_UINT8, BASE_HEX, VALS(iscsi_task_management_responses), 0,
-           "Response", HFILL }
+           NULL, HFILL }
        },
        { &hf_iscsi_TaskManagementFunction_ReferencedTaskTag,
          { "ReferencedTaskTag", "iscsi.taskmanfun.referencedtasktag",
@@ -2375,12 +2859,12 @@ proto_register_iscsi(void)
        { &hf_iscsi_Time2Wait,
          { "Time2Wait", "iscsi.time2wait",
            FT_UINT16, BASE_HEX, NULL, 0,
-           "Time2Wait", HFILL }
+           NULL, HFILL }
        },
        { &hf_iscsi_Time2Retain,
          { "Time2Retain", "iscsi.time2retain",
            FT_UINT16, BASE_HEX, NULL, 0,
-           "Time2Retain", HFILL }
+           NULL, HFILL }
        },
        { &hf_iscsi_DesiredDataLength,
          { "DesiredDataLength", "iscsi.desireddatalength",
@@ -2452,7 +2936,6 @@ proto_register_iscsi(void)
      * 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);
@@ -2546,10 +3029,8 @@ proto_register_iscsi(void)
 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);
+    iscsi_handle = new_create_dissector_handle(dissect_iscsi_handle, proto_iscsi);
     dissector_add_handle("tcp.port", iscsi_handle);
 }