2 * Routines for Fibre Channel Protocol for SCSI (FCP)
3 * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include <epan/packet.h>
15 #include <epan/conversation.h>
16 #include <epan/proto_data.h>
18 #include "packet-scsi.h"
19 #include "packet-fc.h"
20 #include "packet-fcels.h"
22 void proto_register_fcp(void);
23 void proto_reg_handoff_fcp(void);
25 static dissector_handle_t fcp_handle
;
27 typedef struct _fcp_proto_data_t
{
31 /* Initialize the protocol and registered fields */
33 static int hf_fcp_multilun
;
34 static int hf_fcp_singlelun
;
35 static int hf_fcp_crn
;
36 static int hf_fcp_taskattr
;
37 static int hf_fcp_taskmgmt
;
38 static int hf_fcp_addlcdblen
;
39 static int hf_fcp_rddata
;
40 static int hf_fcp_wrdata
;
42 static int hf_fcp_bidir_dl
;
43 static int hf_fcp_data_ro
;
44 static int hf_fcp_r_ctl
;
45 static int hf_fcp_burstlen
;
46 static int hf_fcp_rspflags
;
47 static int hf_fcp_retry_delay_timer
;
48 static int hf_fcp_resid
;
49 static int hf_fcp_bidir_resid
;
50 static int hf_fcp_snslen
;
51 static int hf_fcp_rsplen
;
52 static int hf_fcp_rspcode
;
53 static int hf_fcp_scsistatus
;
54 static int hf_fcp_mgmt_flags_obsolete
;
55 static int hf_fcp_mgmt_flags_clear_aca
;
56 static int hf_fcp_mgmt_flags_target_reset
;
57 static int hf_fcp_mgmt_flags_lu_reset
;
58 static int hf_fcp_mgmt_flags_rsvd
;
59 static int hf_fcp_mgmt_flags_clear_task_set
;
60 static int hf_fcp_mgmt_flags_abort_task_set
;
61 static int hf_fcp_rsp_flags_bidi
;
62 static int hf_fcp_rsp_flags_bidi_rru
;
63 static int hf_fcp_rsp_flags_bidi_rro
;
64 static int hf_fcp_rsp_flags_conf_req
;
65 static int hf_fcp_rsp_flags_resid_under
;
66 static int hf_fcp_rsp_flags_resid_over
;
67 static int hf_fcp_rsp_flags_sns_vld
;
68 static int hf_fcp_rsp_flags_res_vld
;
69 static int hf_fcp_request_in
;
70 static int hf_fcp_response_in
;
71 static int hf_fcp_time
;
72 static int hf_fcp_els_op
;
73 static int hf_fcp_srr_ox_id
;
74 static int hf_fcp_srr_rx_id
;
76 /* Initialize the subtree pointers */
78 static int ett_fcp_taskmgmt
;
79 static int ett_fcp_rsp_flags
;
81 typedef struct _fcp_conv_data_t
{
85 typedef struct fcp_request_data
{
86 uint32_t request_frame
;
87 uint32_t response_frame
;
88 nstime_t request_time
;
92 #define FCP_DEF_CMND_LEN 32 /* by default cmnd is 32 bytes */
93 #define FCP_DEF_RSP_LEN 24 /* default FCP_RSP len */
95 /* Information Categories based on lower 4 bits of R_CTL */
96 #define FCP_IU_DATA 0x1
97 #define FCP_IU_UNSOL_CTL 0x2
98 #define FCP_IU_SOL_CTL 0x3
99 #define FCP_IU_CONFIRM 0x3
100 #define FCP_IU_XFER_RDY 0x5
101 #define FCP_IU_CMD 0x6
102 #define FCP_IU_RSP 0x7
104 static const value_string fcp_iu_val
[] = {
105 {FCP_IU_DATA
, "FCP_DATA"},
106 {FCP_IU_UNSOL_CTL
, "Control"},
107 {FCP_IU_CONFIRM
, "Confirm"},
108 {FCP_IU_XFER_RDY
, "XFER_RDY"},
109 {FCP_IU_CMD
, "FCP_CMND"},
110 {FCP_IU_RSP
, "FCP_RSP"},
115 /* Task Attribute Values */
116 static const value_string fcp_task_attr_val
[] = {
118 {1, "Head of Queue"},
125 /* RSP Code Definitions (from FCP_RSP_INFO) */
126 static const value_string fcp_rsp_code_val
[] = {
127 {0, "Task Management Function Complete"},
128 {1, "FCP_DATA length Different from FCP_BURST_LEN"},
129 {2, "FCP_CMND Fields Invalid"},
130 {3, "FCP_DATA Parameter Mismatch With FCP_DATA_RO"},
131 {4, "Task Management Function Rejected"},
132 {5, "Task Management Function Failed"},
133 {9, "Task Management Function Incorrect LUN"},
139 static const true_false_string fcp_mgmt_flags_obsolete_tfs
= {
140 "OBSOLETE BIT is SET",
141 "OBSOLETE BIT is NOT set",
143 static const true_false_string fcp_mgmt_flags_clear_aca_tfs
= {
145 "Clear aca is NOT set",
147 static const true_false_string fcp_mgmt_flags_target_reset_tfs
= {
148 "TARGET RESET is SET",
149 "Target reset is NOT set",
151 static const true_false_string fcp_mgmt_flags_lu_reset_tfs
= {
153 "Lu reset is NOT set",
155 static const true_false_string fcp_mgmt_flags_rsvd_tfs
= {
159 static const true_false_string fcp_mgmt_flags_clear_task_set_tfs
= {
160 "CLEAR TASK SET is SET",
161 "Clear task set is NOT set",
163 static const true_false_string fcp_mgmt_flags_abort_task_set_tfs
= {
164 "ABORT TASK SET is SET",
165 "Abort task set is NOT set",
169 dissect_task_mgmt_flags(packet_info
*pinfo
, proto_tree
*parent_tree
, tvbuff_t
*tvb
, int offset
)
172 static int * const mgmt_flags
[] = {
173 &hf_fcp_mgmt_flags_obsolete
,
174 &hf_fcp_mgmt_flags_clear_aca
,
175 &hf_fcp_mgmt_flags_target_reset
,
176 &hf_fcp_mgmt_flags_lu_reset
,
177 &hf_fcp_mgmt_flags_rsvd
,
178 &hf_fcp_mgmt_flags_clear_task_set
,
179 &hf_fcp_mgmt_flags_abort_task_set
,
185 flags
= tvb_get_uint8(tvb
, offset
);
186 item
= proto_tree_add_bitmask_with_flags(parent_tree
, tvb
, offset
, hf_fcp_taskmgmt
,
187 ett_fcp_taskmgmt
, mgmt_flags
, ENC_NA
, BMT_NO_FALSE
|BMT_NO_TFS
);
190 proto_item_append_text(item
, " (No values set)");
193 col_prepend_fence_fstr(pinfo
->cinfo
, COL_INFO
, "[FCP OBSOLETE] ");
197 col_prepend_fence_fstr(pinfo
->cinfo
, COL_INFO
, "[FCP CLEAR_ACA] ");
201 col_prepend_fence_fstr(pinfo
->cinfo
, COL_INFO
, "[FCP TARGET_RESET] ");
205 col_prepend_fence_fstr(pinfo
->cinfo
, COL_INFO
, "[FCP LU_RESET] ");
209 col_prepend_fence_fstr(pinfo
->cinfo
, COL_INFO
, "[FCP RSVD] ");
213 col_prepend_fence_fstr(pinfo
->cinfo
, COL_INFO
, "[FCP CLEAR_TASK_SET] ");
217 col_prepend_fence_fstr(pinfo
->cinfo
, COL_INFO
, "[FCP ABORT_TASK_SET] ");
221 static const true_false_string fcp_rsp_flags_bidi_tfs
= {
222 "Bidirectional residual fields are PRESENT",
223 "Bidirectional residual fields are NOT present",
225 static const true_false_string fcp_rsp_flags_bidi_rru_tfs
= {
226 "Bidirectional residual underflow is PRESENT",
227 "Bidirectional residual underflow is NOT present",
229 static const true_false_string fcp_rsp_flags_bidi_rro_tfs
= {
230 "Bidirectional residual overflow is PRESENT",
231 "Bidirectional residual overflow is NOT present",
233 static const true_false_string fcp_rsp_flags_conf_req_tfs
= {
235 "Conf req set is NOT set",
237 static const true_false_string fcp_rsp_flags_resid_under_tfs
= {
238 "RESID UNDER is SET",
239 "Resid under is NOT set",
241 static const true_false_string fcp_rsp_flags_resid_over_tfs
= {
243 "Resid over is NOT set",
245 static const true_false_string fcp_rsp_flags_sns_vld_tfs
= {
247 "Sns vld is NOT set",
249 static const true_false_string fcp_rsp_flags_res_vld_tfs
= {
251 "Res vld is NOT set",
255 dissect_rsp_flags(proto_tree
*parent_tree
, tvbuff_t
*tvb
, int offset
)
259 static int * const resid_present_flags
[] = {
260 &hf_fcp_rsp_flags_bidi
,
261 &hf_fcp_rsp_flags_bidi_rru
,
262 &hf_fcp_rsp_flags_bidi_rro
,
263 &hf_fcp_rsp_flags_conf_req
,
264 &hf_fcp_rsp_flags_resid_under
,
265 &hf_fcp_rsp_flags_resid_over
,
266 &hf_fcp_rsp_flags_sns_vld
,
267 &hf_fcp_rsp_flags_res_vld
,
271 static int * const no_resid_flags
[] = {
272 &hf_fcp_rsp_flags_bidi
,
273 &hf_fcp_rsp_flags_conf_req
,
274 &hf_fcp_rsp_flags_resid_under
,
275 &hf_fcp_rsp_flags_resid_over
,
276 &hf_fcp_rsp_flags_sns_vld
,
277 &hf_fcp_rsp_flags_res_vld
,
281 flags
= tvb_get_uint8(tvb
, offset
);
283 item
= proto_tree_add_bitmask_with_flags(parent_tree
, tvb
, offset
, hf_fcp_rspflags
,
284 ett_fcp_rsp_flags
, resid_present_flags
, ENC_NA
, BMT_NO_FALSE
|BMT_NO_TFS
);
286 item
= proto_tree_add_bitmask_with_flags(parent_tree
, tvb
, offset
, hf_fcp_rspflags
,
287 ett_fcp_rsp_flags
, no_resid_flags
, ENC_NA
, BMT_NO_FALSE
|BMT_NO_TFS
);
291 proto_item_append_text(item
, " (No values set)");
295 dissect_fcp_cmnd(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, proto_tree
*tree
, conversation_t
*conversation
, fc_hdr
*fchdr
, fcp_conv_data_t
*fcp_conv_data
)
299 uint8_t flags
, rwflags
, lun0
;
300 uint16_t lun
= 0xffff;
303 fcp_request_data_t
*request_data
= NULL
;
305 fcp_proto_data_t
*proto_data
;
307 /* Determine the length of the FCP part of the packet */
308 flags
= tvb_get_uint8(tvb
, offset
+10);
310 add_len
= tvb_get_uint8(tvb
, offset
+11) & 0x7C;
311 add_len
= add_len
>> 2;
314 lun0
= tvb_get_uint8(tvb
, offset
);
316 /* Display single-level LUNs in decimal for clarity */
317 /* I'm taking a shortcut here by assuming that if the first byte of the
318 * LUN field is 0, it is a single-level LUN. This is not true. For a
319 * real single-level LUN, all 8 bytes except byte 1 must be 0.
322 proto_tree_add_item(tree
, hf_fcp_multilun
, tvb
, offset
, 8, ENC_NA
);
323 lun
= tvb_get_uint8(tvb
, offset
) & 0x3f;
325 lun
|= tvb_get_uint8(tvb
, offset
+1);
327 proto_tree_add_item(tree
, hf_fcp_singlelun
, tvb
, offset
+1,
329 lun
= tvb_get_uint8(tvb
, offset
+1);
332 if (!pinfo
->fd
->visited
) {
333 proto_data
= wmem_new(wmem_file_scope(), fcp_proto_data_t
);
334 proto_data
->lun
= lun
;
335 p_add_proto_data(wmem_file_scope(), pinfo
, proto_fcp
, 0, proto_data
);
338 request_data
= (fcp_request_data_t
*)wmem_map_lookup(fcp_conv_data
->luns
, GUINT_TO_POINTER((unsigned)lun
));
340 request_data
= wmem_new(wmem_file_scope(), fcp_request_data_t
);
341 request_data
->request_frame
= pinfo
->num
;
342 request_data
->response_frame
= 0;
343 request_data
->request_time
= pinfo
->abs_ts
;
345 request_data
->itlq
= wmem_new(wmem_file_scope(), itlq_nexus_t
);
346 request_data
->itlq
->first_exchange_frame
=0;
347 request_data
->itlq
->last_exchange_frame
=0;
348 request_data
->itlq
->lun
=lun
;
349 request_data
->itlq
->scsi_opcode
=0xffff;
350 request_data
->itlq
->task_flags
=0;
351 request_data
->itlq
->data_length
=0;
352 request_data
->itlq
->bidir_data_length
=0;
353 request_data
->itlq
->fc_time
=pinfo
->abs_ts
;
354 request_data
->itlq
->flags
=0;
355 request_data
->itlq
->alloc_len
=0;
356 request_data
->itlq
->extra_data
=NULL
;
358 wmem_map_insert(fcp_conv_data
->luns
, GUINT_TO_POINTER((unsigned)lun
), request_data
);
361 /* populate the exchange struct */
362 if(!pinfo
->fd
->visited
){
363 if(fchdr
->fctl
&FC_FCTL_EXCHANGE_FIRST
){
364 request_data
->itlq
->first_exchange_frame
=pinfo
->num
;
365 request_data
->itlq
->fc_time
= pinfo
->abs_ts
;
367 if(fchdr
->fctl
&FC_FCTL_EXCHANGE_LAST
){
368 request_data
->itlq
->last_exchange_frame
=pinfo
->num
;
372 if (request_data
->itlq
)
373 request_data
->itlq
->lun
= lun
;
377 proto_tree_add_item(tree
, hf_fcp_crn
, tvb
, offset
+8, 1, ENC_BIG_ENDIAN
);
378 proto_tree_add_item(tree
, hf_fcp_taskattr
, tvb
, offset
+9, 1, ENC_BIG_ENDIAN
);
379 dissect_task_mgmt_flags(pinfo
, tree
, tvb
, offset
+10);
380 proto_tree_add_item(tree
, hf_fcp_addlcdblen
, tvb
, offset
+11, 1, ENC_BIG_ENDIAN
);
381 rwflags
= tvb_get_uint8(tvb
, offset
+11);
382 if (request_data
->itlq
) {
383 if (rwflags
& 0x02) {
384 request_data
->itlq
->task_flags
|= SCSI_DATA_READ
;
386 if (rwflags
& 0x01) {
387 request_data
->itlq
->task_flags
|= SCSI_DATA_WRITE
;
390 proto_tree_add_item(tree
, hf_fcp_rddata
, tvb
, offset
+11, 1, ENC_BIG_ENDIAN
);
391 proto_tree_add_item(tree
, hf_fcp_wrdata
, tvb
, offset
+11, 1, ENC_BIG_ENDIAN
);
393 tvb_len
= tvb_captured_length_remaining(tvb
, offset
+12);
394 if (tvb_len
> (16 + add_len
))
395 tvb_len
= 16 + add_len
;
398 itl
.conversation
= conversation
;
400 cdb_tvb
= tvb_new_subset_length(tvb
, offset
+12, tvb_len
);
401 dissect_scsi_cdb(cdb_tvb
, pinfo
, parent_tree
, SCSI_DEV_UNKNOWN
, request_data
->itlq
, &itl
);
403 proto_tree_add_item(tree
, hf_fcp_dl
, tvb
, offset
+12+16+add_len
,
405 if (request_data
->itlq
) {
406 request_data
->itlq
->data_length
= tvb_get_ntohl(tvb
, offset
+12+16+add_len
);
409 if ( ((rwflags
& 0x03) == 0x03)
410 && tvb_reported_length_remaining(tvb
, offset
+12+16+add_len
+4) >= 4) {
411 proto_tree_add_item(tree
, hf_fcp_bidir_dl
, tvb
, offset
+12+16+add_len
+4,
413 if (request_data
->itlq
) {
414 request_data
->itlq
->bidir_data_length
= tvb_get_ntohl(tvb
, offset
+12+16+add_len
+4);
422 dissect_fcp_data(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, conversation_t
*conversation
, itlq_nexus_t
*itlq
, uint32_t relative_offset
)
425 itlq_nexus_t empty_itlq
;
428 itl
.conversation
= conversation
;
432 /* Provide "default" itlq */
433 memset(&empty_itlq
, 0, sizeof(empty_itlq
));
434 empty_itlq
.lun
=0xffff;
435 empty_itlq
.scsi_opcode
=0xffff;
439 dissect_scsi_payload(tvb
, pinfo
, parent_tree
, false, itlq
, &itl
, relative_offset
);
442 /* fcp-3 9.5 table 24 */
444 dissect_fcp_rspinfo(tvbuff_t
*tvb
, proto_tree
*tree
, int offset
)
446 /* 3 reserved bytes */
450 proto_tree_add_item(tree
, hf_fcp_rspcode
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
453 /* 4 reserved bytes */
460 dissect_fcp_rsp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, proto_tree
*tree
, conversation_t
*conversation
, fc_hdr
*fchdr
, fcp_request_data_t
*request_data
)
468 itlq_nexus_t empty_itlq
;
470 status
= tvb_get_uint8(tvb
, offset
+11);
472 col_append_fstr(pinfo
->cinfo
, COL_INFO
, ":%s",
473 val_to_str(status
, scsi_status_val
, "0x%x"));
475 /* Save the response frame */
476 if (request_data
!= NULL
) {
477 request_data
->response_frame
= pinfo
->num
;
479 /* populate the exchange struct */
480 if(!pinfo
->fd
->visited
){
481 if(fchdr
->fctl
&FC_FCTL_EXCHANGE_FIRST
){
482 request_data
->itlq
->first_exchange_frame
=pinfo
->num
;
483 request_data
->itlq
->fc_time
= pinfo
->abs_ts
;
485 if(fchdr
->fctl
&FC_FCTL_EXCHANGE_LAST
){
486 request_data
->itlq
->last_exchange_frame
=pinfo
->num
;
490 /* Provide "default" itlq */
491 memset(&empty_itlq
, 0, sizeof(empty_itlq
));
492 empty_itlq
.lun
=0xffff;
493 empty_itlq
.scsi_opcode
=0xffff;
496 /* 8 reserved bytes */
499 /* retry delay timer */
500 proto_tree_add_item(tree
, hf_fcp_retry_delay_timer
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
504 flags
= tvb_get_uint8(tvb
, offset
);
505 dissect_rsp_flags(tree
, tvb
, offset
);
509 itl
.conversation
= conversation
;
511 /* scsi status code */
512 proto_tree_add_item(tree
, hf_fcp_scsistatus
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
513 dissect_scsi_rsp(tvb
, pinfo
, parent_tree
, (request_data
!= NULL
) ? request_data
->itlq
: &empty_itlq
, &itl
, tvb_get_uint8(tvb
, offset
));
518 proto_tree_add_item(tree
, hf_fcp_resid
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
524 snslen
= tvb_get_ntohl(tvb
, offset
);
525 proto_tree_add_uint(tree
, hf_fcp_snslen
, tvb
, offset
, 4,
530 /* response length */
532 rsplen
= tvb_get_ntohl(tvb
, offset
);
533 proto_tree_add_uint(tree
, hf_fcp_rsplen
, tvb
, offset
, 4,
540 tvbuff_t
*rspinfo_tvb
;
542 rspinfo_tvb
= tvb_new_subset_length_caplen(tvb
, offset
, MIN(rsplen
, tvb_captured_length_remaining(tvb
, offset
)), rsplen
);
543 dissect_fcp_rspinfo(rspinfo_tvb
, tree
, 0);
552 sns_tvb
= tvb_new_subset_length_caplen(tvb
, offset
, MIN(snslen
, tvb_captured_length_remaining(tvb
, offset
)), snslen
);
553 dissect_scsi_snsinfo(sns_tvb
, pinfo
, parent_tree
, 0,
555 (request_data
!= NULL
) ? request_data
->itlq
: &empty_itlq
, &itl
);
560 /* bidir read resid (only present for bidirectional responses) */
563 proto_tree_add_item(tree
, hf_fcp_bidir_resid
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
570 dissect_fcp_xfer_rdy(tvbuff_t
*tvb
, proto_tree
*tree
)
574 proto_tree_add_item(tree
, hf_fcp_data_ro
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
575 proto_tree_add_item(tree
, hf_fcp_burstlen
, tvb
, offset
+4, 4, ENC_BIG_ENDIAN
);
579 dissect_fcp_srr(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
, fc_hdr
*fchdr
)
583 r_ctl
= fchdr
->r_ctl
& 0xf;
584 if (r_ctl
== FCP_IU_UNSOL_CTL
) { /* request */
585 proto_tree_add_item(tree
, hf_fcp_srr_ox_id
, tvb
, 4, 2, ENC_BIG_ENDIAN
);
586 proto_tree_add_item(tree
, hf_fcp_srr_rx_id
, tvb
, 6, 2, ENC_BIG_ENDIAN
);
587 proto_tree_add_item(tree
, hf_fcp_data_ro
, tvb
, 8, 4, ENC_BIG_ENDIAN
);
588 proto_tree_add_item(tree
, hf_fcp_r_ctl
, tvb
, 12, 1, ENC_NA
);
592 static const value_string fcp_els_iu_val
[] = {
593 {FCP_IU_UNSOL_CTL
, "FCP ELS Request"},
594 {FCP_IU_SOL_CTL
, "FCP ELS Response"},
599 * Dissect FC-4 ELS for FCP.
602 dissect_fcp_els(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, fc_hdr
*fchdr
)
606 op
= tvb_get_uint8(tvb
, 0);
607 col_add_str(pinfo
->cinfo
, COL_INFO
, val_to_str_ext(op
, &fc_els_proto_val_ext
, "0x%x"));
608 proto_tree_add_item(tree
, hf_fcp_els_op
, tvb
, 0, 1, ENC_NA
);
610 switch (op
) { /* XXX should switch based on conv for LS_ACC */
612 dissect_fcp_srr(tvb
, pinfo
, tree
, fchdr
);
615 call_data_dissector(tvb
, pinfo
, tree
);
621 dissect_fcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
623 proto_item
*ti
= NULL
;
624 proto_tree
*fcp_tree
= NULL
;
627 conversation_t
*fc_conv
;
628 fcp_conv_data_t
*fcp_conv_data
= NULL
;
629 fcp_request_data_t
*request_data
= NULL
;
631 fcp_proto_data_t
*proto_data
;
633 /* Reject the packet if data is NULL */
636 fchdr
= (fc_hdr
*)data
;
638 /* Make entries in Protocol column and Info column on summary display */
639 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "FCP");
641 r_ctl
= fchdr
->r_ctl
;
642 els
= (r_ctl
& 0xf0) == FC_RCTL_LINK_DATA
;
645 col_add_str(pinfo
->cinfo
, COL_INFO
,
646 val_to_str(r_ctl
, els
? fcp_els_iu_val
: fcp_iu_val
,
649 ti
= proto_tree_add_protocol_format(tree
, proto_fcp
, tvb
, 0, -1,
652 els
? fcp_els_iu_val
:
653 fcp_iu_val
, "Unknown 0x%02x"));
654 fcp_tree
= proto_item_add_subtree(ti
, ett_fcp
);
656 fc_conv
= find_or_create_conversation(pinfo
);
657 fcp_conv_data
= (fcp_conv_data_t
*)conversation_get_proto_data(fc_conv
, proto_fcp
);
659 if (!fcp_conv_data
) {
660 fcp_conv_data
= wmem_new(wmem_file_scope(), fcp_conv_data_t
);
661 fcp_conv_data
->luns
= wmem_map_new(wmem_file_scope(), g_direct_hash
, g_direct_equal
);
662 conversation_add_proto_data(fc_conv
, proto_fcp
, fcp_conv_data
);
665 /* Lun is only populated by FCP_IU_CMD, and subsequent packets assume the same lun.
666 The only way that consistently works is to save the lun on the first pass when packets
667 are guaranteed to be parsed consecutively */
668 if (!pinfo
->fd
->visited
) {
669 proto_data
= wmem_new(wmem_file_scope(), fcp_proto_data_t
);
670 proto_data
->lun
= fchdr
->lun
;
671 p_add_proto_data(wmem_file_scope(), pinfo
, proto_fcp
, 0, proto_data
);
673 proto_data
= (fcp_proto_data_t
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_fcp
, 0);
676 if ((r_ctl
!= FCP_IU_CMD
) && (r_ctl
!= FCP_IU_UNSOL_CTL
) && (proto_data
!= NULL
)) {
677 request_data
= (fcp_request_data_t
*)wmem_map_lookup(fcp_conv_data
->luns
, GUINT_TO_POINTER((unsigned)(proto_data
->lun
)));
680 /* put a request_in in all frames except the command frame */
681 if ((r_ctl
!= FCP_IU_CMD
) && (r_ctl
!= FCP_IU_UNSOL_CTL
) &&
682 (request_data
!= NULL
) && (request_data
->itlq
->first_exchange_frame
)) {
684 it
= proto_tree_add_uint(fcp_tree
, hf_fcp_singlelun
, tvb
, 0, 0, proto_data
->lun
);
685 proto_item_set_generated(it
);
686 if (request_data
!= NULL
) {
687 it
= proto_tree_add_uint(fcp_tree
, hf_fcp_request_in
, tvb
, 0, 0, request_data
->request_frame
);
688 proto_item_set_generated(it
);
689 /* only put the response time in the actual response frame */
690 if (r_ctl
== FCP_IU_RSP
) {
692 nstime_delta(&delta_ts
, &pinfo
->abs_ts
, &request_data
->request_time
);
693 it
= proto_tree_add_time(ti
, hf_fcp_time
, tvb
, 0, 0, &delta_ts
);
694 proto_item_set_generated(it
);
698 /* put a response_in in all frames except the response frame */
699 if ((r_ctl
!= FCP_IU_RSP
) && (r_ctl
!= FCP_IU_SOL_CTL
) &&
700 (request_data
!= NULL
) && (request_data
->response_frame
)) {
702 it
= proto_tree_add_uint(fcp_tree
, hf_fcp_response_in
, tvb
, 0, 0, request_data
->response_frame
);
703 proto_item_set_generated(it
);
707 dissect_fcp_els(tvb
, pinfo
, fcp_tree
, fchdr
);
708 return tvb_captured_length(tvb
);
713 dissect_fcp_data(tvb
, pinfo
, tree
, fc_conv
, (request_data
!= NULL
) ? request_data
->itlq
: NULL
, fchdr
->relative_offset
);
716 /* Nothing to be done here */
718 case FCP_IU_XFER_RDY
:
719 dissect_fcp_xfer_rdy(tvb
, fcp_tree
);
722 dissect_fcp_cmnd(tvb
, pinfo
, tree
, fcp_tree
, fc_conv
, fchdr
, fcp_conv_data
);
725 dissect_fcp_rsp(tvb
, pinfo
, tree
, fcp_tree
, fc_conv
, fchdr
, request_data
);
728 call_data_dissector(tvb
, pinfo
, tree
);
731 /*xxx once the subdissectors return bytes consumed: proto_item_set_end(ti, tvb, offset);*/
732 return tvb_captured_length(tvb
);
735 /* Register the protocol with Wireshark */
738 proto_register_fcp(void)
741 /* Setup list of header fields See Section 1.6.1 for details*/
742 static hf_register_info hf
[] = {
744 {"Multi-Level LUN", "fcp.multilun",
745 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
750 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
754 {"Command Ref Num", "fcp.crn",
755 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
759 {"Task Attribute", "fcp.taskattr",
760 FT_UINT8
, BASE_HEX
, VALS(fcp_task_attr_val
), 0x7,
764 {"Task Management Flags", "fcp.taskmgmt",
765 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
768 { &hf_fcp_addlcdblen
,
769 {"Additional CDB Length", "fcp.addlcdblen",
770 FT_UINT8
, BASE_DEC
, NULL
, 0xFC,
774 {"RDDATA", "fcp.rddata",
775 FT_BOOLEAN
, 8, NULL
, 0x02,
779 {"WRDATA", "fcp.wrdata",
780 FT_BOOLEAN
, 8, NULL
, 0x01,
785 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
789 {"FCP_BIDIRECTIONAL_READ_DL", "fcp.bidir_dl",
790 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
794 {"FCP_DATA_RO", "fcp.data_ro",
795 FT_UINT32
, BASE_DEC
, VALS(fcp_iu_val
), 0x0,
799 {"R_CTL", "fcp.r_ctl",
800 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
804 {"Burst Length", "fcp.burstlen",
805 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
808 { &hf_fcp_retry_delay_timer
,
809 {"Retry Delay Timer", "fcp.rsp.retry_delay_timer",
810 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
814 {"FCP_RSP Flags", "fcp.rspflags",
815 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
819 {"FCP_RESID", "fcp.resid",
820 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
823 { &hf_fcp_bidir_resid
,
824 {"Bidirectional Read Resid", "fcp.bidir_resid",
825 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
829 {"FCP_SNS_LEN", "fcp.snslen",
830 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
834 {"FCP_RSP_LEN", "fcp.rsplen",
835 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
839 {"RSP_CODE", "fcp.rspcode",
840 FT_UINT8
, BASE_HEX
, VALS(fcp_rsp_code_val
), 0x0,
843 { &hf_fcp_scsistatus
,
844 {"SCSI Status", "fcp.status",
845 FT_UINT8
, BASE_HEX
, VALS(scsi_status_val
), 0x0,
848 { &hf_fcp_mgmt_flags_obsolete
,
849 { "Obsolete", "fcp.mgmt.flags.obsolete",
850 FT_BOOLEAN
, 8, TFS(&fcp_mgmt_flags_obsolete_tfs
), 0x80,
853 { &hf_fcp_mgmt_flags_clear_aca
,
854 { "Clear ACA", "fcp.mgmt.flags.clear_aca",
855 FT_BOOLEAN
, 8, TFS(&fcp_mgmt_flags_clear_aca_tfs
), 0x40,
858 { &hf_fcp_mgmt_flags_target_reset
,
859 { "Target Reset", "fcp.mgmt.flags.target_reset",
860 FT_BOOLEAN
, 8, TFS(&fcp_mgmt_flags_target_reset_tfs
), 0x20,
863 { &hf_fcp_mgmt_flags_lu_reset
,
864 { "LU Reset", "fcp.mgmt.flags.lu_reset",
865 FT_BOOLEAN
, 8, TFS(&fcp_mgmt_flags_lu_reset_tfs
), 0x10,
868 { &hf_fcp_mgmt_flags_rsvd
,
869 { "Rsvd", "fcp.mgmt.flags.rsvd",
870 FT_BOOLEAN
, 8, TFS(&fcp_mgmt_flags_rsvd_tfs
), 0x08,
873 { &hf_fcp_mgmt_flags_clear_task_set
,
874 { "Clear Task Set", "fcp.mgmt.flags.clear_task_set",
875 FT_BOOLEAN
, 8, TFS(&fcp_mgmt_flags_clear_task_set_tfs
), 0x04,
878 { &hf_fcp_mgmt_flags_abort_task_set
,
879 { "Abort Task Set", "fcp.mgmt.flags.abort_task_set",
880 FT_BOOLEAN
, 8, TFS(&fcp_mgmt_flags_abort_task_set_tfs
), 0x02,
883 { &hf_fcp_rsp_flags_bidi
,
884 { "Bidi Rsp", "fcp.rsp.flags.bidi",
885 FT_BOOLEAN
, 8, TFS(&fcp_rsp_flags_bidi_tfs
), 0x80,
888 { &hf_fcp_rsp_flags_bidi_rru
,
889 { "Bidi Read Resid Under", "fcp.rsp.flags.bidi_rru",
890 FT_BOOLEAN
, 8, TFS(&fcp_rsp_flags_bidi_rru_tfs
), 0x40,
893 { &hf_fcp_rsp_flags_bidi_rro
,
894 { "Bidi Read Resid Over", "fcp.rsp.flags.bidi_rro",
895 FT_BOOLEAN
, 8, TFS(&fcp_rsp_flags_bidi_rro_tfs
), 0x20,
898 { &hf_fcp_rsp_flags_conf_req
,
899 { "Conf Req", "fcp.rsp.flags.conf_req",
900 FT_BOOLEAN
, 8, TFS(&fcp_rsp_flags_conf_req_tfs
), 0x10,
903 { &hf_fcp_rsp_flags_resid_under
,
904 { "Resid Under", "fcp.rsp.flags.resid_under",
905 FT_BOOLEAN
, 8, TFS(&fcp_rsp_flags_resid_under_tfs
), 0x08,
908 { &hf_fcp_rsp_flags_resid_over
,
909 { "Resid Over", "fcp.rsp.flags.resid_over",
910 FT_BOOLEAN
, 8, TFS(&fcp_rsp_flags_resid_over_tfs
), 0x04,
913 { &hf_fcp_rsp_flags_sns_vld
,
914 { "SNS Vld", "fcp.rsp.flags.sns_vld",
915 FT_BOOLEAN
, 8, TFS(&fcp_rsp_flags_sns_vld_tfs
), 0x02,
918 { &hf_fcp_rsp_flags_res_vld
,
919 { "RES Vld", "fcp.rsp.flags.res_vld",
920 FT_BOOLEAN
, 8, TFS(&fcp_rsp_flags_res_vld_tfs
), 0x01,
923 { &hf_fcp_request_in
,
924 { "Request In", "fcp.request_in",
925 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
), 0,
926 "The frame number for the request", HFILL
}},
928 { &hf_fcp_response_in
,
929 { "Response In", "fcp.response_in",
930 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE
), 0,
931 "The frame number of the response", HFILL
}},
934 { "Time from FCP_CMND", "fcp.time",
935 FT_RELATIVE_TIME
, BASE_NONE
, NULL
, 0,
936 "Time since the FCP_CMND frame", HFILL
}},
939 {"Opcode", "fcp.els.op",
940 FT_UINT8
, BASE_HEX
|BASE_EXT_STRING
, &fc_els_proto_val_ext
, 0x0,
944 {"OX_ID", "fcp.els.srr.ox_id",
945 FT_UINT16
, BASE_HEX
, NULL
, 0x0,
949 {"RX_ID", "fcp.els.srr.rx_id",
950 FT_UINT16
, BASE_HEX
, NULL
, 0x0,
954 /* Setup protocol subtree array */
955 static int *ett
[] = {
961 /* Register the protocol name and description */
962 proto_fcp
= proto_register_protocol("Fibre Channel Protocol for SCSI",
965 /* Required function calls to register the header fields and subtrees used */
966 proto_register_field_array(proto_fcp
, hf
, array_length(hf
));
967 proto_register_subtree_array(ett
, array_length(ett
));
969 fcp_handle
= register_dissector("fcp", dissect_fcp
, proto_fcp
);
973 proto_reg_handoff_fcp(void)
975 dissector_add_uint("fc.ftype", FC_FTYPE_SCSI
, fcp_handle
);
979 * Editor modelines - https://www.wireshark.org/tools/modelines.html
984 * indent-tabs-mode: nil
987 * vi: set shiftwidth=4 tabstop=8 expandtab:
988 * :indentSize=4:tabSize=8:noTabs=true: