aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-iscsi.c
diff options
context:
space:
mode:
authorsahlberg <sahlberg@f5534014-38df-0310-8fa8-9805f1628bb7>2006-06-20 13:16:50 +0000
committersahlberg <sahlberg@f5534014-38df-0310-8fa8-9805f1628bb7>2006-06-20 13:16:50 +0000
commit72eba588892ba669f2942cb6a6da3c595b8ba801 (patch)
treee4adf9d11e2a0af444b6831b94902d2a31ddf594 /epan/dissectors/packet-iscsi.c
parent0d3e4785934a53aa827ed883bed54c6a597de54d (diff)
for iscsi traces where there is a lot of packetloss the dissector frequently gets out of sync
and the weak heuristics often cause wireshark to mistake some segment containing read/write data to be iscsi. make the heuristics to check that a packet really is iscsi much stronger git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@18523 f5534014-38df-0310-8fa8-9805f1628bb7
Diffstat (limited to 'epan/dissectors/packet-iscsi.c')
-rw-r--r--epan/dissectors/packet-iscsi.c516
1 files changed, 511 insertions, 5 deletions
diff --git a/epan/dissectors/packet-iscsi.c b/epan/dissectors/packet-iscsi.c
index 3b434a2abe..6f2ae1f4ec 100644
--- a/epan/dissectors/packet-iscsi.c
+++ b/epan/dissectors/packet-iscsi.c
@@ -406,6 +406,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},
};
@@ -1579,24 +1580,528 @@ dissect_iscsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean chec
int digestsActive = 1;
conversation_t *conversation = NULL;
iscsi_session_t *iscsi_session=NULL;
+ guint8 opcode, tmpbyte;
/* 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)) {
+ if (available_bytes < 48 ){
/* no, so give up */
return FALSE;
}
+ 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_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;
+ }
+ /* exactly one of the T and C bits must 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:
+ 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;
+ }
+ /* 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;
guint32 pduLen = 48;
- 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 = tvb_get_guint8(tvb, offset + 0);
opcode &= OPCODE_MASK;
opcode_str = match_strval(opcode, iscsi_opcodes);
@@ -1791,10 +2296,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.
@@ -2456,6 +2962,6 @@ proto_reg_handoff_iscsi(void)
{
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);
}