forked from osmocom/wireshark
![Ronnie Sahlberg](/assets/img/avatar_default.png)
Dont implement the complex "extended logical unit addressing format" just yet. That one is hairy so wait until we actually see it in the wild first. Add decoding of LUNs using single level lun structure and flat addressing space modes svn path=/trunk/; revision=44058
313 lines
13 KiB
C
313 lines
13 KiB
C
/* packet-scsi.h
|
|
* Author: Dinesh G Dutt (ddutt@cisco.com)
|
|
*
|
|
* $Id$
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#ifndef __PACKET_SCSI_H_
|
|
#define __PACKET_SCSI_H_
|
|
|
|
|
|
/* Structure containing itl nexus data :
|
|
* The itlq nexus is a structure containing data specific
|
|
* for a initiator target lun combination.
|
|
*/
|
|
typedef struct _itl_nexus_t {
|
|
#define SCSI_CMDSET_DEFAULT 0x80
|
|
#define SCSI_CMDSET_MASK 0x7f
|
|
guint8 cmdset; /* This is a bitfield.
|
|
* The MSB (0x80) represents whether
|
|
* 0: the commandset is known from a INQ PDU
|
|
* 1: is using the "default" from preferences.
|
|
* The lower 7 bits represent the commandset used
|
|
* for decoding commands on this itl nexus.
|
|
* The field is initialized to 0xff == unknown.
|
|
*/
|
|
conversation_t *conversation;
|
|
} itl_nexus_t;
|
|
|
|
/* Structure containing itlq nexus data :
|
|
* The itlq nexus is a structure containing data specific
|
|
* for a initiator target lun queue/commandid combination.
|
|
*/
|
|
typedef struct _itlq_nexus_t {
|
|
guint32 first_exchange_frame;
|
|
guint32 last_exchange_frame;
|
|
guint16 lun; /* initialized to 0xffff == unknown */
|
|
guint16 scsi_opcode; /* initialized to 0xffff == unknown */
|
|
guint16 flags;
|
|
|
|
#define SCSI_DATA_READ 0x0001
|
|
#define SCSI_DATA_WRITE 0x0002
|
|
guint16 task_flags; /* Flags set by the transport for this
|
|
* scsi task.
|
|
*
|
|
* If there is no data being transferred both flags
|
|
* are 0 and both data lengths below are undefined.
|
|
*
|
|
* If one of the flags are set the amount of
|
|
* data being transferred is held in data_length
|
|
* and bidir_data_length is undefined.
|
|
*
|
|
* If both flags are set (a bidirectional transfer)
|
|
* data_length specifies the amount of DATA-OUT and
|
|
* bidir_data_length specifies the amount of DATA-IN
|
|
*/
|
|
guint32 data_length;
|
|
guint32 bidir_data_length;
|
|
|
|
guint32 alloc_len; /* we need to track alloc_len between the CDB and
|
|
* the DATA pdus for some opcodes.
|
|
*/
|
|
nstime_t fc_time;
|
|
|
|
|
|
void *extra_data; /* extra data that that is task specific */
|
|
} itlq_nexus_t;
|
|
|
|
|
|
#define SCSI_PDU_TYPE_CDB 1
|
|
#define SCSI_PDU_TYPE_DATA 2
|
|
#define SCSI_PDU_TYPE_RSP 4
|
|
#define SCSI_PDU_TYPE_SNS 5
|
|
typedef struct _scsi_task_data {
|
|
int type;
|
|
itlq_nexus_t *itlq;
|
|
itl_nexus_t *itl;
|
|
} scsi_task_data_t;
|
|
|
|
|
|
/* list of commands for each commandset */
|
|
typedef void (*scsi_dissector_t)(tvbuff_t *tvb, packet_info *pinfo,
|
|
proto_tree *tree, guint offset,
|
|
gboolean isreq, gboolean iscdb,
|
|
guint32 payload_len, scsi_task_data_t *cdata);
|
|
|
|
typedef struct _scsi_cdb_table_t {
|
|
scsi_dissector_t func;
|
|
} scsi_cdb_table_t;
|
|
|
|
|
|
/* SPC Commands */
|
|
#define SCSI_SPC_ACCESS_CONTROL_IN 0x86
|
|
#define SCSI_SPC_ACCESS_CONTROL_OUT 0x87
|
|
#define SCSI_SPC_CHANGE_DEFINITION 0x40
|
|
#define SCSI_SPC_COMPARE 0x39
|
|
#define SCSI_SPC_COPY 0x18
|
|
#define SCSI_SPC_COPY_AND_VERIFY 0x3A
|
|
#define SCSI_SPC_INQUIRY 0x12
|
|
#define SCSI_SPC_EXTCOPY 0x83
|
|
#define SCSI_SPC_LOGSELECT 0x4C
|
|
#define SCSI_SPC_LOGSENSE 0x4D
|
|
#define SCSI_SPC_MODESELECT6 0x15
|
|
#define SCSI_SPC_MODESELECT10 0x55
|
|
#define SCSI_SPC_MODESENSE6 0x1A
|
|
#define SCSI_SPC_MODESENSE10 0x5A
|
|
#define SCSI_SPC_PERSRESVIN 0x5E
|
|
#define SCSI_SPC_PERSRESVOUT 0x5F
|
|
#define SCSI_SPC_PREVMEDREMOVAL 0x1E
|
|
#define SCSI_SPC_READBUFFER 0x3C
|
|
#define SCSI_SPC_RCVCOPYRESULTS 0x84
|
|
#define SCSI_SPC_RCVDIAGRESULTS 0x1C
|
|
#define SCSI_SPC_RELEASE6 0x17
|
|
#define SCSI_SPC_RELEASE10 0x57
|
|
#define SCSI_SPC_REPORTDEVICEID 0xA3
|
|
#define SCSI_SPC_REPORTLUNS 0xA0
|
|
#define SCSI_SPC_REQSENSE 0x03
|
|
#define SCSI_SPC_RESERVE6 0x16
|
|
#define SCSI_SPC_RESERVE10 0x56
|
|
#define SCSI_SPC_SENDDIAG 0x1D
|
|
#define SCSI_SPC_SETDEVICEID 0xA4
|
|
#define SCSI_SPC_TESTUNITRDY 0x00
|
|
#define SCSI_SPC_WRITEBUFFER 0x3B
|
|
#define SCSI_SPC_VARLENCDB 0x7F
|
|
|
|
void dissect_spc_inquiry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint32 payload_len, scsi_task_data_t *cdata);
|
|
void dissect_spc_logselect(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_logsense(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_modeselect6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len, scsi_task_data_t *cdata);
|
|
void dissect_spc_modesense6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len, scsi_task_data_t *cdata);
|
|
void dissect_spc_modeselect10(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len, scsi_task_data_t *cdata);
|
|
void dissect_spc_modesense10(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len, scsi_task_data_t *cdata);
|
|
void dissect_spc_persistentreservein(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len, scsi_task_data_t *cdata);
|
|
void dissect_spc_persistentreserveout(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_reportluns(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_testunitready (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_requestsense (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_preventallowmediaremoval (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_writebuffer (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb _U_, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_reserve6 (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_release6 (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_reserve10 (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_release10 (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_senddiagnostic (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
void dissect_spc_extcopy (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint offset, gboolean isreq, gboolean iscdb, guint payload_len _U_, scsi_task_data_t *cdata _U_);
|
|
|
|
|
|
|
|
|
|
|
|
extern const value_string scsi_status_val[];
|
|
|
|
/*
|
|
* SCSI Device Types.
|
|
*
|
|
* These can be supplied to the dissection routines if the caller happens
|
|
* to know the device type (e.g., NDMP assumes that a "jukebox" is a
|
|
* media changer, SCSI_DEV_SMC, and a "tape" is a sequential access device,
|
|
* SCSI_DEV_SSC).
|
|
*
|
|
* If the caller doesn't know the device type, it supplies SCSI_DEV_UNKNOWN.
|
|
*/
|
|
#define SCSI_DEV_UNKNOWN -1
|
|
#define SCSI_DEV_SBC 0x0
|
|
#define SCSI_DEV_SSC 0x1
|
|
#define SCSI_DEV_PRNT 0x2
|
|
#define SCSI_DEV_PROC 0x3
|
|
#define SCSI_DEV_WORM 0x4
|
|
#define SCSI_DEV_CDROM 0x5
|
|
#define SCSI_DEV_SCAN 0x6
|
|
#define SCSI_DEV_OPTMEM 0x7
|
|
#define SCSI_DEV_SMC 0x8
|
|
#define SCSI_DEV_COMM 0x9
|
|
#define SCSI_DEV_RAID 0xC
|
|
#define SCSI_DEV_SES 0xD
|
|
#define SCSI_DEV_RBC 0xE
|
|
#define SCSI_DEV_OCRW 0xF
|
|
#define SCSI_DEV_OSD 0x11
|
|
#define SCSI_DEV_ADC 0x12
|
|
#define SCSI_DEV_NOLUN 0x1F
|
|
|
|
#define SCSI_DEV_BITS 0x1F /* the lower 5 bits indicate device type */
|
|
#define SCSI_MS_PCODE_BITS 0x3F /* Page code bits in Mode Sense */
|
|
|
|
/* Function Decls; functions invoked by SAM-2 transport protocols such as
|
|
* FCP/iSCSI
|
|
*/
|
|
void dissect_scsi_cdb (tvbuff_t *, packet_info *, proto_tree *,
|
|
gint, itlq_nexus_t *, itl_nexus_t *);
|
|
void dissect_scsi_rsp (tvbuff_t *, packet_info *, proto_tree *, itlq_nexus_t *, itl_nexus_t *, guint8);
|
|
void dissect_scsi_payload (tvbuff_t *, packet_info *, proto_tree *,
|
|
gboolean, itlq_nexus_t *, itl_nexus_t *,
|
|
guint32 relative_offset);
|
|
void dissect_scsi_snsinfo (tvbuff_t *, packet_info *, proto_tree *, guint, guint, itlq_nexus_t *, itl_nexus_t *);
|
|
|
|
void dissect_scsi_lun(proto_tree *, tvbuff_t *, guint);
|
|
|
|
WS_VAR_IMPORT const value_string scsi_mmc_vals[];
|
|
|
|
extern const int *cdb_control_fields[6];
|
|
extern gint ett_scsi_control;
|
|
extern int hf_scsi_control;
|
|
extern int hf_scsi_alloclen16;
|
|
|
|
/* service actions */
|
|
#define SHORT_FORM_BLOCK_ID 0x00
|
|
#define SHORT_FORM_VENDOR_SPECIFIC 0x01
|
|
#define LONG_FORM 0x06
|
|
#define EXTENDED_FORM 0x08
|
|
#define SERVICE_READ_CAPACITY16 0x10
|
|
#define SERVICE_READ_LONG16 0x11
|
|
#define SERVICE_GET_LBA_STATUS 0x12
|
|
|
|
extern const value_string service_action_vals[];
|
|
extern const value_string scsi_devid_codeset_val[];
|
|
extern const value_string scsi_devid_idtype_val[];
|
|
extern value_string_ext scsi_asc_val_ext;
|
|
|
|
/* These two defines are used to handle cases where data coming back from
|
|
* the device is truncated due to a too short allocation_length specified
|
|
* in the command CDB.
|
|
* This is semi-common in SCSI and it would be wrong to mark these packets
|
|
* as [malformed packets].
|
|
* These macros will reset the reported length to what the data pdu specified
|
|
* and if a ReportedBoundsError is generated we will instead throw
|
|
* ScsiBoundsError
|
|
*
|
|
* Please see dissect_spc_inquiry() for an example how to use these
|
|
* macros.
|
|
*/
|
|
#define TRY_SCSI_CDB_ALLOC_LEN(pinfo, tvb, offset, length) \
|
|
{ \
|
|
volatile gboolean short_packet; \
|
|
tvbuff_t *new_tvb; \
|
|
guint32 end_data_offset=0; \
|
|
\
|
|
short_packet=pinfo->fd->cap_len<pinfo->fd->pkt_len; \
|
|
new_tvb=tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset), length);\
|
|
tvb=new_tvb; \
|
|
offset=0; \
|
|
TRY {
|
|
|
|
#define END_TRY_SCSI_CDB_ALLOC_LEN \
|
|
if(end_data_offset){ \
|
|
/* just verify we can read all the bytes we were\
|
|
* supposed to. \
|
|
*/ \
|
|
tvb_get_guint8(tvb,end_data_offset); \
|
|
} \
|
|
} /* TRY */ \
|
|
CATCH(BoundsError) { \
|
|
if(short_packet){ \
|
|
/* this was a short packet */ \
|
|
RETHROW; \
|
|
} else { \
|
|
/* We probably tried to dissect beyond the end \
|
|
* of the alloc len reported in the data \
|
|
* pdu. This is not an error so dont flag it as \
|
|
* one \
|
|
* it is the alloc_len in the CDB that is the \
|
|
* important one \
|
|
*/ \
|
|
} \
|
|
} \
|
|
CATCH(ReportedBoundsError) { \
|
|
if(short_packet){ \
|
|
/* this was a short packet */ \
|
|
RETHROW; \
|
|
} else { \
|
|
/* this packet was not really short but limited \
|
|
* due to a short SCSI allocation length \
|
|
*/ \
|
|
THROW(ScsiBoundsError); \
|
|
} \
|
|
} \
|
|
CATCH_ALL { \
|
|
RETHROW; \
|
|
} \
|
|
ENDTRY; \
|
|
}
|
|
|
|
/* If the data pdu contains an alloc_len as well, this macro can be set
|
|
* to registe this offset for the TRY section above.
|
|
* At the end of the TRY section we will, if set, verify that the data
|
|
* pdu contained all bytes that was specified in the data alloc len.
|
|
*
|
|
* This macro does currently not do anything but we might enhance it in
|
|
* the future. There is no harm in teaching the dissector about how long
|
|
* the data pdu is supposed to be according to alloc_len in the data pdu
|
|
*/
|
|
#define SET_SCSI_DATA_END(offset) \
|
|
end_data_offset=offset;
|
|
|
|
|
|
#endif
|