2 * Routines for Fibre Channel Protocol for SCSI (FCP)
3 * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com>
5 * $Id: packet-fcp.c,v 1.2 2003/01/31 03:17:46 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED"
12 * is a dissector file; if you just copied this from README.developer,
13 * don't bother with the "Copied from" - you don't even need to put
14 * in a "Copied from" if you copied an existing dissector, especially
15 * if the bulk of the code in the new dissector is your code)
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version 2
20 * of the License, or (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40 #ifdef HAVE_SYS_TYPES_H
41 # include <sys/types.h>
44 #ifdef HAVE_NETINET_IN_H
45 # include <netinet/in.h>
50 #ifdef NEED_SNPRINTF_H
51 # include "snprintf.h"
55 #include <epan/packet.h>
56 #include <epan/conversation.h>
58 #include "packet-fc.h"
59 #include "packet-fcp.h"
60 #include "packet-scsi.h"
62 /* Initialize the protocol and registered fields */
63 static int proto_fcp = -1;
64 static int hf_fcp_multilun = -1;
65 static int hf_fcp_singlelun = -1;
66 static int hf_fcp_crn = -1;
67 static int hf_fcp_taskattr = -1;
68 static int hf_fcp_taskmgmt = -1;
69 static int hf_fcp_addlcdblen = -1;
70 static int hf_fcp_rddata = -1;
71 static int hf_fcp_wrdata = -1;
72 static int hf_fcp_dl = -1;
73 static int hf_fcp_data_ro = -1;
74 static int hf_fcp_burstlen = -1;
75 static int hf_fcp_rspflags = -1;
76 static int hf_fcp_resid = -1;
77 static int hf_fcp_snslen = -1;
78 static int hf_fcp_rsplen = -1;
79 static int hf_fcp_rspcode = -1;
80 static int hf_fcp_scsistatus = -1;
81 static int hf_fcp_type = -1;
84 /* Initialize the subtree pointers */
85 static gint ett_fcp = -1;
86 static dissector_table_t fcp_dissector;
87 static dissector_handle_t data_handle;
89 typedef struct _fcp_conv_key {
93 typedef struct _fcp_conv_data {
100 GHashTable *fcp_req_hash = NULL;
101 GMemChunk *fcp_req_keys = NULL;
102 GMemChunk *fcp_req_vals = NULL;
103 guint32 fcp_init_count = 25;
109 fcp_equal(gconstpointer v, gconstpointer w)
111 fcp_conv_key_t *v1 = (fcp_conv_key_t *)v;
112 fcp_conv_key_t *v2 = (fcp_conv_key_t *)w;
114 return (v1->conv_idx == v2->conv_idx);
118 fcp_hash (gconstpointer v)
120 fcp_conv_key_t *key = (fcp_conv_key_t *)v;
129 * Protocol initialization
132 fcp_init_protocol(void)
135 g_mem_chunk_destroy(fcp_req_keys);
137 g_mem_chunk_destroy(fcp_req_vals);
139 g_hash_table_destroy(fcp_req_hash);
141 fcp_req_hash = g_hash_table_new(fcp_hash, fcp_equal);
142 fcp_req_keys = g_mem_chunk_new("fcp_req_keys",
143 sizeof(fcp_conv_key_t),
144 fcp_init_count * sizeof(fcp_conv_key_t),
146 fcp_req_vals = g_mem_chunk_new("fcp_req_vals",
147 sizeof(fcp_conv_data_t),
148 fcp_init_count * sizeof(fcp_conv_data_t),
153 task_mgmt_flags_to_str (guint8 flags, gchar *str)
163 strcpy (str, "Obsolete, ");
168 strcpy (&str[stroff], "Clear ACA, ");
173 strcpy (&str[stroff], "Target Reset, ");
178 strcpy (&str[stroff], "LU Reset, ");
183 strcpy (&str[stroff], "Rsvd, ");
188 strcpy (&str[stroff], "Clear Task Set, ");
193 strcpy (&str[stroff], "Abort Task Set");
201 rspflags_to_str (guint8 flags, gchar *str)
211 strcpy (str, "FCP_CONF_REQ | ");
215 strcpy (&str[stroff], "FCP_RESID_UNDER | ");
219 strcpy (&str[stroff], "FCP_RESID_OVER | ");
223 strcpy (&str[stroff], "FCP_SNS_LEN_VLD | ");
227 strcpy (&str[stroff], "FCP_RSP_LEN_VLD | ");
233 /* Code to actually dissect the packets */
235 dissect_fcp_cmnd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
243 proto_tree *fcp_tree = NULL;
244 conversation_t *conversation;
245 fcp_conv_data_t *cdata;
246 fcp_conv_key_t ckey, *req_key;
247 scsi_task_id_t task_key;
249 /* Determine the length of the FCP part of the packet */
250 flags = tvb_get_guint8 (tvb, offset+10);
252 add_len = tvb_get_guint8 (tvb, offset+11) & 0x7C;
253 add_len = add_len >> 2;
255 len = FCP_DEF_CMND_LEN + add_len;
258 len = FCP_DEF_CMND_LEN;
261 /* We track the conversation to determine how many bytes is required */
262 /* by the data that is sent back or sent next by the initiator as part */
263 /* of this command. The state is destroyed in the response dissector */
265 conversation = find_conversation (&pinfo->src, &pinfo->dst,
266 pinfo->ptype, pinfo->oxid,
267 pinfo->rxid, NO_PORT2);
269 conversation = conversation_new (&pinfo->src, &pinfo->dst,
270 pinfo->ptype, pinfo->oxid,
271 pinfo->rxid, NO_PORT2);
274 ckey.conv_idx = conversation->index;
275 task_key.conv_id = conversation->index;
276 task_key.task_id = conversation->index;
277 pinfo->private_data = (void *)&task_key;
279 cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash,
282 /* Since we never free the memory used by an exchange, this maybe a
283 * case of another request using the same exchange as a previous
286 cdata->fcp_dl = tvb_get_ntohl (tvb, offset+12+16+add_len);
287 cdata->abs_usecs = pinfo->fd->abs_usecs;
288 cdata->abs_secs = pinfo->fd->abs_secs;
291 req_key = g_mem_chunk_alloc (fcp_req_keys);
292 req_key->conv_idx = conversation->index;
294 cdata = g_mem_chunk_alloc (fcp_req_vals);
295 cdata->fcp_dl = tvb_get_ntohl (tvb, offset+12+16+add_len);
296 cdata->abs_usecs = pinfo->fd->abs_usecs;
297 cdata->abs_secs = pinfo->fd->abs_secs;
299 g_hash_table_insert (fcp_req_hash, req_key, cdata);
302 dissect_scsi_cdb (tvb, pinfo, fcp_tree, offset+12, 16+add_len,
306 ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, len,
308 fcp_tree = proto_item_add_subtree (ti, ett_fcp);
309 proto_tree_add_uint_hidden (fcp_tree, hf_fcp_type, tvb, offset, 0, 0);
311 lun0 = tvb_get_guint8 (tvb, offset);
313 /* Display single-level LUNs in decimal for clarity */
314 /* I'm taking a shortcut here by assuming that if the first byte of the
315 * LUN field is 0, it is a single-level LUN. This is not true. For a
316 * real single-level LUN, all 8 bytes except byte 1 must be 0.
320 proto_tree_add_item (fcp_tree, hf_fcp_multilun, tvb, offset, 8, 0);
323 cdata->fcp_lun = tvb_get_guint8 (tvb, offset+1);
324 proto_tree_add_item (fcp_tree, hf_fcp_singlelun, tvb, offset+1,
328 proto_tree_add_item (fcp_tree, hf_fcp_crn, tvb, offset+8, 1, 0);
329 proto_tree_add_item (fcp_tree, hf_fcp_taskattr, tvb, offset+9, 1, 0);
330 proto_tree_add_uint_format (fcp_tree, hf_fcp_taskmgmt, tvb, offset+10,
332 "Task Management Flags: 0x%x (%s)",
334 task_mgmt_flags_to_str (flags, str));
335 proto_tree_add_item (fcp_tree, hf_fcp_addlcdblen, tvb, offset+11, 1, 0);
336 proto_tree_add_item (fcp_tree, hf_fcp_rddata, tvb, offset+11, 1, 0);
337 proto_tree_add_item (fcp_tree, hf_fcp_wrdata, tvb, offset+11, 1, 0);
338 dissect_scsi_cdb (tvb, pinfo, tree, offset+12, 16+add_len,
340 proto_tree_add_item (fcp_tree, hf_fcp_dl, tvb, offset+12+16+add_len,
346 dissect_fcp_data (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
348 conversation_t *conversation;
349 fcp_conv_data_t *cdata = NULL;
352 proto_tree *fcp_tree;
353 scsi_task_id_t task_key;
355 /* Retrieve conversation state to determine expected payload */
356 conversation = find_conversation (&pinfo->src, &pinfo->dst,
357 pinfo->ptype, pinfo->oxid,
358 pinfo->rxid, NO_PORT2);
360 ckey.conv_idx = conversation->index;
362 cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash,
364 task_key.conv_id = conversation->index;
365 task_key.task_id = conversation->index;
366 pinfo->private_data = (void *)&task_key;
369 pinfo->private_data = NULL;
372 ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, 0,
374 fcp_tree = proto_item_add_subtree (ti, ett_fcp);
376 if (cdata->fcp_lun >= 0)
377 proto_tree_add_uint_hidden (fcp_tree, hf_fcp_singlelun, tvb,
378 0, 0, cdata->fcp_lun);
380 dissect_scsi_payload (tvb, pinfo, tree, 0, FALSE, cdata->fcp_dl);
383 dissect_scsi_payload (tvb, pinfo, tree, 0, FALSE, 0);
388 dissect_fcp_rsp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
398 proto_tree *fcp_tree;
400 conversation_t *conversation;
401 fcp_conv_data_t *cdata = NULL;
403 scsi_task_id_t task_key;
405 status = tvb_get_guint8 (tvb, offset+11);
407 if (check_col (pinfo->cinfo, COL_INFO)) {
408 col_append_fstr (pinfo->cinfo, COL_INFO, " , %s",
409 val_to_str (status, scsi_status_val, "0x%x"));
412 /* Response marks the end of the conversation. So destroy state */
413 conversation = find_conversation (&pinfo->src, &pinfo->dst,
414 pinfo->ptype, pinfo->oxid,
415 pinfo->rxid, NO_PORT2);
417 ckey.conv_idx = conversation->index;
419 cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash,
421 task_key.conv_id = task_key.task_id = conversation->index;
422 pinfo->private_data = (void *)&task_key;
426 /* Determine the length of the FCP part of the packet */
427 len = FCP_DEF_RSP_LEN;
429 flags = tvb_get_guint8 (tvb, offset+10);
431 add_len = tvb_get_ntohl (tvb, offset+20);
435 add_len = tvb_get_ntohl (tvb, offset+16);
439 ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, len,
441 fcp_tree = proto_item_add_subtree (ti, ett_fcp);
442 proto_tree_add_uint_hidden (fcp_tree, hf_fcp_type, tvb, offset, 0, 0);
445 del_usecs = (pinfo->fd->abs_secs - cdata->abs_secs)* 1000000 +
446 (pinfo->fd->abs_usecs - cdata->abs_usecs);
447 if (del_usecs > 1000)
448 proto_tree_add_text (fcp_tree, tvb, offset, 0,
449 "Cmd Response Time: %d msecs",
452 proto_tree_add_text (fcp_tree, tvb, offset, 0,
453 "Cmd Response Time: %d usecs",
455 if (cdata->fcp_lun >= 0)
456 proto_tree_add_uint_hidden (fcp_tree, hf_fcp_singlelun, tvb,
457 offset, 0, cdata->fcp_lun);
459 proto_tree_add_uint_format (fcp_tree, hf_fcp_rspflags, tvb, offset+10,
460 1, flags, "Flags: 0x%02x (%s)", flags,
461 rspflags_to_str (flags, str));
462 proto_tree_add_item (fcp_tree, hf_fcp_scsistatus, tvb, offset+11, 1, 0);
464 proto_tree_add_item (fcp_tree, hf_fcp_resid, tvb, offset+12, 4, 0);
466 proto_tree_add_item (fcp_tree, hf_fcp_snslen, tvb, offset+16, 4, 0);
468 rsplen = tvb_get_ntohl (tvb, offset+20);
469 proto_tree_add_item (fcp_tree, hf_fcp_rsplen, tvb, offset+20, 4, 0);
470 proto_tree_add_item (fcp_tree, hf_fcp_rspcode, tvb, offset+27, 1,
474 dissect_scsi_snsinfo (tvb, pinfo, tree, offset+24+rsplen,
475 tvb_get_ntohl (tvb, offset+16));
478 g_mem_chunk_free (fcp_req_vals, cdata);
479 g_hash_table_remove (fcp_req_hash, &ckey);
485 dissect_fcp_xfer_rdy (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
489 proto_tree *fcp_tree;
492 conversation_t *conversation;
493 fcp_conv_data_t *cdata = NULL;
494 fcp_conv_key_t ckey, *req_key;
496 /* Retrieve conversation state to determine expected payload */
497 conversation = find_conversation (&pinfo->src, &pinfo->dst,
498 pinfo->ptype, pinfo->oxid,
499 pinfo->rxid, NO_PORT2);
501 conversation = conversation_new (&pinfo->src, &pinfo->dst,
502 pinfo->ptype, pinfo->oxid,
503 pinfo->rxid, NO_PORT2);
507 ckey.conv_idx = conversation->index;
509 cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash,
512 cdata->fcp_dl = tvb_get_ntohl (tvb, offset+4);
515 req_key = g_mem_chunk_alloc (fcp_req_keys);
516 req_key->conv_idx = conversation->index;
518 cdata = g_mem_chunk_alloc (fcp_req_vals);
519 cdata->fcp_dl = tvb_get_ntohl (tvb, offset+4);
522 g_hash_table_insert (fcp_req_hash, req_key, cdata);
527 ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, 12,
529 fcp_tree = proto_item_add_subtree (ti, ett_fcp);
530 proto_tree_add_uint_hidden (fcp_tree, hf_fcp_type, tvb, offset, 0, 0);
533 del_usecs = (pinfo->fd->abs_secs - cdata->abs_secs)* 1000000 +
534 (pinfo->fd->abs_usecs - cdata->abs_usecs);
535 if (del_usecs > 1000)
536 proto_tree_add_text (fcp_tree, tvb, offset, 0,
537 "Cmd Response Time: %d msecs",
540 proto_tree_add_text (fcp_tree, tvb, offset, 0,
541 "Cmd Response Time: %d usecs",
543 if (cdata->fcp_lun >= 0)
544 proto_tree_add_uint_hidden (fcp_tree, hf_fcp_singlelun, tvb,
545 offset, 0, cdata->fcp_lun);
547 proto_tree_add_item (fcp_tree, hf_fcp_data_ro, tvb, offset, 4, 0);
548 proto_tree_add_item (fcp_tree, hf_fcp_burstlen, tvb, offset+4, 4, 0);
553 dissect_fcp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
556 /* Set up structures needed to add the protocol subtree and manage it */
559 /* Make entries in Protocol column and Info column on summary display */
560 if (check_col(pinfo->cinfo, COL_PROTOCOL))
561 col_set_str(pinfo->cinfo, COL_PROTOCOL, "FCP");
563 r_ctl = pinfo->r_ctl;
567 if (check_col (pinfo->cinfo, COL_INFO)) {
568 col_set_str (pinfo->cinfo, COL_INFO, val_to_str (r_ctl, fcp_iu_val,
574 dissect_fcp_data (tvb, pinfo, tree);
577 /* Nothing to be done here */
579 case FCP_IU_XFER_RDY:
580 dissect_fcp_xfer_rdy (tvb, pinfo, tree);
583 dissect_fcp_cmnd (tvb, pinfo, tree);
586 dissect_fcp_rsp (tvb, pinfo, tree);
589 call_dissector (data_handle, tvb, pinfo, tree);
594 /* Register the protocol with Ethereal */
596 /* this format is require because a script is used to build the C function
597 that calls all the protocol registration.
601 proto_register_fcp (void)
604 /* Setup list of header fields See Section 1.6.1 for details*/
605 static hf_register_info hf[] = {
607 {"Field to branch off to SCSI", "fcp.type", FT_UINT8, BASE_HEX, NULL,
610 {"Multi-Level LUN", "fcp.multilun", FT_BYTES, BASE_HEX, NULL, 0x0,
613 {"LUN", "fcp.lun", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
615 {"Command Ref Num", "fcp.crn", FT_UINT8, BASE_DEC, NULL, 0x0, "",
618 {"Task Attribute", "fcp.taskattr", FT_UINT8, BASE_HEX,
619 VALS (fcp_task_attr_val), 0x7, "", HFILL}},
621 {"Task Management Flags", "fcp.taskmgmt", FT_UINT8, BASE_HEX, NULL,
623 { &hf_fcp_addlcdblen,
624 {"Additional CDB Length", "fcp.addlcdblen", FT_UINT8, BASE_DEC, NULL,
627 {"RDDATA", "fcp.rddata", FT_BOOLEAN, 8, NULL, 0x02, "", HFILL}},
629 {"WRDATA", "fcp.wrdata", FT_BOOLEAN, 8, NULL, 0x01, "", HFILL}},
631 {"FCP_DL", "fcp.dl", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
633 {"FCP_DATA_RO", "fcp.data_ro", FT_UINT32, BASE_DEC, NULL, 0x0, "",
636 {"Burst Length", "fcp.burstlen", FT_UINT32, BASE_DEC, NULL, 0x0, "",
639 {"FCP_RSP Flags", "fcp.rspflags", FT_UINT8, BASE_HEX, NULL, 0x0, "",
642 {"FCP_RESID", "fcp.resid", FT_UINT32, BASE_DEC, NULL, 0x0, "",
645 {"FCP_SNS_LEN", "fcp.snslen", FT_UINT32, BASE_DEC, NULL, 0x0, "",
648 {"FCP_RSP_LEN", "fcp.rsplen", FT_UINT32, BASE_DEC, NULL, 0x0, "",
651 {"RSP_CODE", "fcp.rspcode", FT_UINT8, BASE_HEX,
652 VALS (fcp_rsp_code_val), 0x0, "", HFILL}},
653 { &hf_fcp_scsistatus,
654 {"SCSI Status", "fcp.status", FT_UINT8, BASE_HEX,
655 VALS (scsi_status_val), 0x0, "", HFILL}},
658 /* Setup protocol subtree array */
659 static gint *ett[] = {
663 /* Register the protocol name and description */
664 proto_fcp = proto_register_protocol("Fibre Channel Protocol for SCSI",
667 /* Required function calls to register the header fields and subtrees used */
668 proto_register_field_array(proto_fcp, hf, array_length(hf));
669 proto_register_subtree_array(ett, array_length(ett));
670 fcp_dissector = register_dissector_table ("fcp.type", "FCP Type", FT_UINT8,
672 register_init_routine (&fcp_init_protocol);
675 /* If this dissector uses sub-dissector registration add a registration routine.
676 This format is required because a script is used to find these routines and
677 create the code that calls these routines.
680 proto_reg_handoff_fcp (void)
682 dissector_handle_t fcp_handle;
684 fcp_handle = create_dissector_handle (dissect_fcp, proto_fcp);
685 dissector_add("fc.ftype", FC_FTYPE_SCSI, fcp_handle);
687 data_handle = find_dissector ("data");